不要怂,就是干,撸起袖子干!

Commit f50469b6 by Felix Becker Committed by Mick Hansen

ES6 refactor: dialects / SQLite (#6051)

* ES6 refactor of SQLite ConnectionManager

* ES6 refactor of SQLiteDialect

* Make sqlite Query an ES6 class

* ES6 refactor of SQLite Query

* ES6 refactor of SQLite QueryGenerator

* ES6 refactor of SQLite QueryInterface
1 parent 3107396d
'use strict'; 'use strict';
var AbstractConnectionManager = require('../abstract/connection-manager') const AbstractConnectionManager = require('../abstract/connection-manager');
, ConnectionManager const Promise = require('../../promise');
, Utils = require('../../utils') const dataTypes = require('../../data-types').sqlite;
, Promise = require('../../promise') const sequelizeErrors = require('../../errors');
, dataTypes = require('../../data-types').sqlite const parserStore = require('../parserStore')('sqlite');
, sequelizeErrors = require('../../errors')
, parserStore = require('../parserStore')('sqlite');
ConnectionManager = function(dialect, sequelize) { class ConnectionManager extends AbstractConnectionManager {
this.sequelize = sequelize; constructor(dialect, sequelize) {
this.config = sequelize.config; super(dialect, sequelize);
this.dialect = dialect; this.sequelize = sequelize;
this.dialectName = this.sequelize.options.dialect; this.config = sequelize.config;
this.connections = {}; this.dialect = dialect;
this.dialectName = this.sequelize.options.dialect;
this.connections = {};
// We attempt to parse file location from a connection uri but we shouldn't match sequelize default host. // We attempt to parse file location from a connection uri but we shouldn't match sequelize default host.
if (this.sequelize.options.host === 'localhost') delete this.sequelize.options.host; if (this.sequelize.options.host === 'localhost') delete this.sequelize.options.host;
try { try {
this.lib = require(sequelize.config.dialectModulePath || 'sqlite3').verbose(); this.lib = require(sequelize.config.dialectModulePath || 'sqlite3').verbose();
} catch (err) { } catch (err) {
if (err.code === 'MODULE_NOT_FOUND') { if (err.code === 'MODULE_NOT_FOUND') {
throw new Error('Please install sqlite3 package manually'); throw new Error('Please install sqlite3 package manually');
}
throw err;
} }
throw err;
}
this.refreshTypeParser(dataTypes);
};
Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype); this.refreshTypeParser(dataTypes);
}
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types // Expose this as a method so that the parsing may be updated when the user has added additional, custom types
ConnectionManager.prototype.$refreshTypeParser = function (dataType) { $refreshTypeParser(dataType) {
parserStore.refresh(dataType); parserStore.refresh(dataType);
}; }
ConnectionManager.prototype.$clearTypeParser = function () { $clearTypeParser() {
parserStore.clear(); parserStore.clear();
}; }
ConnectionManager.prototype.getConnection = function(options) { getConnection(options) {
var self = this; options = options || {};
options = options || {}; options.uuid = options.uuid || 'default';
options.uuid = options.uuid || 'default'; options.inMemory = ((this.sequelize.options.storage || this.sequelize.options.host || ':memory:') === ':memory:') ? 1 : 0;
options.inMemory = ((self.sequelize.options.storage || self.sequelize.options.host || ':memory:') === ':memory:') ? 1 : 0;
var dialectOptions = self.sequelize.options.dialectOptions; const dialectOptions = this.sequelize.options.dialectOptions;
options.readWriteMode = dialectOptions && dialectOptions.mode; options.readWriteMode = dialectOptions && dialectOptions.mode;
if (self.connections[options.inMemory || options.uuid]) { if (this.connections[options.inMemory || options.uuid]) {
return Promise.resolve(self.connections[options.inMemory || options.uuid]); return Promise.resolve(this.connections[options.inMemory || options.uuid]);
} }
return new Promise(function (resolve, reject) { return new Promise((resolve, reject) => {
self.connections[options.inMemory || options.uuid] = new self.lib.Database( this.connections[options.inMemory || options.uuid] = new this.lib.Database(
self.sequelize.options.storage || self.sequelize.options.host || ':memory:', this.sequelize.options.storage || this.sequelize.options.host || ':memory:',
options.readWriteMode || (self.lib.OPEN_READWRITE | self.lib.OPEN_CREATE), // default mode options.readWriteMode || (this.lib.OPEN_READWRITE | this.lib.OPEN_CREATE), // default mode
function(err) { err => {
if (err) { if (err) {
if (err.code === 'SQLITE_CANTOPEN') return reject(new sequelizeErrors.ConnectionError(err)); if (err.code === 'SQLITE_CANTOPEN') return reject(new sequelizeErrors.ConnectionError(err));
return reject(new sequelizeErrors.ConnectionError(err)); return reject(new sequelizeErrors.ConnectionError(err));
}
resolve(this.connections[options.inMemory || options.uuid]);
} }
resolve(self.connections[options.inMemory || options.uuid]); );
}).tap(connection => {
if (this.sequelize.options.foreignKeys !== false) {
// Make it possible to define and use foreign key constraints unless
// explicitly disallowed. It's still opt-in per relation
connection.run('PRAGMA FOREIGN_KEYS=ON');
} }
); });
}).tap(function (connection) { }
if (self.sequelize.options.foreignKeys !== false) {
// Make it possible to define and use foreign key constraints unless
// explicitly disallowed. It's still opt-in per relation
connection.run('PRAGMA FOREIGN_KEYS=ON');
}
});
};
ConnectionManager.prototype.releaseConnection = function(connection, force) { releaseConnection(connection, force) {
if (connection.filename === ':memory:' && force !== true) return; if (connection.filename === ':memory:' && force !== true) return;
if (connection.uuid) { if (connection.uuid) {
connection.close(); connection.close();
delete this.connections[connection.uuid]; delete this.connections[connection.uuid];
}
} }
}; }
module.exports = ConnectionManager; module.exports = ConnectionManager;
module.exports.ConnectionManager = ConnectionManager;
module.exports.default = ConnectionManager;
'use strict'; 'use strict';
var _ = require('lodash') const _ = require('lodash');
, Abstract = require('../abstract') const AbstractDialect = require('../abstract');
, ConnectionManager = require('./connection-manager') const ConnectionManager = require('./connection-manager');
, Query = require('./query') const Query = require('./query');
, QueryGenerator = require('./query-generator') const QueryGenerator = require('./query-generator');
, DataTypes = require('../../data-types').sqlite; const DataTypes = require('../../data-types').sqlite;
var SqliteDialect = function(sequelize) { class SqliteDialect extends AbstractDialect {
this.sequelize = sequelize; constructor(sequelize) {
this.connectionManager = new ConnectionManager(this, sequelize); super();
this.QueryGenerator = _.extend({}, QueryGenerator, { this.sequelize = sequelize;
options: sequelize.options, this.connectionManager = new ConnectionManager(this, sequelize);
_dialect: this, this.QueryGenerator = _.extend({}, QueryGenerator, {
sequelize: sequelize options: sequelize.options,
}); _dialect: this,
}; sequelize
});
}
}
SqliteDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supports), { SqliteDialect.prototype.supports = _.merge(_.cloneDeep(AbstractDialect.prototype.supports), {
'DEFAULT': false, 'DEFAULT': false,
'DEFAULT VALUES': true, 'DEFAULT VALUES': true,
'UNION ALL': false, 'UNION ALL': false,
...@@ -43,3 +46,5 @@ SqliteDialect.prototype.TICK_CHAR_LEFT = SqliteDialect.prototype.TICK_CHAR; ...@@ -43,3 +46,5 @@ SqliteDialect.prototype.TICK_CHAR_LEFT = SqliteDialect.prototype.TICK_CHAR;
SqliteDialect.prototype.TICK_CHAR_RIGHT = SqliteDialect.prototype.TICK_CHAR; SqliteDialect.prototype.TICK_CHAR_RIGHT = SqliteDialect.prototype.TICK_CHAR;
module.exports = SqliteDialect; module.exports = SqliteDialect;
module.exports.SqliteDialect = SqliteDialect;
module.exports.default = SqliteDialect;
'use strict'; 'use strict';
/* jshint -W110 */ /* jshint -W110 */
var Utils = require('../../utils') const Utils = require('../../utils');
, Transaction = require('../../transaction') const Transaction = require('../../transaction');
, _ = require('lodash'); const _ = require('lodash');
var MySqlQueryGenerator = Utils._.extend( const MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require('../abstract/query-generator')), Utils._.clone(require('../abstract/query-generator')),
Utils._.clone(require('../mysql/query-generator')) Utils._.clone(require('../mysql/query-generator'))
); );
var QueryGenerator = { const QueryGenerator = {
options: {}, options: {},
dialect: 'sqlite', dialect: 'sqlite',
createSchema: function() { createSchema() {
var query = "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
return Utils._.template(query)({});
}, },
showSchemasQuery: function() { showSchemasQuery() {
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
}, },
versionQuery: function() { versionQuery() {
return 'SELECT sqlite_version() as `version`'; return 'SELECT sqlite_version() as `version`';
}, },
createTableQuery: function(tableName, attributes, options) { createTableQuery(tableName, attributes, options) {
options = options || {}; options = options || {};
var query = 'CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)' const primaryKeys = [];
, primaryKeys = [] const needsMultiplePrimaryKeys = (Utils._.values(attributes).filter(definition => _.includes(definition, 'PRIMARY KEY')).length > 1);
, needsMultiplePrimaryKeys = (Utils._.values(attributes).filter(function(definition) { const attrArray = [];
return Utils._.includes(definition, 'PRIMARY KEY');
}).length > 1)
, attrStr = [];
for (var attr in attributes) { for (const attr in attributes) {
if (attributes.hasOwnProperty(attr)) { if (attributes.hasOwnProperty(attr)) {
var dataType = attributes[attr]; let dataType = attributes[attr];
var containsAutoIncrement = Utils._.includes(dataType, 'AUTOINCREMENT'); const containsAutoIncrement = Utils._.includes(dataType, 'AUTOINCREMENT');
if (containsAutoIncrement) { if (containsAutoIncrement) {
dataType = dataType.replace(/BIGINT/, 'INTEGER'); dataType = dataType.replace(/BIGINT/, 'INTEGER');
} }
var dataTypeString = dataType; let dataTypeString = dataType;
if (Utils._.includes(dataType, 'PRIMARY KEY')) { if (Utils._.includes(dataType, 'PRIMARY KEY')) {
if (Utils._.includes(dataType, 'INTEGER')) { // Only INTEGER is allowed for primary key, see https://github.com/sequelize/sequelize/issues/969 (no lenght, unsigned etc) if (Utils._.includes(dataType, 'INTEGER')) { // Only INTEGER is allowed for primary key, see https://github.com/sequelize/sequelize/issues/969 (no lenght, unsigned etc)
dataTypeString = containsAutoIncrement ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INTEGER PRIMARY KEY'; dataTypeString = containsAutoIncrement ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INTEGER PRIMARY KEY';
...@@ -57,82 +53,69 @@ var QueryGenerator = { ...@@ -57,82 +53,69 @@ var QueryGenerator = {
dataTypeString = dataType.replace(/PRIMARY KEY/, 'NOT NULL'); dataTypeString = dataType.replace(/PRIMARY KEY/, 'NOT NULL');
} }
} }
attrStr.push(this.quoteIdentifier(attr) + ' ' + dataTypeString); attrArray.push(this.quoteIdentifier(attr) + ' ' + dataTypeString);
} }
} }
var values = { const table = this.quoteTable(tableName);
table: this.quoteTable(tableName), let attrStr = attrArray.join(', ');
attributes: attrStr.join(', '), const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', ');
charset: (options.charset ? 'DEFAULT CHARSET=' + options.charset : '')
}
, pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk); }.bind(this)).join(', ');
if (!!options.uniqueKeys) { if (!!options.uniqueKeys) {
Utils._.each(options.uniqueKeys, function(columns) { Utils._.each(options.uniqueKeys, columns => {
if (!columns.singleField) { // If it's a single field its handled in column def, not as an index if (!columns.singleField) { // If it's a single field its handled in column def, not as an index
values.attributes += ', UNIQUE (' + columns.fields.join(', ') + ')'; attrStr += ', UNIQUE (' + columns.fields.join(', ') + ')';
} }
}); });
} }
if (pkString.length > 0) { if (pkString.length > 0) {
values.attributes += ', PRIMARY KEY (' + pkString + ')'; attrStr += ', PRIMARY KEY (' + pkString + ')';
} }
var sql = Utils._.template(query)(values).trim() + ';'; const sql = `CREATE TABLE IF NOT EXISTS ${table} (${attrStr});`;
return this.replaceBooleanDefaults(sql); return this.replaceBooleanDefaults(sql);
}, },
booleanValue: function(value){ booleanValue(value){
return !!value ? 1 : 0; return !!value ? 1 : 0;
}, },
addColumnQuery: function(table, key, dataType) { addColumnQuery(table, key, dataType) {
var query = 'ALTER TABLE <%= table %> ADD <%= attribute %>;'
, attributes = {};
const attributes = {};
attributes[key] = dataType; attributes[key] = dataType;
var fields = this.attributesToSQL(attributes, { const fields = this.attributesToSQL(attributes, {context: 'addColumn'});
context: 'addColumn' const attribute = this.quoteIdentifier(key) + ' ' + fields[key];
});
var attribute = Utils._.template('<%= key %> <%= definition %>')({
key: this.quoteIdentifier(key),
definition: fields[key]
});
var sql = Utils._.template(query)({ const sql = `ALTER TABLE ${this.quoteTable(table)} ADD ${attribute};`;
table: this.quoteTable(table),
attribute: attribute
});
return this.replaceBooleanDefaults(sql); return this.replaceBooleanDefaults(sql);
}, },
showTablesQuery: function() { showTablesQuery() {
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';";
}, },
upsertQuery: function (tableName, insertValues, updateValues, where, rawAttributes, options) { upsertQuery(tableName, insertValues, updateValues, where, rawAttributes, options) {
options.ignore = true; options.ignore = true;
var sql = this.insertQuery(tableName, insertValues, rawAttributes, options) + ' ' + this.updateQuery(tableName, updateValues, where, options, rawAttributes); const sql = this.insertQuery(tableName, insertValues, rawAttributes, options) + ' ' + this.updateQuery(tableName, updateValues, where, options, rawAttributes);
return sql; return sql;
}, },
updateQuery: function(tableName, attrValueHash, where, options, attributes) { updateQuery(tableName, attrValueHash, where, options, attributes) {
options = options || {}; options = options || {};
_.defaults(options, this.options); _.defaults(options, this.options);
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, options.omitNull, options); attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, options.omitNull, options);
var query = 'UPDATE <%= table %> SET <%= values %> <%= where %>' const modelAttributeMap = {};
, modelAttributeMap = {} const values = [];
, values = [];
if (attributes) { if (attributes) {
_.each(attributes, function(attribute, key) { _.each(attributes, (attribute, key) => {
modelAttributeMap[key] = attribute; modelAttributeMap[key] = attribute;
if (attribute.field) { if (attribute.field) {
modelAttributeMap[attribute.field] = attribute; modelAttributeMap[attribute.field] = attribute;
...@@ -140,94 +123,81 @@ var QueryGenerator = { ...@@ -140,94 +123,81 @@ var QueryGenerator = {
}); });
} }
for (var key in attrValueHash) { for (const key in attrValueHash) {
var value = attrValueHash[key]; const value = attrValueHash[key];
values.push(this.quoteIdentifier(key) + '=' + this.escape(value, (modelAttributeMap && modelAttributeMap[key] || undefined), { context: 'UPDATE' })); values.push(this.quoteIdentifier(key) + '=' + this.escape(value, (modelAttributeMap && modelAttributeMap[key] || undefined), { context: 'UPDATE' }));
} }
var replacements = { return `UPDATE ${this.quoteTable(tableName)} SET ${values.join(',')} ${this.whereQuery(where)}`;
table: this.quoteTable(tableName),
values: values.join(','),
where: this.whereQuery(where)
};
return Utils._.template(query)(replacements).trim();
}, },
deleteQuery: function(tableName, where, options) { deleteQuery(tableName, where, options) {
options = options || {}; options = options || {};
var query = 'DELETE FROM <%= table %><%= where %>'; let whereClause = this.getWhereConditions(where);
var replacements = { if (whereClause) {
table: this.quoteTable(tableName), whereClause = ' WHERE ' + whereClause;
where: this.getWhereConditions(where)
};
if (replacements.where) {
replacements.where = ' WHERE ' + replacements.where;
} }
return Utils._.template(query)(replacements); return `DELETE FROM ${this.quoteTable(tableName)}${whereClause}`;
}, },
attributesToSQL: function(attributes) { attributesToSQL(attributes) {
var result = {}; const result = {};
for (var name in attributes) { for (const name in attributes) {
var dataType = attributes[name]; const dataType = attributes[name];
var fieldName = dataType.field || name; const fieldName = dataType.field || name;
if (Utils._.isObject(dataType)) { if (Utils._.isObject(dataType)) {
var template = '<%= type %>' let sql = dataType.type.toString();
, replacements = { type: dataType.type.toString() };
if (dataType.hasOwnProperty('allowNull') && !dataType.allowNull) { if (dataType.hasOwnProperty('allowNull') && !dataType.allowNull) {
template += ' NOT NULL'; sql += ' NOT NULL';
} }
if (Utils.defaultValueSchemable(dataType.defaultValue)) { if (Utils.defaultValueSchemable(dataType.defaultValue)) {
// TODO thoroughly check that DataTypes.NOW will properly // TODO thoroughly check that DataTypes.NOW will properly
// get populated on all databases as DEFAULT value // get populated on all databases as DEFAULT value
// i.e. mysql requires: DEFAULT CURRENT_TIMESTAMP // i.e. mysql requires: DEFAULT CURRENT_TIMESTAMP
template += ' DEFAULT <%= defaultValue %>'; sql += ' DEFAULT ' + this.escape(dataType.defaultValue, dataType);
replacements.defaultValue = this.escape(dataType.defaultValue, dataType);
} }
if (dataType.unique === true) { if (dataType.unique === true) {
template += ' UNIQUE'; sql += ' UNIQUE';
} }
if (dataType.primaryKey) { if (dataType.primaryKey) {
template += ' PRIMARY KEY'; sql += ' PRIMARY KEY';
if (dataType.autoIncrement) { if (dataType.autoIncrement) {
template += ' AUTOINCREMENT'; sql += ' AUTOINCREMENT';
} }
} }
if(dataType.references) { if(dataType.references) {
template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)'; const referencesTable = this.quoteTable(dataType.references.model);
replacements.referencesTable = this.quoteTable(dataType.references.model);
let referencesKey;
if(dataType.references.key) { if(dataType.references.key) {
replacements.referencesKey = this.quoteIdentifier(dataType.references.key); referencesKey = this.quoteIdentifier(dataType.references.key);
} else { } else {
replacements.referencesKey = this.quoteIdentifier('id'); referencesKey = this.quoteIdentifier('id');
} }
sql += ` REFERENCES ${referencesTable} (${referencesKey})`;
if(dataType.onDelete) { if(dataType.onDelete) {
template += ' ON DELETE <%= onDeleteAction %>'; sql += ' ON DELETE ' + dataType.onDelete.toUpperCase();
replacements.onDeleteAction = dataType.onDelete.toUpperCase();
} }
if(dataType.onUpdate) { if(dataType.onUpdate) {
template += ' ON UPDATE <%= onUpdateAction %>'; sql += ' ON UPDATE ' + dataType.onUpdate.toUpperCase();
replacements.onUpdateAction = dataType.onUpdate.toUpperCase();
} }
} }
result[fieldName] = Utils._.template(template)(replacements); result[fieldName] = sql;
} else { } else {
result[fieldName] = dataType; result[fieldName] = dataType;
} }
...@@ -236,12 +206,12 @@ var QueryGenerator = { ...@@ -236,12 +206,12 @@ var QueryGenerator = {
return result; return result;
}, },
findAutoIncrementField: function(factory) { findAutoIncrementField(factory) {
var fields = []; const fields = [];
for (var name in factory.attributes) { for (const name in factory.attributes) {
if (factory.attributes.hasOwnProperty(name)) { if (factory.attributes.hasOwnProperty(name)) {
var definition = factory.attributes[name]; const definition = factory.attributes[name];
if (definition && definition.autoIncrement) { if (definition && definition.autoIncrement) {
fields.push(name); fields.push(name);
} }
...@@ -251,38 +221,34 @@ var QueryGenerator = { ...@@ -251,38 +221,34 @@ var QueryGenerator = {
return fields; return fields;
}, },
showIndexesQuery: function(tableName) { showIndexesQuery(tableName) {
var sql = 'PRAGMA INDEX_LIST(<%= tableName %>)'; return `PRAGMA INDEX_LIST(${this.quoteTable(tableName)})`;
return Utils._.template(sql)({ tableName: this.quoteTable(tableName) });
}, },
removeIndexQuery: function(tableName, indexNameOrAttributes) { removeIndexQuery(tableName, indexNameOrAttributes) {
var sql = 'DROP INDEX IF EXISTS <%= indexName %>' let indexName = indexNameOrAttributes;
, indexName = indexNameOrAttributes;
if (typeof indexName !== 'string') { if (typeof indexName !== 'string') {
indexName = Utils.inflection.underscore(tableName + '_' + indexNameOrAttributes.join('_')); indexName = Utils.inflection.underscore(tableName + '_' + indexNameOrAttributes.join('_'));
} }
return Utils._.template(sql)( { tableName: this.quoteIdentifiers(tableName), indexName: indexName }); return 'DROP INDEX IF EXISTS ' + indexName;
}, },
describeTableQuery: function(tableName, schema, schemaDelimiter) { describeTableQuery(tableName, schema, schemaDelimiter) {
var table = {}; const table = {
table.$schema = schema; $schema: schema,
table.$schemaDelimiter = schemaDelimiter; $schemaDelimiter: schemaDelimiter,
table.tableName = tableName; tableName
};
var sql = 'PRAGMA TABLE_INFO(<%= tableName %>);'; return `PRAGMA TABLE_INFO(${this.quoteTable(this.addSchema(table))});`;
return Utils._.template(sql)({tableName: this.quoteTable(this.addSchema(table))});
}, },
removeColumnQuery: function(tableName, attributes) { removeColumnQuery(tableName, attributes) {
var backupTableName
, query;
attributes = this.attributesToSQL(attributes); attributes = this.attributesToSQL(attributes);
let backupTableName;
if (typeof tableName === 'object') { if (typeof tableName === 'object') {
backupTableName = { backupTableName = {
tableName: tableName.tableName + '_backup', tableName: tableName.tableName + '_backup',
...@@ -292,25 +258,21 @@ var QueryGenerator = { ...@@ -292,25 +258,21 @@ var QueryGenerator = {
backupTableName = tableName + '_backup'; backupTableName = tableName + '_backup';
} }
query = [ const quotedTableName = this.quoteTable(tableName);
this.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'), const quotedBackupTableName = this.quoteTable(backupTableName);
'INSERT INTO <%= backupTableName %> SELECT <%= attributeNames %> FROM <%= tableName %>;', const attributeNames = Object.keys(attributes).join(', ');
'DROP TABLE <%= tableName %>;',
this.createTableQuery(tableName, attributes), return this.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE')
'INSERT INTO <%= tableName %> SELECT <%= attributeNames %> FROM <%= backupTableName %>;', + `INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};`
'DROP TABLE <%= backupTableName %>;' + `DROP TABLE ${quotedTableName};`
].join(''); + this.createTableQuery(tableName, attributes)
+ `INSERT INTO ${quotedTableName} SELECT ${attributeNames} FROM ${quotedBackupTableName};`
return Utils._.template(query)({ + `DROP TABLE ${quotedBackupTableName};`;
tableName: this.quoteTable(tableName),
backupTableName: this.quoteTable(backupTableName),
attributeNames: Utils._.keys(attributes).join(', ')
});
}, },
renameColumnQuery: function(tableName, attrNameBefore, attrNameAfter, attributes) { renameColumnQuery(tableName, attrNameBefore, attrNameAfter, attributes) {
var backupTableName
, query; let backupTableName;
attributes = this.attributesToSQL(attributes); attributes = this.attributesToSQL(attributes);
...@@ -323,28 +285,22 @@ var QueryGenerator = { ...@@ -323,28 +285,22 @@ var QueryGenerator = {
backupTableName = tableName + '_backup'; backupTableName = tableName + '_backup';
} }
query = [ const quotedTableName = this.quoteTable(tableName);
this.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'), const quotedBackupTableName = this.quoteTable(backupTableName);
'INSERT INTO <%= backupTableName %> SELECT <%= attributeNamesImport %> FROM <%= tableName %>;', const attributeNamesImport = Object.keys(attributes).map(attr =>
'DROP TABLE <%= tableName %>;', (attrNameAfter === attr) ? this.quoteIdentifier(attrNameBefore) + ' AS ' + this.quoteIdentifier(attr) : this.quoteIdentifier(attr)
this.createTableQuery(tableName, attributes), ).join(', ');
'INSERT INTO <%= tableName %> SELECT <%= attributeNamesExport %> FROM <%= backupTableName %>;', const attributeNamesExport = Object.keys(attributes).map(attr => this.quoteIdentifier(attr)).join(', ');
'DROP TABLE <%= backupTableName %>;'
].join(''); return this.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE')
+ `INSERT INTO ${quotedBackupTableName} SELECT ${attributeNamesImport} FROM ${quotedTableName};`
return Utils._.template(query)({ + `DROP TABLE ${quotedTableName};`
tableName: this.quoteTable(tableName), + this.createTableQuery(tableName, attributes)
backupTableName: this.quoteTable(backupTableName), + `INSERT INTO ${quotedTableName} SELECT ${attributeNamesExport} FROM ${quotedBackupTableName};`
attributeNamesImport: Utils._.keys(attributes).map(function(attr) { + `DROP TABLE ${quotedBackupTableName};`;
return (attrNameAfter === attr) ? this.quoteIdentifier(attrNameBefore) + ' AS ' + this.quoteIdentifier(attr) : this.quoteIdentifier(attr);
}.bind(this)).join(', '),
attributeNamesExport: Utils._.keys(attributes).map(function(attr) {
return this.quoteIdentifier(attr);
}.bind(this)).join(', ')
});
}, },
startTransactionQuery: function(transaction, options) { startTransactionQuery(transaction, options) {
if (transaction.parent) { if (transaction.parent) {
return 'SAVEPOINT ' + this.quoteIdentifier(transaction.name) + ';'; return 'SAVEPOINT ' + this.quoteIdentifier(transaction.name) + ';';
} }
...@@ -352,12 +308,12 @@ var QueryGenerator = { ...@@ -352,12 +308,12 @@ var QueryGenerator = {
return 'BEGIN ' + transaction.options.type + ' TRANSACTION;'; return 'BEGIN ' + transaction.options.type + ' TRANSACTION;';
}, },
setAutocommitQuery: function() { setAutocommitQuery() {
// SQLite does not support SET autocommit // SQLite does not support SET autocommit
return null; return null;
}, },
setIsolationLevelQuery: function(value) { setIsolationLevelQuery(value) {
switch (value) { switch (value) {
case Transaction.ISOLATION_LEVELS.REPEATABLE_READ: case Transaction.ISOLATION_LEVELS.REPEATABLE_READ:
return '-- SQLite is not able to choose the isolation level REPEATABLE READ.'; return '-- SQLite is not able to choose the isolation level REPEATABLE READ.';
...@@ -372,25 +328,24 @@ var QueryGenerator = { ...@@ -372,25 +328,24 @@ var QueryGenerator = {
} }
}, },
replaceBooleanDefaults: function(sql) { replaceBooleanDefaults(sql) {
return sql.replace(/DEFAULT '?false'?/g, 'DEFAULT 0').replace(/DEFAULT '?true'?/g, 'DEFAULT 1'); return sql.replace(/DEFAULT '?false'?/g, 'DEFAULT 0').replace(/DEFAULT '?true'?/g, 'DEFAULT 1');
}, },
quoteIdentifier: function(identifier) { quoteIdentifier(identifier) {
if (identifier === '*') return identifier; if (identifier === '*') return identifier;
return Utils.addTicks(identifier, '`'); return Utils.addTicks(identifier, '`');
}, },
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
* *
* @param {String} tableName The name of the table. * @param {String} tableName The name of the table.
* @param {String} schemaName The name of the schema. * @param {String} schemaName The name of the schema.
* @return {String} The generated sql query. * @return {String} The generated sql query.
*/ */
getForeignKeysQuery: function(tableName, schemaName) { getForeignKeysQuery(tableName, schemaName) {
var sql = 'PRAGMA foreign_key_list(<%= tableName %>)'; return `PRAGMA foreign_key_list(${tableName})`;
return Utils._.template(sql)({ tableName: tableName });
} }
}; };
......
'use strict'; 'use strict';
var Utils = require('../../utils') const Utils = require('../../utils');
, Promise = require('../../promise'); const Promise = require('../../promise');
/** /**
Returns an object that treats SQLite's inabilities to do certain queries. Returns an object that treats SQLite's inabilities to do certain queries.
...@@ -25,21 +25,20 @@ var Utils = require('../../utils') ...@@ -25,21 +25,20 @@ var Utils = require('../../utils')
@since 1.6.0 @since 1.6.0
*/ */
var removeColumn = function(tableName, attributeName, options) { function removeColumn(tableName, attributeName, options) {
var self = this;
options = options || {}; options = options || {};
return this.describeTable(tableName, options).then(function(fields) { /* jshint validthis:true */
return this.describeTable(tableName, options).then(fields => {
delete fields[attributeName]; delete fields[attributeName];
var sql = self.QueryGenerator.removeColumnQuery(tableName, fields) const sql = this.QueryGenerator.removeColumnQuery(tableName, fields);
, subQueries = sql.split(';').filter(function(q) { return q !== ''; }); const subQueries = sql.split(';').filter(q => q !== '');
return Promise.each(subQueries, function(subQuery) { return Promise.each(subQueries, subQuery => this.sequelize.query(subQuery + ';', Utils._.assign({raw: true}, options)));
return self.sequelize.query(subQuery + ';', Utils._.assign({raw: true}, options));
});
}); });
}; }
exports.removeColumn = removeColumn;
/** /**
A wrapper that fixes SQLite's inability to change columns from existing tables. A wrapper that fixes SQLite's inability to change columns from existing tables.
...@@ -56,22 +55,21 @@ var removeColumn = function(tableName, attributeName, options) { ...@@ -56,22 +55,21 @@ var removeColumn = function(tableName, attributeName, options) {
@since 1.6.0 @since 1.6.0
*/ */
var changeColumn = function(tableName, attributes, options) { function changeColumn(tableName, attributes, options) {
var attributeName = Utils._.keys(attributes)[0] const attributeName = Object.keys(attributes)[0];
, self = this;
options = options || {}; options = options || {};
return this.describeTable(tableName, options).then(function(fields) { /* jshint validthis:true */
return this.describeTable(tableName, options).then(fields => {
fields[attributeName] = attributes[attributeName]; fields[attributeName] = attributes[attributeName];
var sql = self.QueryGenerator.removeColumnQuery(tableName, fields) const sql = this.QueryGenerator.removeColumnQuery(tableName, fields);
, subQueries = sql.split(';').filter(function(q) { return q !== ''; }); const subQueries = sql.split(';').filter(q => q !== '');
return Promise.each(subQueries, function(subQuery) { return Promise.each(subQueries, subQuery => this.sequelize.query(subQuery + ';', Utils._.assign({raw: true}, options)));
return self.sequelize.query(subQuery + ';', Utils._.assign({raw: true}, options));
});
}); });
}; }
exports.changeColumn = changeColumn;
/** /**
A wrapper that fixes SQLite's inability to rename columns from existing tables. A wrapper that fixes SQLite's inability to rename columns from existing tables.
...@@ -89,25 +87,18 @@ var changeColumn = function(tableName, attributes, options) { ...@@ -89,25 +87,18 @@ var changeColumn = function(tableName, attributes, options) {
@since 1.6.0 @since 1.6.0
*/ */
var renameColumn = function(tableName, attrNameBefore, attrNameAfter, options) { function renameColumn(tableName, attrNameBefore, attrNameAfter, options) {
var self = this;
options = options || {}; options = options || {};
return this.describeTable(tableName, options).then(function(fields) { /* jshint validthis:true */
return this.describeTable(tableName, options).then(fields => {
fields[attrNameAfter] = Utils._.clone(fields[attrNameBefore]); fields[attrNameAfter] = Utils._.clone(fields[attrNameBefore]);
delete fields[attrNameBefore]; delete fields[attrNameBefore];
var sql = self.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields) const sql = this.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields);
, subQueries = sql.split(';').filter(function(q) { return q !== ''; }); const subQueries = sql.split(';').filter(q => q !== '');
return Promise.each(subQueries, function(subQuery) { return Promise.each(subQueries, subQuery => this.sequelize.query(subQuery + ';', Utils._.assign({raw: true}, options)));
return self.sequelize.query(subQuery + ';', Utils._.assign({raw: true}, options));
});
}); });
}; }
exports.renameColumn = renameColumn;
module.exports = {
removeColumn: removeColumn,
changeColumn: changeColumn,
renameColumn: renameColumn
};
'use strict'; 'use strict';
var Utils = require('../../utils') const _ = require('lodash');
, _ = require('lodash') const Promise = require('../../promise');
, Promise = require('../../promise') const AbstractQuery = require('../abstract/query');
, AbstractQuery = require('../abstract/query') const QueryTypes = require('../../query-types');
, QueryTypes = require('../../query-types') const sequelizeErrors = require('../../errors.js');
, sequelizeErrors = require('../../errors.js') const parserStore = require('../parserStore')('sqlite');
, parserStore = require('../parserStore')('sqlite');
class Query extends AbstractQuery {
var Query = function(database, sequelize, options) {
this.database = database; constructor(database, sequelize, options) {
this.sequelize = sequelize; super();
this.instance = options.instance; this.database = database;
this.model = options.model; this.sequelize = sequelize;
this.options = _.extend({ this.instance = options.instance;
logging: console.log, this.model = options.model;
plain: false, this.options = _.extend({
raw: false logging: console.log,
}, options || {}); plain: false,
raw: false
this.checkLoggingOption(); }, options || {});
};
Utils.inherit(Query, AbstractQuery); this.checkLoggingOption();
}
Query.prototype.getInsertIdField = function() {
return 'lastID'; getInsertIdField() {
}; return 'lastID';
/**
* rewrite query with parameters
*/
Query.formatBindParameters = function(sql, values, dialect) {
var bindParam = [];
if (Array.isArray(values)) {
bindParam = {};
values.forEach(function(v, i) {
bindParam['$'+(i+1)] = v;
});
sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
} else {
bindParam = {};
if (typeof values === 'object') {
Object.keys(values).forEach(function(k) {
bindParam['$'+k] = values[k];
});
}
sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
} }
return [sql, bindParam];
};
Query.prototype.$collectModels = function(include, prefix) {
var ret = {};
if (include) {
include.forEach(function (include) {
var key;
if (!prefix) {
key = include.as;
} else {
key = prefix + '.' + include.as;
}
ret[key] = include.model;
if (include.include) { /**
_.merge(ret, this.$collectModels(include.include, key)); * rewrite query with parameters
*/
static formatBindParameters(sql, values, dialect) {
let bindParam;
if (Array.isArray(values)) {
bindParam = {};
values.forEach((v, i) => {
bindParam['$'+(i+1)] = v;
});
sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
} else {
bindParam = {};
if (typeof values === 'object') {
for (const k of Object.keys(values)) {
bindParam['$'+k] = values[k];
}
} }
}, this); sql = AbstractQuery.formatBindParameters(sql, values, dialect, { skipValueReplace: true })[0];
}
return [sql, bindParam];
} }
return ret; $collectModels(include, prefix) {
}; const ret = {};
Query.prototype.run = function(sql, parameters) { if (include) {
var self = this for (const _include of include) {
, promise; let key;
if (!prefix) {
key = _include.as;
} else {
key = prefix + '.' + _include.as;
}
ret[key] = _include.model;
this.sql = sql; if (_include.include) {
var method = self.getDatabaseMethod(); _.merge(ret, this.$collectModels(_include.include, key));
if (method === 'exec') { }
// exec does not support bind parameter }
sql = AbstractQuery.formatBindParameters(sql, self.options.bind, self.options.dialect, { skipUnescape: true })[0]; }
this.sql = sql;
return ret;
} }
//do we need benchmark for this query execution run(sql, parameters) {
var benchmark = this.sequelize.options.benchmark || this.options.benchmark; this.sql = sql;
const method = this.getDatabaseMethod();
if (method === 'exec') {
// exec does not support bind parameter
sql = AbstractQuery.formatBindParameters(sql, this.options.bind, this.options.dialect, { skipUnescape: true })[0];
this.sql = sql;
}
if (benchmark) { //do we need benchmark for this query execution
var queryBegin = Date.now(); const benchmark = this.sequelize.options.benchmark || this.options.benchmark;
} else {
this.sequelize.log('Executing (' + (this.database.uuid || 'default') + '): ' + this.sql, this.options);
}
promise = new Promise(function(resolve) { let queryBegin;
var columnTypes = {}; if (benchmark) {
self.database.serialize(function() { queryBegin = Date.now();
var executeSql = function() { } else {
if (self.sql.indexOf('-- ') === 0) { this.sequelize.log('Executing (' + (this.database.uuid || 'default') + '): ' + this.sql, this.options);
return resolve(); }
} else {
resolve(new Promise(function(resolve, reject) {
var afterExecute = function(err, results) {
if (benchmark) { return new Promise(resolve => {
self.sequelize.log('Executed (' + (self.database.uuid || 'default') + '): ' + self.sql, (Date.now() - queryBegin), self.options); const columnTypes = {};
} this.database.serialize(() => {
const executeSql = () => {
if (this.sql.indexOf('-- ') === 0) {
return resolve();
} else {
resolve(new Promise((resolve, reject) => {
const query = this;
// cannot use arrow function here because the function is bound to the statement
function afterExecute(err, results) {
/* jshint validthis:true */
if (benchmark) {
query.sequelize.log('Executed (' + (query.database.uuid || 'default') + '): ' + query.sql, (Date.now() - queryBegin), query.options);
}
if (err) { if (err) {
err.sql = self.sql; err.sql = query.sql;
reject(self.formatError(err)); reject(query.formatError(err));
} else { } else {
var metaData = this const metaData = this;
, result = self.instance; let result = query.instance;
// add the inserted row id to the instance // add the inserted row id to the instance
if (self.isInsertQuery(results, metaData)) { if (query.isInsertQuery(results, metaData)) {
self.handleInsertQuery(results, metaData); query.handleInsertQuery(results, metaData);
if (!self.instance) { if (!query.instance) {
result = metaData[self.getInsertIdField()]; result = metaData[query.getInsertIdField()];
}
} }
}
if (self.sql.indexOf('sqlite_master') !== -1) { if (query.sql.indexOf('sqlite_master') !== -1) {
result = results.map(function(resultSet) { return resultSet.name; }); result = results.map(resultSet => resultSet.name);
} else if (self.isSelectQuery()) { } else if (query.isSelectQuery()) {
if (!self.options.raw) { if (!query.options.raw) {
// This is a map of prefix strings to models, e.g. user.projects -> Project model // This is a map of prefix strings to models, e.g. user.projects -> Project model
var prefixes = self.$collectModels(self.options.include); const prefixes = query.$collectModels(query.options.include);
results = results.map(function(result) { results = results.map(result => {
return _.mapValues(result, function (value, name) { return _.mapValues(result, (value, name) => {
var model; let model;
if (name.indexOf('.') !== -1) { if (name.indexOf('.') !== -1) {
var lastind = name.lastIndexOf('.'); const lastind = name.lastIndexOf('.');
model = prefixes[name.substr(0, lastind)]; model = prefixes[name.substr(0, lastind)];
name = name.substr(lastind + 1);
} else {
model = self.options.model;
}
var tableName = model.getTableName().toString().replace(/`/g, '')
, tableTypes = columnTypes[tableName] || {};
if (tableTypes && !(name in tableTypes)) {
// The column is aliased
_.forOwn(model.rawAttributes, function (attribute, key) {
if (name === key && attribute.field) {
name = attribute.field;
return false;
}
});
}
var type = tableTypes[name];
if (type) {
if (type.indexOf('(') !== -1) {
// Remove the lenght part
type = type.substr(0, type.indexOf('('));
}
type = type.replace('UNSIGNED', '').replace('ZEROFILL', '');
type = type.trim().toUpperCase();
var parse = parserStore.get(type);
if (value !== null && parse) { name = name.substr(lastind + 1);
return parse(value, { timezone: self.sequelize.options.timezone}); } else {
model = query.options.model;
} }
}
return value;
});
});
}
result = self.handleSelectQuery(results); const tableName = model.getTableName().toString().replace(/`/g, '');
} else if (self.isShowOrDescribeQuery()) { const tableTypes = columnTypes[tableName] || {};
result = results;
} else if (self.sql.indexOf('PRAGMA INDEX_LIST') !== -1) { if (tableTypes && !(name in tableTypes)) {
result = self.handleShowIndexesQuery(results); // The column is aliased
} else if (self.sql.indexOf('PRAGMA INDEX_INFO') !== -1) { _.forOwn(model.rawAttributes, (attribute, key) => {
result = results; if (name === key && attribute.field) {
} else if (self.sql.indexOf('PRAGMA TABLE_INFO') !== -1) { name = attribute.field;
// this is the sqlite way of getting the metadata of a table return false;
result = {}; }
});
var defaultValue; }
results.forEach(function(_result) {
if (_result.dflt_value === null) {
// Column schema omits any "DEFAULT ..."
defaultValue = undefined;
} else if (_result.dflt_value === 'NULL') {
// Column schema is a "DEFAULT NULL"
defaultValue = null;
} else {
defaultValue = _result.dflt_value;
}
result[_result.name] = { let type = tableTypes[name];
type: _result.type, if (type) {
allowNull: (_result.notnull === 0), if (type.indexOf('(') !== -1) {
defaultValue: defaultValue, // Remove the lenght part
primaryKey : (_result.pk === 1) type = type.substr(0, type.indexOf('('));
}; }
type = type.replace('UNSIGNED', '').replace('ZEROFILL', '');
type = type.trim().toUpperCase();
const parse = parserStore.get(type);
if (result[_result.name].type === 'TINYINT(1)') { if (value !== null && parse) {
result[_result.name].defaultValue = { '0': false, '1': true }[result[_result.name].defaultValue]; return parse(value, { timezone: query.sequelize.options.timezone});
}
}
return value;
});
});
} }
if (typeof result[_result.name].defaultValue === 'string') { result = query.handleSelectQuery(results);
result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, ''); } else if (query.isShowOrDescribeQuery()) {
result = results;
} else if (query.sql.indexOf('PRAGMA INDEX_LIST') !== -1) {
result = query.handleShowIndexesQuery(results);
} else if (query.sql.indexOf('PRAGMA INDEX_INFO') !== -1) {
result = results;
} else if (query.sql.indexOf('PRAGMA TABLE_INFO') !== -1) {
// this is the sqlite way of getting the metadata of a table
result = {};
let defaultValue;
for (const _result of results) {
if (_result.dflt_value === null) {
// Column schema omits any "DEFAULT ..."
defaultValue = undefined;
} else if (_result.dflt_value === 'NULL') {
// Column schema is a "DEFAULT NULL"
defaultValue = null;
} else {
defaultValue = _result.dflt_value;
}
result[_result.name] = {
type: _result.type,
allowNull: (_result.notnull === 0),
defaultValue,
primaryKey : (_result.pk === 1)
};
if (result[_result.name].type === 'TINYINT(1)') {
result[_result.name].defaultValue = { '0': false, '1': true }[result[_result.name].defaultValue];
}
if (typeof result[_result.name].defaultValue === 'string') {
result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, '');
}
} }
}); } else if (query.sql.indexOf('PRAGMA foreign_keys;') !== -1) {
} else if (self.sql.indexOf('PRAGMA foreign_keys;') !== -1) { result = results[0];
result = results[0]; } else if (query.sql.indexOf('PRAGMA foreign_keys') !== -1) {
} else if (self.sql.indexOf('PRAGMA foreign_keys') !== -1) { result = results;
result = results; } else if (query.sql.indexOf('PRAGMA foreign_key_list') !== -1) {
} else if (self.sql.indexOf('PRAGMA foreign_key_list') !== -1) { result = results;
result = results; } else if ([QueryTypes.BULKUPDATE, QueryTypes.BULKDELETE].indexOf(query.options.type) !== -1) {
} else if ([QueryTypes.BULKUPDATE, QueryTypes.BULKDELETE].indexOf(self.options.type) !== -1) { result = metaData.changes;
result = metaData.changes; } else if (query.options.type === QueryTypes.UPSERT) {
} else if (self.options.type === QueryTypes.UPSERT) { result = undefined;
result = undefined; } else if (query.options.type === QueryTypes.VERSION) {
} else if (self.options.type === QueryTypes.VERSION) { result = results[0].version;
result = results[0].version; } else if (query.options.type === QueryTypes.RAW) {
} else if (self.options.type === QueryTypes.RAW) { result = [results, metaData];
result = [results, metaData]; }
resolve(result);
} }
}
resolve(result); if (method === 'exec') {
// exec does not support bind parameter
this.database[method](this.sql, afterExecute);
} else {
if (!parameters) parameters = [];
this.database[method](this.sql, parameters, afterExecute);
} }
}; }));
return null;
if (method === 'exec') { }
// exec does not support bind parameter };
self.database[method](self.sql, afterExecute);
} else {
if (!parameters) parameters = [];
self.database[method](self.sql, parameters, afterExecute);
}
}));
return null;
}
};
if ((self.getDatabaseMethod() === 'all')) {
var tableNames = [];
if (self.options && self.options.tableNames) {
tableNames = self.options.tableNames;
} else if (/FROM `(.*?)`/i.exec(self.sql)) {
tableNames.push(/FROM `(.*?)`/i.exec(self.sql)[1]);
}
// If we already have the metadata for the table, there's no need to ask for it again if ((this.getDatabaseMethod() === 'all')) {
tableNames = _.filter(tableNames, function (tableName) { let tableNames = [];
return !(tableName in columnTypes) && tableName !== 'sqlite_master'; if (this.options && this.options.tableNames) {
}); tableNames = this.options.tableNames;
} else if (/FROM `(.*?)`/i.exec(this.sql)) {
tableNames.push(/FROM `(.*?)`/i.exec(this.sql)[1]);
}
if (!tableNames.length) { // If we already have the metadata for the table, there's no need to ask for it again
return executeSql(); tableNames = _.filter(tableNames, tableName => !(tableName in columnTypes) && tableName !== 'sqlite_master');
if (!tableNames.length) {
return executeSql();
} else {
return Promise.map(tableNames, tableName =>
new Promise(resolve => {
tableName = tableName.replace(/`/g, '');
columnTypes[tableName] = {};
this.database.all('PRAGMA table_info(`' + tableName + '`)', (err, results) => {
if (!err) {
for (const result of results) {
columnTypes[tableName][result.name] = result.type;
}
}
resolve();
});
})
).then(executeSql);
}
} else { } else {
return Promise.map(tableNames, function(tableName) { return executeSql();
return new Promise(function(resolve) {
tableName = tableName.replace(/`/g, '');
columnTypes[tableName] = {};
self.database.all('PRAGMA table_info(`' + tableName + '`)', function(err, results) {
if (!err) {
results.forEach(function (result) {
columnTypes[tableName][result.name] = result.type;
});
}
resolve();
});
});
}).then(executeSql);
} }
} else { });
return executeSql();
}
}); });
}); }
return promise;
};
Query.prototype.formatError = function (err) {
var match;
switch (err.code) { formatError(err) {
case 'SQLITE_CONSTRAINT':
match = err.message.match(/FOREIGN KEY constraint failed/);
if (match !== null) {
return new sequelizeErrors.ForeignKeyConstraintError({
parent :err
});
}
var fields = []; switch (err.code) {
case 'SQLITE_CONSTRAINT': {
let match = err.message.match(/FOREIGN KEY constraint failed/);
if (match !== null) {
return new sequelizeErrors.ForeignKeyConstraintError({
parent :err
});
}
// Sqlite pre 2.2 behavior - Error: SQLITE_CONSTRAINT: columns x, y are not unique let fields = [];
match = err.message.match(/columns (.*?) are/);
if (match !== null && match.length >= 2) {
fields = match[1].split(', ');
} else {
// Sqlite post 2.2 behavior - Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: table.x, table.y // Sqlite pre 2.2 behavior - Error: SQLITE_CONSTRAINT: columns x, y are not unique
match = err.message.match(/UNIQUE constraint failed: (.*)/); match = err.message.match(/columns (.*?) are/);
if (match !== null && match.length >= 2) { if (match !== null && match.length >= 2) {
fields = match[1].split(', ').map(function (columnWithTable) { fields = match[1].split(', ');
return columnWithTable.split('.')[1]; } else {
});
// Sqlite post 2.2 behavior - Error: SQLITE_CONSTRAINT: UNIQUE constraint failed: table.x, table.y
match = err.message.match(/UNIQUE constraint failed: (.*)/);
if (match !== null && match.length >= 2) {
fields = match[1].split(', ').map(columnWithTable => columnWithTable.split('.')[1]);
}
} }
}
var errors = [] const errors = [];
, self = this let message = 'Validation error';
, message = 'Validation error';
fields.forEach(function(field) { for (const field of fields) {
errors.push(new sequelizeErrors.ValidationErrorItem( errors.push(new sequelizeErrors.ValidationErrorItem(
self.getUniqueConstraintErrorMessage(field), this.getUniqueConstraintErrorMessage(field),
'unique violation', field, self.instance && self.instance[field])); 'unique violation', field, this.instance && this.instance[field]));
}); }
if (this.model) { if (this.model) {
_.forOwn(this.model.uniqueKeys, function(constraint) { _.forOwn(this.model.uniqueKeys, constraint => {
if (_.isEqual(constraint.fields, fields) && !!constraint.msg) { if (_.isEqual(constraint.fields, fields) && !!constraint.msg) {
message = constraint.msg; message = constraint.msg;
return false; return false;
} }
}); });
}
return new sequelizeErrors.UniqueConstraintError({message, errors, parent: err, fields});
} }
case 'SQLITE_BUSY':
return new sequelizeErrors.TimeoutError(err);
return new sequelizeErrors.UniqueConstraintError({ default:
message: message, return new sequelizeErrors.DatabaseError(err);
errors: errors, }
parent: err, }
fields: fields
});
case 'SQLITE_BUSY': handleShowIndexesQuery(data) {
return new sequelizeErrors.TimeoutError(err);
// Sqlite returns indexes so the one that was defined last is returned first. Lets reverse that!
return this.sequelize.Promise.map(data.reverse(), item => {
item.fields = [];
item.primary = false;
item.unique = !!item.unique;
return this.run('PRAGMA INDEX_INFO(`' + item.name + '`)').then(columns => {
for (const column of columns) {
item.fields[column.seqno] = {
attribute: column.name,
length: undefined,
order: undefined,
};
}
default: return item;
return new sequelizeErrors.DatabaseError(err);
}
};
Query.prototype.handleShowIndexesQuery = function (data) {
var self = this;
// Sqlite returns indexes so the one that was defined last is returned first. Lets reverse that!
return this.sequelize.Promise.map(data.reverse(), function (item) {
item.fields = [];
item.primary = false;
item.unique = !!item.unique;
return self.run('PRAGMA INDEX_INFO(`' + item.name + '`)').then(function (columns) {
columns.forEach(function (column) {
item.fields[column.seqno] = {
attribute: column.name,
length: undefined,
order: undefined,
};
}); });
return item;
}); });
});
};
Query.prototype.getDatabaseMethod = function() {
if (this.isUpsertQuery()) {
return 'exec'; // Needed to run multiple queries in one
} else if (this.isInsertQuery() || this.isUpdateQuery() || this.isBulkUpdateQuery() || (this.sql.toLowerCase().indexOf('CREATE TEMPORARY TABLE'.toLowerCase()) !== -1) || this.options.type === QueryTypes.BULKDELETE) {
return 'run';
} else {
return 'all';
} }
};
getDatabaseMethod() {
if (this.isUpsertQuery()) {
return 'exec'; // Needed to run multiple queries in one
} else if (this.isInsertQuery() || this.isUpdateQuery() || this.isBulkUpdateQuery() || (this.sql.toLowerCase().indexOf('CREATE TEMPORARY TABLE'.toLowerCase()) !== -1) || this.options.type === QueryTypes.BULKDELETE) {
return 'run';
} else {
return 'all';
}
}
}
module.exports = Query; module.exports = Query;
module.exports.Query = Query;
module.exports.default = Query;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!