"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