offshore-sql
Version:
Sql Databases adapter for offshore
393 lines (348 loc) • 14.3 kB
JavaScript
var asynk = require('asynk');
var _ = require('lodash');
var crypto = require('crypto');
var Dialect = require('../../dialect.js');
var OracleCriteriaProcessor = require('./oracleCriteriaProcessor');
var OracleDialect = module.exports = function () {
};
OracleDialect.prototype = new Dialect();
OracleDialect.prototype.CriteriaProcessor = OracleCriteriaProcessor;
OracleDialect.prototype.describe = function(connection, collection, callback) {
var tableName = this.normalizeTableName(collection.tableName);
var queries = [];
queries[0] = connection.client.select('COLUMN_NAME', 'DATA_TYPE', 'NULLABLE').from('USER_TAB_COLUMNS').where('TABLE_NAME', tableName);
queries[1] = connection.client.select('INDEX_NAME', 'COLUMN_NAME').from('USER_IND_COLUMNS').where('TABLE_NAME', tableName);
queries[2] = connection.client.select('cols.TABLE_NAME', 'cols.COLUMN_NAME', 'cols.POSITION', 'cons.STATUS', 'cons.OWNER')
.from('ALL_CONSTRAINTS AS cons').leftJoin('ALL_CONS_COLUMNS AS cols', 'cols.CONSTRAINT_NAME', 'cons.CONSTRAINT_NAME')
.where({'cols.TABLE_NAME': tableName, 'cons.CONSTRAINT_TYPE': 'P'});
connection.client.count().from('USER_SEQUENCES').where('SEQUENCE_NAME', tableName.toLowerCase() + '_seq').then(function(count) {
if (count[0]['COUNT(*)']) {
queries[3] = connection.client.raw('SELECT "' + tableName.toLowerCase() + '_seq".nextval FROM DUAL');
}
asynk.each(queries, function(query, nextQuery) {
query.asCallback(nextQuery);
}).serie().done(function(results) {
var schema = results[0];
var indexes = results[1];
var tablePrimaryKeys = results[2];
if (schema.length === 0) {
return callback({code: 'ER_NO_SUCH_TABLE', message: 'Table ' + tableName + ' doesn\'t exist.'}, null);
}
// Loop through Schema and attach extra attributes
schema.forEach(function(attribute) {
tablePrimaryKeys.forEach(function(pk) {
// Set Primary Key Attribute
if (attribute.COLUMN_NAME === pk.COLUMN_NAME) {
attribute.primaryKey = true;
// If also a number set auto increment attribute
if (attribute.DATA_TYPE === 'NUMBER') {
attribute.autoIncrement = true;
}
}
});
// Set Unique Attribute
if (attribute.NULLABLE === 'N') {
attribute.required = true;
}
});
// Loop Through Indexes and Add Properties
indexes.forEach(function(index) {
schema.forEach(function(attribute) {
if (attribute.COLUMN_NAME === index.COLUMN_NAME)
{
attribute.indexed = true;
}
});
});
//console.log("describe schema: ",schema);
callback(null, schema);
}).fail(function(err) {
return callback(err, null);
});
});
};
OracleDialect.prototype.normalizeSchema = function (schema, definition) {
var normalized = _.reduce(_.clone(schema), function (memo, field) {
var attrName = field.COLUMN_NAME;
var type = field.DATA_TYPE;
// Remove (n) column-size indicators
type = type.replace(/\([0-9]+\)$/, '');
memo[attrName] = {
type: type
};
if (field.primaryKey) {
memo[attrName].primaryKey = field.primaryKey;
}
if (field.unique) {
memo[attrName].unique = field.unique;
}
if (field.indexed) {
memo[attrName].indexed = field.indexed;
}
return memo;
}, {});
return normalized;
};
OracleDialect.prototype.sqlEscapeString = function (string) {
if (_.isUndefined(string))
return null;
return this.stringDelimiter + string + this.stringDelimiter;
};
OracleDialect.prototype.normalizeTableName = function (tableName) {
if (tableName.length < 30) {
return tableName;
}
return crypto.createHash('sha1').update(tableName).digest('base64').replace('=', '');
};
OracleDialect.prototype.createAlias = function(tableAlias,columnName) {
var alias = tableAlias + '_' + columnName;
if(!columnName) {
alias = tableAlias;
}
if (alias.length > 30) {
return crypto.createHash('sha1').update(alias).digest('base64').replace('=', '');
}
return alias;
};
OracleDialect.prototype.afterSelect = function(connection, select) {
if (!select.skipLimitQuery) {
return;
}
select.query = select.skipLimitQuery.from(select.query.as('SKLMT'));
};
OracleDialect.prototype.joinId = function(select, relations) {
if (_.keys(relations).length) {
var joinSubquery = 'select 1 as "id" from dual';
for (var i = 2; i <= _.keys(relations).length; i++) {
joinSubquery += ' UNION ALL SELECT ' + i + ' from dual';
}
select.query.join(select.connection.client.raw('(' + joinSubquery + ') "joins" on 1 = 1'));
}
};
OracleDialect.prototype.selectSkipLimit = function(connection, select) {
var self = this;
if (select.options.skip || select.options.limit) {
if (!select.skipLimitQuery) {
select.skipLimitQuery = connection.client.select('SKLMT.*');
}
if (!select.options.sort) {
var denseRank = connection.client.raw('(dense_rank() over (order by ?? ASC)) "row_PARENT"',[select.alias + '.' + select.pk]);
select.query.select(denseRank);
} else {
var opts = _.cloneDeep(select.options);
delete opts.skip;
delete opts.limit;
if (opts.joins) {
opts.joins.forEach(function(join) {
join.select = [];
});
}
opts.select = [];
var collection = {
tableName: select.tableName,
definition: select.definition
};
if (opts.joins) {
opts.joins.forEach(function(join) {
delete join.criteria.sort;
});
}
var sort = opts.sort;
delete opts.sort;
var SklmtJoinSub = this.select(connection, collection, opts);
SklmtJoinSub.query.select(select.alias + '.' + select.pk);
SklmtJoinSub.query.groupBy(select.alias + '.' + select.pk);
_.keys(sort).forEach(function(toSort) {
var order = sort[toSort];
if (!toSort.includes('.')) {
toSort = select.alias + '.' + toSort;
}
if (toSort !== select.alias + '.' + select.pk) {
var alias = self.createAlias('SORT_', select.alias + '_' + toSort);
if (order === 1) {
SklmtJoinSub.query.min(toSort + ' as ' + alias);
SklmtJoinSub.query.orderBy(alias, 'asc');
} else {
SklmtJoinSub.query.max(toSort + ' as ' + alias);
SklmtJoinSub.query.orderBy(alias, 'desc');
}
} else {
if (order === 1) {
SklmtJoinSub.query.orderBy(toSort, 'asc');
} else {
SklmtJoinSub.query.orderBy(toSort, 'desc');
}
}
});
select.associations.forEach(function(association) {
if (association.join.criteria.sort) {
_.keys(association.join.criteria.sort).forEach(function(toSort) {
var order = association.join.criteria.sort[toSort];
var alias = self.createAlias('SORT_', association.join.alias + '_' + toSort);
if (order === 1) {
SklmtJoinSub.query.min(association.join.alias + '.' + toSort + ' as ' + alias);
SklmtJoinSub.query.orderBy(alias, 'asc');
} else {
SklmtJoinSub.query.max(association.join.alias + '.' + toSort + ' as ' + alias);
SklmtJoinSub.query.orderBy(alias, 'desc');
}
});
}
});
var SklmtJoin = connection.client.select(connection.client.raw('rownum "row_PARENT"'));
SklmtJoin.select('SJB.' + select.pk);
SklmtJoin.from(SklmtJoinSub.query.as('SJB'));
select.query.leftJoin(SklmtJoin.as('SJ'), connection.client.raw('??.?? = ??.??', [select.alias, select.pk, 'SJ', select.pk]));
select.query.select('SJ.row_PARENT');
}
if (select.options.skip && select.options.limit) {
select.skipLimitQuery.where('SKLMT.row_PARENT','>', select.options.skip);
select.skipLimitQuery.where('SKLMT.row_PARENT','<=', select.options.limit + select.options.skip);
} else if (select.options.skip) {
select.skipLimitQuery.where('SKLMT.row_PARENT','>', select.options.skip);
} else if (select.options.limit) {
select.skipLimitQuery.where('SKLMT.row_PARENT','<=', select.options.limit);
}
}
};
OracleDialect.prototype.joinSkipLimit = function(select, association) {
var connection = select.connection;
var join = association.join;
if (join.criteria.skip || join.criteria.limit) {
if (!select.skipLimitQuery) {
select.skipLimitQuery = connection.client.select('SKLMT.*');
}
var skip = join.criteria.skip;
var limit = join.criteria.limit;
delete join.criteria.skip;
delete join.criteria.limit;
this.join(select, association);
join.criteria.skip = skip;
join.criteria.limit = limit;
var sort = '';
if (!join.criteria.sort) {
var pk = connection.getPk(join.child);
join.criteria.sort = {};
join.criteria.sort[pk] = 1;
}
if (join.criteria.sort) {
_.keys(join.criteria.sort).forEach(function (toSort) {
var direction = join.criteria.sort[toSort] === 1 ? 'ASC' : 'DESC';
select.query.orderBy(join.alias + '.' + toSort, direction);
if (sort !== '') {
sort += connection.client.raw(', ?? ' + direction,[join.alias + '.' + toSort]).toString();
} else {
sort = connection.client.raw('order by ?? ' + direction,[join.alias + '.' + toSort]).toString();
}
});
}
var sklmtAlias = this.createAlias('_SKLMT_',join.alias);
var denseRankQuery = this._getDenseRank(connection, sort, select.alias, select.pk, sklmtAlias);
select.query.select(denseRankQuery);
if (skip && limit) {
select.skipLimitQuery.where(function(){
this.andWhere(function() {
this.andWhere('SKLMT.' + sklmtAlias, '>', skip);
this.andWhere('SKLMT.' + sklmtAlias, '<=', skip + limit);
});
// do not skip parent data when child skip >= child count
this.orWhere('SKLMT.' + sklmtAlias, '=', 1);
});
// inform cursor to skip first child data
association.skipFirst = sklmtAlias;
} else if (skip) {
select.skipLimitQuery.where(function(){
this.andWhere('SKLMT.' + sklmtAlias, '>', skip);
// do not skip parent data when child skip >= child count
this.orWhere('SKLMT.' + sklmtAlias, '=', 1);
});
// inform cursor to skip first child data
association.skipFirst = sklmtAlias;
} else if (limit) {
select.skipLimitQuery.where(function(){
this.andWhere('SKLMT.' + sklmtAlias, '<=', limit);
});
}
}
};
OracleDialect.prototype.joinManyToManySkipLimit = function(select, association) {
var connection = select.connection;
var join = association.join;
var junction = association.junction;
if (join.criteria.skip || join.criteria.limit) {
if (!select.skipLimitQuery) {
select.skipLimitQuery = connection.client.select('SKLMT.*');
}
var self = this;
var childDefinition = connection.getCollection(join.child).definition;
var junctionAlias = this.createAlias('junction_', join.alias);
var skip = join.criteria.skip;
var limit = join.criteria.limit;
var sort = '';
select.query.leftJoin(function() {
var query = this;
join.select.forEach(function(columnName) {
if (childDefinition[columnName]) {
query.select(join.child + '.' + columnName);
}
});
this.select(junction.child + '.' + junction.childKey + ' as ' + junctionAlias);
new self.CriteriaProcessor(connection, join.child, join.criteria, this);
this.from(join.child).leftJoin(join.parent, function() {
this.on(join.parent + '.' + join.parentKey, '=', join.child + '.' + join.childKey);
}).as(join.alias);
}, function() {
this.on(select.alias + '.' + junction.parentKey, join.alias + '.' + junctionAlias);
if (association.joinId) {
this.on('joins.id', '=', association.joinId);
}
});
join.select.forEach(function(columnName) {
var childAlias = self.createAlias(association.name, columnName);
select.query.select(join.alias + '.' + columnName + ' as ' + childAlias);
});
if (!join.criteria.sort) {
var pk = connection.getPk(join.child);
join.criteria.sort = {};
join.criteria.sort[pk] = 1;
}
if (join.criteria.sort) {
_.keys(join.criteria.sort).forEach(function (toSort) {
var direction = join.criteria.sort[toSort] === 1 ? 'ASC' : 'DESC';
select.query.orderBy(join.alias + '.' + toSort, direction);
if (sort !== '') {
sort += connection.client.raw(', ?? ' + direction,[join.alias + '.' + toSort]).toString();
} else {
sort = connection.client.raw('order by ?? ' + direction,[join.alias + '.' + toSort]).toString();
}
});
}
var sklmtAlias = this.createAlias('_SKLMT_',join.alias);
var denseRankQuery = this._getDenseRank(connection, sort, select.alias, select.pk, sklmtAlias);
select.query.select(denseRankQuery);
if (skip && limit) {
select.skipLimitQuery.where(function(){
this.andWhere(function() {
this.andWhere('SKLMT.' + sklmtAlias, '>', skip);
this.andWhere('SKLMT.' + sklmtAlias, '<=', skip + limit);
});
// do not skip parent data when child skip >= child count
this.orWhere('SKLMT.' + sklmtAlias, '=', 1);
});
// inform cursor to skip first child data
association.skipFirst = sklmtAlias;
} else if (skip) {
select.skipLimitQuery.where(function(){
this.andWhere('SKLMT.' + sklmtAlias, '>', skip);
// do not skip parent data when child skip >= child count
this.orWhere('SKLMT.' + sklmtAlias, '=', 1);
});
// inform cursor to skip first child data
association.skipFirst = sklmtAlias;
} else if (limit) {
select.skipLimitQuery.where(function(){
this.andWhere('SKLMT.' + sklmtAlias, '<=', limit);
});
}
}
};