UNPKG

sqlite3orm

Version:

ORM for sqlite3 and TypeScript/JavaScript

366 lines 14.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Table = void 0; const tslib_1 = require("tslib"); const core = tslib_1.__importStar(require("../core/core")); const utils_1 = require("../utils"); const Field_1 = require("./Field"); /** * Class holding a table definition (name of the table and fields in the table) * * @export * @class Table */ class Table { name; get quotedName() { return (0, utils_1.quoteIdentifier)(this.name); } get schemaName() { return (0, utils_1.splitSchemaIdentifier)(this.name).identSchema; } /** * Flag to indicate if this table should be created with the 'WITHOUT * ROWID'-clause */ _withoutRowId; get withoutRowId() { return this._withoutRowId == undefined ? false : this._withoutRowId; } set withoutRowId(withoutRowId) { this._withoutRowId = withoutRowId; } get isWithoutRowIdDefined() { return this._withoutRowId == undefined ? false : true; } /** * Flag to indicate if AUTOINCREMENT should be enabled for a table having a * single-column INTEGER primary key * and withoutRowId is disabled */ _autoIncrement; get autoIncrement() { return this._autoIncrement == undefined ? false : this._autoIncrement; } set autoIncrement(autoIncrement) { this._autoIncrement = autoIncrement; } get isAutoIncrementDefined() { return this._autoIncrement == undefined ? false : true; } /** * The fields defined for this table */ fields = []; /** * The field mapped to the primary key; only set if using the * primary key column is alias for the rowId. */ _rowIdField; get rowIdField() { return this._rowIdField; } /** * The field mapped to the primary key; only set if using the * AUTOINCREMENT feature */ _autoIncrementField; get autoIncrementField() { return this._autoIncrementField; } // map column name to a field definition mapNameToField; // map column name to a identity field definition mapNameToIdentityField; // map constraint name to foreign key definition mapNameToFKDef; // map index name to index key definition mapNameToIDXDef; models; /** * Creates an instance of Table. * * @param name - The table name (containing the schema name if specified) */ constructor(name) { this.name = name; this.mapNameToField = new Map(); this.mapNameToIdentityField = new Map(); this.mapNameToFKDef = new Map(); this.mapNameToIDXDef = new Map(); this.fields = []; this.models = new Set(); } /** * Test if table has a column with the given column name * * @param colName - The name of the column */ hasTableField(name) { return this.mapNameToField.get(name); } /** * Get the field definition for the given column name * * @param colName - The name of the column * @returns The field definition */ getTableField(name) { const field = this.mapNameToField.get(name); if (!field) { throw new Error(`table '${this.name}': field '${name}' not registered yet`); } return field; } /** * Add a table field to this table * * @param name - The name of the column * @param isIdentity * @param [opts] * @param [propertyType] * @returns The field definition */ getOrAddTableField(name, isIdentity, opts, propertyType) { let field = this.mapNameToField.get(name); if (!field) { // create field field = new Field_1.Field(name, isIdentity, opts, propertyType); this.fields.push(field); this.mapNameToField.set(field.name, field); if (field.isIdentity) { this.mapNameToIdentityField.set(field.name, field); } } else { // merge field if (field.isIdentity !== isIdentity) { throw new Error(`conflicting identity setting: new: ${isIdentity}, old: ${field.isIdentity}`); } if (opts && opts.dbtype) { if (field.isDbTypeDefined && field.dbtype !== opts.dbtype) { throw new Error(`conflicting dbtype setting: new: '${opts.dbtype}', old: '${field.dbtype}'`); } field.dbtype = opts.dbtype; } if (opts && opts.isJson != undefined) { if (field.isIsJsonDefined && field.isJson !== opts.isJson) { throw new Error(`conflicting json setting: new: ${opts.isJson}, old: ${field.isJson}`); } field.isJson = opts.isJson; } if (opts && opts.dateInMilliSeconds != undefined) { if (field.isDateInMilliSecondsDefined && field.dateInMilliSeconds !== opts.dateInMilliSeconds) { throw new Error(`conflicting dateInMilliSeconds setting: new: ${opts.dateInMilliSeconds}, old: ${field.dateInMilliSeconds}`); } field.dateInMilliSeconds = opts.dateInMilliSeconds; } } if (field.isIdentity) { if (!this.withoutRowId && this.mapNameToIdentityField.size === 1 && field.dbTypeInfo.typeAffinity === 'INTEGER') { this._rowIdField = field; if (this.autoIncrement) { this._autoIncrementField = field; } else { this._autoIncrementField = undefined; } } else { this._autoIncrementField = undefined; this._rowIdField = undefined; } } return field; } hasFKDefinition(name) { return this.mapNameToFKDef.get(name); } getFKDefinition(name) { const constraint = this.mapNameToFKDef.get(name); if (!constraint) { throw new Error(`table '${this.name}': foreign key constraint ${name} not registered yet`); } return constraint; } addFKDefinition(fkDef) { const oldFkDef = this.mapNameToFKDef.get(fkDef.name); if (!oldFkDef) { this.mapNameToFKDef.set(fkDef.name, fkDef); } else { // check conflicts if (oldFkDef.id !== fkDef.id) { core.debugORM(`table '${this.name}': conflicting foreign key definition for '${fkDef.name}'`); core.debugORM(` old: ${oldFkDef.id}`); core.debugORM(` new: ${fkDef.id}`); throw new Error(`table '${this.name}': conflicting foreign key definition for '${fkDef.name}'`); } } return fkDef; } hasIDXDefinition(name) { // NOTE: creating a index in schema1 on a table in schema2 is not supported by Sqlite3 // so using qualifiedIndentifier is currently not required return this.mapNameToIDXDef.get((0, utils_1.qualifiySchemaIdentifier)(name, this.schemaName)); } getIDXDefinition(name) { // NOTE: creating a index in schema1 on a table in schema2 is not supported by Sqlite3 // so using qualifiedIndentifier is currently not required const idxDef = this.mapNameToIDXDef.get((0, utils_1.qualifiySchemaIdentifier)(name, this.schemaName)); if (!idxDef) { throw new Error(`table '${this.name}': index ${name} not registered yet`); } return idxDef; } addIDXDefinition(idxDef) { // NOTE: creating a index in schema1 on a table in schema2 is not supported by Sqlite3 // so using qualifiedIndentifier is currently not required const qname = (0, utils_1.qualifiySchemaIdentifier)(idxDef.name, this.schemaName); const oldIdxDef = this.mapNameToIDXDef.get(qname); if (!oldIdxDef) { this.mapNameToIDXDef.set(qname, idxDef); } else { // check conflicts if (oldIdxDef.id !== idxDef.id) { core.debugORM(`table '${this.name}': conflicting index definition for '${idxDef.name}'`); core.debugORM(` old: ${oldIdxDef.id}`); core.debugORM(` new: ${idxDef.id}`); throw new Error(`table '${this.name}': conflicting index definition '${idxDef.name}'`); } } return idxDef; } /** * Get 'CREATE TABLE'-statement using 'IF NOT EXISTS'-clause * * @returns The sql-statement */ getCreateTableStatement(force) { return this.createCreateTableStatement(force); } /** * Get 'DROP TABLE'-statement * * @returns {string} */ getDropTableStatement() { return `DROP TABLE IF EXISTS ${this.quotedName}`; } /** * Get 'ALTER TABLE...ADD COLUMN'-statement for the given column * * @param colName - The name of the column to add to the table * @returns The sql-statment */ getAlterTableAddColumnStatement(colName) { let stmt = `ALTER TABLE ${this.quotedName}`; const field = this.getTableField(colName); stmt += ` ADD COLUMN ${field.quotedName} ${field.dbtype}`; return stmt; } /** * Get 'CREATE [UNIQUE] INDEX'-statement using 'IF NOT EXISTS'-clause * * @returns The sql-statement */ getCreateIndexStatement(idxName, unique) { const idxDef = this.hasIDXDefinition(idxName); if (!idxDef) { throw new Error(`table '${this.name}': index '${idxName}' is not defined on table '${this.name}'`); } if (unique == undefined) { unique = idxDef.isUnique ? true : false; } const idxCols = idxDef.fields.map((field) => (0, utils_1.quoteSimpleIdentifier)(field.name) + (field.desc ? ' DESC' : '')); return ('CREATE ' + (unique ? 'UNIQUE ' : ' ') + `INDEX IF NOT EXISTS ${(0, utils_1.quoteIdentifier)(idxName)} ON ${(0, utils_1.quoteAndUnqualifyIdentifier)(this.name)} ` + `(` + idxCols.join(', ') + ')'); } /** * Get 'DROP TABLE'-statement * * @returns The sql-statement */ getDropIndexStatement(idxName) { const idxDef = this.hasIDXDefinition(idxName); if (!idxDef) { throw new Error(`table '${this.name}': index '${idxName}' is not defined on table '${this.name}'`); } return `DROP INDEX IF EXISTS ${(0, utils_1.quoteIdentifier)(idxName)}`; } /** * Generate SQL Statements */ createCreateTableStatement(force, addFields) { const colNamesPK = []; const colDefs = []; const quotedTableName = this.quotedName; /* istanbul ignore if */ if (!this.fields.length) { throw new Error(`table '${this.name}': does not have any fields defined`); } this.fields.forEach((field) => { const quotedFieldName = field.quotedName; let colDef = `${quotedFieldName} ${field.dbtype}`; if (field.isIdentity) { colNamesPK.push(quotedFieldName); if (this.mapNameToIdentityField.size === 1) { colDef += ' PRIMARY KEY'; if (this.autoIncrementField) { colDef += ' AUTOINCREMENT'; } } } colDefs.push(colDef); }); if (addFields) { addFields.forEach((field) => { const quotedFieldName = field.quotedName; colDefs.push(`${quotedFieldName} ${field.dbtype}`); }); } // -------------------------------------------------------------- // generate CREATE TABLE statement let stmt = 'CREATE TABLE '; if (!force) { stmt += 'IF NOT EXISTS '; } stmt += `${quotedTableName} (\n `; // add column definitions stmt += colDefs.join(',\n '); if (this.mapNameToIdentityField.size > 1) { // add multi-column primary key ćonstraint: stmt += ',\n CONSTRAINT PRIMARY_KEY PRIMARY KEY ('; stmt += colNamesPK.join(', '); stmt += ')'; } // add foreign key constraint definition: this.mapNameToFKDef.forEach((fk, fkName) => { /* istanbul ignore if */ if (!fk.fields.length || fk.fields.length !== fk.fields.length) { throw new Error(`table '${this.name}': foreign key constraint '${fkName}' definition is incomplete`); } stmt += `,\n CONSTRAINT ${(0, utils_1.quoteSimpleIdentifier)(fk.name)}\n`; stmt += ` FOREIGN KEY (`; stmt += fk.fields.map((field) => (0, utils_1.quoteSimpleIdentifier)(field.name)).join(', '); stmt += ')\n'; // if fk.foreignTableName has qualifier it must match the qualifier of this.name const { identName, identSchema } = (0, utils_1.splitSchemaIdentifier)(fk.foreignTableName); const tableSchema = this.schemaName; /* istanbul ignore next */ if (identSchema && ((identSchema === 'main' && tableSchema && tableSchema !== identSchema) || (identSchema !== 'main' && (!tableSchema || tableSchema !== identSchema)))) { throw new Error(`table '${this.name}': foreign key '${fkName}' references table in wrong schema: '${fk.foreignTableName}'`); } stmt += ` REFERENCES ${(0, utils_1.quoteSimpleIdentifier)(identName)} (`; stmt += fk.fields.map((field) => (0, utils_1.quoteSimpleIdentifier)(field.foreignColumnName)).join(', ') + ') ON DELETE CASCADE'; // TODO: hard-coded 'ON DELETE CASCADE' stmt += '\n'; }); stmt += '\n)'; if (this.withoutRowId) { stmt += ' WITHOUT ROWID'; } return stmt; } } exports.Table = Table; //# sourceMappingURL=Table.js.map