You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
15 KiB
362 lines
15 KiB
"use strict"; |
|
var __defProp = Object.defineProperty; |
|
var __defProps = Object.defineProperties; |
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors; |
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols; |
|
var __hasOwnProp = Object.prototype.hasOwnProperty; |
|
var __propIsEnum = Object.prototype.propertyIsEnumerable; |
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; |
|
var __spreadValues = (a, b) => { |
|
for (var prop in b || (b = {})) |
|
if (__hasOwnProp.call(b, prop)) |
|
__defNormalProp(a, prop, b[prop]); |
|
if (__getOwnPropSymbols) |
|
for (var prop of __getOwnPropSymbols(b)) { |
|
if (__propIsEnum.call(b, prop)) |
|
__defNormalProp(a, prop, b[prop]); |
|
} |
|
return a; |
|
}; |
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); |
|
const Utils = require("../../utils"); |
|
const Transaction = require("../../transaction"); |
|
const _ = require("lodash"); |
|
const MySqlQueryGenerator = require("../mysql/query-generator"); |
|
const AbstractQueryGenerator = require("../abstract/query-generator"); |
|
class SQLiteQueryGenerator extends MySqlQueryGenerator { |
|
createSchema() { |
|
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; |
|
} |
|
showSchemasQuery() { |
|
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; |
|
} |
|
versionQuery() { |
|
return "SELECT sqlite_version() as `version`"; |
|
} |
|
createTableQuery(tableName, attributes, options) { |
|
options = options || {}; |
|
const primaryKeys = []; |
|
const needsMultiplePrimaryKeys = Object.values(attributes).filter((definition) => definition.includes("PRIMARY KEY")).length > 1; |
|
const attrArray = []; |
|
for (const attr in attributes) { |
|
if (Object.prototype.hasOwnProperty.call(attributes, attr)) { |
|
const dataType = attributes[attr]; |
|
const containsAutoIncrement = dataType.includes("AUTOINCREMENT"); |
|
let dataTypeString = dataType; |
|
if (dataType.includes("PRIMARY KEY")) { |
|
if (dataType.includes("INT")) { |
|
dataTypeString = containsAutoIncrement ? "INTEGER PRIMARY KEY AUTOINCREMENT" : "INTEGER PRIMARY KEY"; |
|
if (dataType.includes(" REFERENCES")) { |
|
dataTypeString += dataType.substr(dataType.indexOf(" REFERENCES")); |
|
} |
|
} |
|
if (needsMultiplePrimaryKeys) { |
|
primaryKeys.push(attr); |
|
if (dataType.includes("NOT NULL")) { |
|
dataTypeString = dataType.replace(" PRIMARY KEY", ""); |
|
} else { |
|
dataTypeString = dataType.replace("PRIMARY KEY", "NOT NULL"); |
|
} |
|
} |
|
} |
|
attrArray.push(`${this.quoteIdentifier(attr)} ${dataTypeString}`); |
|
} |
|
} |
|
const table = this.quoteTable(tableName); |
|
let attrStr = attrArray.join(", "); |
|
const pkString = primaryKeys.map((pk) => this.quoteIdentifier(pk)).join(", "); |
|
if (options.uniqueKeys) { |
|
_.each(options.uniqueKeys, (columns) => { |
|
if (columns.customIndex) { |
|
attrStr += `, UNIQUE (${columns.fields.map((field) => this.quoteIdentifier(field)).join(", ")})`; |
|
} |
|
}); |
|
} |
|
if (pkString.length > 0) { |
|
attrStr += `, PRIMARY KEY (${pkString})`; |
|
} |
|
const sql = `CREATE TABLE IF NOT EXISTS ${table} (${attrStr});`; |
|
return this.replaceBooleanDefaults(sql); |
|
} |
|
booleanValue(value) { |
|
return value ? 1 : 0; |
|
} |
|
_checkValidJsonStatement(stmt) { |
|
if (typeof stmt !== "string") { |
|
return false; |
|
} |
|
const jsonFunctionRegex = /^\s*(json(?:_[a-z]+){0,2})\([^)]*\)/i; |
|
const tokenCaptureRegex = /^\s*((?:([`"'])(?:(?!\2).|\2{2})*\2)|[\w\d\s]+|[().,;+-])/i; |
|
let currentIndex = 0; |
|
let openingBrackets = 0; |
|
let closingBrackets = 0; |
|
let hasJsonFunction = false; |
|
let hasInvalidToken = false; |
|
while (currentIndex < stmt.length) { |
|
const string = stmt.substr(currentIndex); |
|
const functionMatches = jsonFunctionRegex.exec(string); |
|
if (functionMatches) { |
|
currentIndex += functionMatches[0].indexOf("("); |
|
hasJsonFunction = true; |
|
continue; |
|
} |
|
const tokenMatches = tokenCaptureRegex.exec(string); |
|
if (tokenMatches) { |
|
const capturedToken = tokenMatches[1]; |
|
if (capturedToken === "(") { |
|
openingBrackets++; |
|
} else if (capturedToken === ")") { |
|
closingBrackets++; |
|
} else if (capturedToken === ";") { |
|
hasInvalidToken = true; |
|
break; |
|
} |
|
currentIndex += tokenMatches[0].length; |
|
continue; |
|
} |
|
break; |
|
} |
|
hasInvalidToken |= openingBrackets !== closingBrackets; |
|
if (hasJsonFunction && hasInvalidToken) { |
|
throw new Error(`Invalid json statement: ${stmt}`); |
|
} |
|
return hasJsonFunction; |
|
} |
|
_toJSONValue(value) { |
|
if (value instanceof Date) { |
|
return value.toISOString(); |
|
} |
|
if (Array.isArray(value) && value[0] instanceof Date) { |
|
return value.map((val) => val.toISOString()); |
|
} |
|
return value; |
|
} |
|
handleSequelizeMethod(smth, tableName, factory, options, prepend) { |
|
if (smth instanceof Utils.Json) { |
|
return super.handleSequelizeMethod(smth, tableName, factory, options, prepend); |
|
} |
|
if (smth instanceof Utils.Cast) { |
|
if (/timestamp/i.test(smth.type)) { |
|
smth.type = "datetime"; |
|
} |
|
} |
|
return AbstractQueryGenerator.prototype.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend); |
|
} |
|
addColumnQuery(table, key, dataType) { |
|
const attributes = {}; |
|
attributes[key] = dataType; |
|
const fields = this.attributesToSQL(attributes, { context: "addColumn" }); |
|
const attribute = `${this.quoteIdentifier(key)} ${fields[key]}`; |
|
const sql = `ALTER TABLE ${this.quoteTable(table)} ADD ${attribute};`; |
|
return this.replaceBooleanDefaults(sql); |
|
} |
|
showTablesQuery() { |
|
return "SELECT name FROM `sqlite_master` WHERE type='table' and name!='sqlite_sequence';"; |
|
} |
|
updateQuery(tableName, attrValueHash, where, options, attributes) { |
|
options = options || {}; |
|
_.defaults(options, this.options); |
|
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, options.omitNull, options); |
|
const modelAttributeMap = {}; |
|
const values = []; |
|
const bind = []; |
|
const bindParam = options.bindParam || this.bindParam(bind); |
|
if (attributes) { |
|
_.each(attributes, (attribute, key) => { |
|
modelAttributeMap[key] = attribute; |
|
if (attribute.field) { |
|
modelAttributeMap[attribute.field] = attribute; |
|
} |
|
}); |
|
} |
|
for (const key in attrValueHash) { |
|
const value = attrValueHash[key]; |
|
if (value instanceof Utils.SequelizeMethod || options.bindParam === false) { |
|
values.push(`${this.quoteIdentifier(key)}=${this.escape(value, modelAttributeMap && modelAttributeMap[key] || void 0, { context: "UPDATE" })}`); |
|
} else { |
|
values.push(`${this.quoteIdentifier(key)}=${this.format(value, modelAttributeMap && modelAttributeMap[key] || void 0, { context: "UPDATE" }, bindParam)}`); |
|
} |
|
} |
|
let query; |
|
const whereOptions = __spreadProps(__spreadValues({}, options), { bindParam }); |
|
if (options.limit) { |
|
query = `UPDATE ${this.quoteTable(tableName)} SET ${values.join(",")} WHERE rowid IN (SELECT rowid FROM ${this.quoteTable(tableName)} ${this.whereQuery(where, whereOptions)} LIMIT ${this.escape(options.limit)})`; |
|
} else { |
|
query = `UPDATE ${this.quoteTable(tableName)} SET ${values.join(",")} ${this.whereQuery(where, whereOptions)}`; |
|
} |
|
return { query, bind }; |
|
} |
|
truncateTableQuery(tableName, options = {}) { |
|
return [ |
|
`DELETE FROM ${this.quoteTable(tableName)}`, |
|
options.restartIdentity ? `; DELETE FROM ${this.quoteTable("sqlite_sequence")} WHERE ${this.quoteIdentifier("name")} = ${Utils.addTicks(Utils.removeTicks(this.quoteTable(tableName), "`"), "'")};` : "" |
|
].join(""); |
|
} |
|
deleteQuery(tableName, where, options = {}, model) { |
|
_.defaults(options, this.options); |
|
let whereClause = this.getWhereConditions(where, null, model, options); |
|
if (whereClause) { |
|
whereClause = `WHERE ${whereClause}`; |
|
} |
|
if (options.limit) { |
|
whereClause = `WHERE rowid IN (SELECT rowid FROM ${this.quoteTable(tableName)} ${whereClause} LIMIT ${this.escape(options.limit)})`; |
|
} |
|
return `DELETE FROM ${this.quoteTable(tableName)} ${whereClause}`; |
|
} |
|
attributesToSQL(attributes) { |
|
const result = {}; |
|
for (const name in attributes) { |
|
const dataType = attributes[name]; |
|
const fieldName = dataType.field || name; |
|
if (_.isObject(dataType)) { |
|
let sql = dataType.type.toString(); |
|
if (Object.prototype.hasOwnProperty.call(dataType, "allowNull") && !dataType.allowNull) { |
|
sql += " NOT NULL"; |
|
} |
|
if (Utils.defaultValueSchemable(dataType.defaultValue)) { |
|
sql += ` DEFAULT ${this.escape(dataType.defaultValue, dataType)}`; |
|
} |
|
if (dataType.unique === true) { |
|
sql += " UNIQUE"; |
|
} |
|
if (dataType.primaryKey) { |
|
sql += " PRIMARY KEY"; |
|
if (dataType.autoIncrement) { |
|
sql += " AUTOINCREMENT"; |
|
} |
|
} |
|
if (dataType.references) { |
|
const referencesTable = this.quoteTable(dataType.references.model); |
|
let referencesKey; |
|
if (dataType.references.key) { |
|
referencesKey = this.quoteIdentifier(dataType.references.key); |
|
} else { |
|
referencesKey = this.quoteIdentifier("id"); |
|
} |
|
sql += ` REFERENCES ${referencesTable} (${referencesKey})`; |
|
if (dataType.onDelete) { |
|
sql += ` ON DELETE ${dataType.onDelete.toUpperCase()}`; |
|
} |
|
if (dataType.onUpdate) { |
|
sql += ` ON UPDATE ${dataType.onUpdate.toUpperCase()}`; |
|
} |
|
} |
|
result[fieldName] = sql; |
|
} else { |
|
result[fieldName] = dataType; |
|
} |
|
} |
|
return result; |
|
} |
|
showIndexesQuery(tableName) { |
|
return `PRAGMA INDEX_LIST(${this.quoteTable(tableName)})`; |
|
} |
|
showConstraintsQuery(tableName, constraintName) { |
|
let sql = `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}'`; |
|
if (constraintName) { |
|
sql += ` AND sql LIKE '%${constraintName}%'`; |
|
} |
|
return `${sql};`; |
|
} |
|
removeIndexQuery(tableName, indexNameOrAttributes) { |
|
let indexName = indexNameOrAttributes; |
|
if (typeof indexName !== "string") { |
|
indexName = Utils.underscore(`${tableName}_${indexNameOrAttributes.join("_")}`); |
|
} |
|
return `DROP INDEX IF EXISTS ${this.quoteIdentifier(indexName)}`; |
|
} |
|
describeTableQuery(tableName, schema, schemaDelimiter) { |
|
const table = { |
|
_schema: schema, |
|
_schemaDelimiter: schemaDelimiter, |
|
tableName |
|
}; |
|
return `PRAGMA TABLE_INFO(${this.quoteTable(this.addSchema(table))});`; |
|
} |
|
describeCreateTableQuery(tableName) { |
|
return `SELECT sql FROM sqlite_master WHERE tbl_name='${tableName}';`; |
|
} |
|
removeColumnQuery(tableName, attributes) { |
|
attributes = this.attributesToSQL(attributes); |
|
let backupTableName; |
|
if (typeof tableName === "object") { |
|
backupTableName = { |
|
tableName: `${tableName.tableName}_backup`, |
|
schema: tableName.schema |
|
}; |
|
} else { |
|
backupTableName = `${tableName}_backup`; |
|
} |
|
const quotedTableName = this.quoteTable(tableName); |
|
const quotedBackupTableName = this.quoteTable(backupTableName); |
|
const attributeNames = Object.keys(attributes).map((attr) => this.quoteIdentifier(attr)).join(", "); |
|
return `${this.createTableQuery(backupTableName, attributes)}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};DROP TABLE ${quotedTableName};${this.createTableQuery(tableName, attributes)}INSERT INTO ${quotedTableName} SELECT ${attributeNames} FROM ${quotedBackupTableName};DROP TABLE ${quotedBackupTableName};`; |
|
} |
|
_alterConstraintQuery(tableName, attributes, createTableSql) { |
|
let backupTableName; |
|
attributes = this.attributesToSQL(attributes); |
|
if (typeof tableName === "object") { |
|
backupTableName = { |
|
tableName: `${tableName.tableName}_backup`, |
|
schema: tableName.schema |
|
}; |
|
} else { |
|
backupTableName = `${tableName}_backup`; |
|
} |
|
const quotedTableName = this.quoteTable(tableName); |
|
const quotedBackupTableName = this.quoteTable(backupTableName); |
|
const attributeNames = Object.keys(attributes).map((attr) => this.quoteIdentifier(attr)).join(", "); |
|
return `${createTableSql.replace(`CREATE TABLE ${quotedTableName}`, `CREATE TABLE ${quotedBackupTableName}`).replace(`CREATE TABLE ${quotedTableName.replace(/`/g, '"')}`, `CREATE TABLE ${quotedBackupTableName}`)}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNames} FROM ${quotedTableName};DROP TABLE ${quotedTableName};ALTER TABLE ${quotedBackupTableName} RENAME TO ${quotedTableName};`; |
|
} |
|
renameColumnQuery(tableName, attrNameBefore, attrNameAfter, attributes) { |
|
let backupTableName; |
|
attributes = this.attributesToSQL(attributes); |
|
if (typeof tableName === "object") { |
|
backupTableName = { |
|
tableName: `${tableName.tableName}_backup`, |
|
schema: tableName.schema |
|
}; |
|
} else { |
|
backupTableName = `${tableName}_backup`; |
|
} |
|
const quotedTableName = this.quoteTable(tableName); |
|
const quotedBackupTableName = this.quoteTable(backupTableName); |
|
const attributeNamesImport = Object.keys(attributes).map((attr) => attrNameAfter === attr ? `${this.quoteIdentifier(attrNameBefore)} AS ${this.quoteIdentifier(attr)}` : this.quoteIdentifier(attr)).join(", "); |
|
const attributeNamesExport = Object.keys(attributes).map((attr) => this.quoteIdentifier(attr)).join(", "); |
|
return `${this.createTableQuery(backupTableName, attributes)}INSERT INTO ${quotedBackupTableName} SELECT ${attributeNamesImport} FROM ${quotedTableName};DROP TABLE ${quotedTableName};${this.createTableQuery(tableName, attributes)}INSERT INTO ${quotedTableName} SELECT ${attributeNamesExport} FROM ${quotedBackupTableName};DROP TABLE ${quotedBackupTableName};`; |
|
} |
|
startTransactionQuery(transaction) { |
|
if (transaction.parent) { |
|
return `SAVEPOINT ${this.quoteIdentifier(transaction.name)};`; |
|
} |
|
return `BEGIN ${transaction.options.type} TRANSACTION;`; |
|
} |
|
setIsolationLevelQuery(value) { |
|
switch (value) { |
|
case Transaction.ISOLATION_LEVELS.REPEATABLE_READ: |
|
return "-- SQLite is not able to choose the isolation level REPEATABLE READ."; |
|
case Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED: |
|
return "PRAGMA read_uncommitted = ON;"; |
|
case Transaction.ISOLATION_LEVELS.READ_COMMITTED: |
|
return "PRAGMA read_uncommitted = OFF;"; |
|
case Transaction.ISOLATION_LEVELS.SERIALIZABLE: |
|
return "-- SQLite's default isolation level is SERIALIZABLE. Nothing to do."; |
|
default: |
|
throw new Error(`Unknown isolation level: ${value}`); |
|
} |
|
} |
|
replaceBooleanDefaults(sql) { |
|
return sql.replace(/DEFAULT '?false'?/g, "DEFAULT 0").replace(/DEFAULT '?true'?/g, "DEFAULT 1"); |
|
} |
|
getForeignKeysQuery(tableName) { |
|
return `PRAGMA foreign_key_list(${this.quoteTable(this.addSchema(tableName))})`; |
|
} |
|
tableExistsQuery(tableName) { |
|
return `SELECT name FROM sqlite_master WHERE type='table' AND name=${this.escape(this.addSchema(tableName))};`; |
|
} |
|
quoteIdentifier(identifier, force) { |
|
return Utils.addTicks(Utils.removeTicks(identifier, "`"), "`"); |
|
} |
|
} |
|
module.exports = SQLiteQueryGenerator; |
|
//# sourceMappingURL=query-generator.js.map
|
|
|