@mikro-orm/knex
Version:
TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.
201 lines (200 loc) • 8.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseSqliteSchemaHelper = void 0;
const SchemaHelper_1 = require("../../schema/SchemaHelper");
class BaseSqliteSchemaHelper extends SchemaHelper_1.SchemaHelper {
disableForeignKeysSQL() {
return 'pragma foreign_keys = off;';
}
enableForeignKeysSQL() {
return 'pragma foreign_keys = on;';
}
supportsSchemaConstraints() {
return false;
}
getListTablesSQL() {
return `select name as table_name from sqlite_master where type = 'table' and name != 'sqlite_sequence' and name != 'geometry_columns' and name != 'spatial_ref_sys' `
+ `union all select name as table_name from sqlite_temp_master where type = 'table' order by name`;
}
getDropDatabaseSQL(name) {
if (name === ':memory:') {
return '';
}
/* istanbul ignore next */
return `drop database if exists ${this.platform.quoteIdentifier(name)}`;
}
getDropColumnsSQL(tableName, columns, schemaName) {
/* istanbul ignore next */
const name = this.platform.quoteIdentifier((schemaName && schemaName !== this.platform.getDefaultSchemaName() ? schemaName + '.' : '') + tableName);
return columns.map(column => {
return `alter table ${name} drop column ${this.platform.quoteIdentifier(column.name)}`;
}).join(';\n');
}
parseTableDefinition(sql, cols) {
const columns = {};
// extract all columns definitions
let columnsDef = sql.replaceAll('\n', '').match(new RegExp(`create table [\`"']?.*?[\`"']? \\((.*)\\)`, 'i'))?.[1];
/* istanbul ignore else */
if (columnsDef) {
for (let i = cols.length - 1; i >= 0; i--) {
const col = cols[i];
const re = ` *, *[\`"']?${col.name}[\`"']? (.*)`;
const columnDef = columnsDef.match(new RegExp(re, 'i'));
/* istanbul ignore else */
if (columnDef) {
columns[col.name] = { name: col.name, definition: columnDef[1] };
columnsDef = columnsDef.substring(0, columnDef.index);
}
}
}
return columns;
}
async getColumns(connection, tableName, schemaName) {
const columns = await connection.execute(`pragma table_xinfo('${tableName}')`);
const sql = `select sql from sqlite_master where type = ? and name = ?`;
const tableDefinition = await connection.execute(sql, ['table', tableName], 'get');
const composite = columns.reduce((count, col) => count + (col.pk ? 1 : 0), 0) > 1;
// there can be only one, so naive check like this should be enough
const hasAutoincrement = tableDefinition.sql.toLowerCase().includes('autoincrement');
const columnDefinitions = this.parseTableDefinition(tableDefinition.sql, columns);
return columns.map(col => {
const mappedType = connection.getPlatform().getMappedType(col.type);
let generated;
if (col.hidden > 1) {
/* istanbul ignore next */
const storage = col.hidden === 2 ? 'virtual' : 'stored';
const re = `(generated always)? as \\((.*)\\)( ${storage})?$`;
const match = columnDefinitions[col.name].definition.match(re);
if (match) {
generated = `${match[2]} ${storage}`;
}
}
return {
name: col.name,
type: col.type,
default: col.dflt_value,
nullable: !col.notnull,
primary: !!col.pk,
mappedType,
unsigned: false,
autoincrement: !composite && col.pk && this.platform.isNumericColumn(mappedType) && hasAutoincrement,
generated,
};
});
}
async getEnumDefinitions(connection, checks, tableName, schemaName) {
const sql = `select sql from sqlite_master where type = ? and name = ?`;
const tableDefinition = await connection.execute(sql, ['table', tableName], 'get');
const checkConstraints = [...tableDefinition.sql.match(/[`["'][^`\]"']+[`\]"'] text check \(.*?\)/gi) ?? []];
return checkConstraints.reduce((o, item) => {
// check constraints are defined as (note that last closing paren is missing):
// `type` text check (`type` in ('local', 'global')
const match = item.match(/[`["']([^`\]"']+)[`\]"'] text check \(.* \((.*)\)/i);
/* istanbul ignore else */
if (match) {
o[match[1]] = match[2].split(',').map((item) => item.trim().match(/^\(?'(.*)'/)[1]);
}
return o;
}, {});
}
async getPrimaryKeys(connection, indexes, tableName, schemaName) {
const sql = `pragma table_info(\`${tableName}\`)`;
const cols = await connection.execute(sql);
return cols.filter(col => !!col.pk).map(col => col.name);
}
async getIndexes(connection, tableName, schemaName) {
const sql = `pragma table_info(\`${tableName}\`)`;
const cols = await connection.execute(sql);
const indexes = await connection.execute(`pragma index_list(\`${tableName}\`)`);
const ret = [];
for (const col of cols.filter(c => c.pk)) {
ret.push({
columnNames: [col.name],
keyName: 'primary',
constraint: true,
unique: true,
primary: true,
});
}
for (const index of indexes.filter(index => !this.isImplicitIndex(index.name))) {
const res = await connection.execute(`pragma index_info(\`${index.name}\`)`);
ret.push(...res.map(row => ({
columnNames: [row.name],
keyName: index.name,
unique: !!index.unique,
constraint: !!index.unique,
primary: false,
})));
}
return this.mapIndexes(ret);
}
async getChecks(connection, tableName, schemaName) {
// Not supported at the moment.
return [];
}
getForeignKeysSQL(tableName) {
return `pragma foreign_key_list(\`${tableName}\`)`;
}
mapForeignKeys(fks, tableName) {
return fks.reduce((ret, fk) => {
ret[fk.from] = {
constraintName: this.platform.getIndexName(tableName, [fk.from], 'foreign'),
columnName: fk.from,
columnNames: [fk.from],
localTableName: tableName,
referencedTableName: fk.table,
referencedColumnName: fk.to,
referencedColumnNames: [fk.to],
updateRule: fk.on_update.toLowerCase(),
deleteRule: fk.on_delete.toLowerCase(),
};
return ret;
}, {});
}
getManagementDbName() {
return '';
}
getCreateDatabaseSQL(name) {
return '';
}
async databaseExists(connection, name) {
const tables = await connection.execute(this.getListTablesSQL());
return tables.length > 0;
}
/**
* Implicit indexes will be ignored when diffing
*/
isImplicitIndex(name) {
// Ignore indexes with reserved names, e.g. autoindexes
return name.startsWith('sqlite_');
}
async getAlterTable(changedTable, wrap) {
const tempName = `${(changedTable.toTable.name)}__temp_alter`;
const quotedName = this.platform.quoteIdentifier(changedTable.toTable.name);
const quotedTempName = this.platform.quoteIdentifier(tempName);
const createSql = await this.dump(this.createTable(changedTable.toTable), '');
const [first, ...rest] = createSql.split('\n');
const sql = [
'pragma foreign_keys = off;',
first.replace(`create table ${quotedName}`, `create table ${quotedTempName}`),
];
const columns = [];
for (const column of changedTable.toTable.getColumns()) {
const fromColumn = changedTable.fromTable.getColumn(column.name);
/* istanbul ignore else */
if (fromColumn) {
columns.push(this.platform.quoteIdentifier(column.name));
}
else {
columns.push(`null as ${this.platform.quoteIdentifier(column.name)}`);
}
}
sql.push(`insert into ${quotedTempName} select ${columns.join(', ')} from ${quotedName};`);
sql.push(`drop table ${quotedName};`);
sql.push(`alter table ${quotedTempName} rename to ${quotedName};`);
sql.push(...rest);
sql.push('pragma foreign_keys = on;');
return sql.join('\n');
}
}
exports.BaseSqliteSchemaHelper = BaseSqliteSchemaHelper;