sqlite3orm
Version:
ORM for sqlite3 and TypeScript/JavaScript
366 lines • 14.1 kB
JavaScript
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
;