@cheetah.js/orm
Version:
A simple ORM for Cheetah.js
313 lines • 14 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.PgDriver = void 0;
const pg_1 = require("pg");
class PgDriver {
constructor(options) {
if (options.connectionString) {
this.connectionString = options.connectionString;
}
else {
const { host, port, username, password, database } = options;
this.connectionString = `postgres://${username}:${password}@${host}:${port}/${database}`;
}
this.client = new pg_1.Client({
connectionString: this.connectionString,
});
}
getCreateTableInstruction(schema, tableName, creates) {
let beforeSql = ``;
const st = `CREATE TABLE "${schema}"."${tableName}" (${creates.map(colDiff => {
const isAutoIncrement = colDiff.colChanges?.autoIncrement;
let sql = ``;
if (colDiff.colChanges?.enumItems) {
beforeSql += `CREATE TYPE "${schema}_${tableName}_${colDiff.colName}_enum" AS ENUM (${colDiff.colChanges.enumItems.map(item => `'${item}'`).join(', ')});`;
sql += `"${colDiff.colName}" "${schema}_${tableName}_${colDiff.colName}_enum"`;
}
else {
sql += `"${colDiff.colName}" ${isAutoIncrement ? 'SERIAL' : colDiff.colType + (colDiff.colLength ? `(${colDiff.colLength})` : '')}`;
}
if (!colDiff.colChanges?.nullable) {
sql += ' NOT NULL';
}
if (colDiff.colChanges?.primary) {
sql += ' PRIMARY KEY';
}
if (colDiff.colChanges?.unique) {
sql += ' UNIQUE';
}
if (colDiff.colChanges?.default) {
sql += ` DEFAULT ${colDiff.colChanges.default}`;
}
return sql;
})});`;
return beforeSql + st;
}
getAlterTableFkInstruction(schema, tableName, colDiff, fk) {
return `ALTER TABLE "${schema}"."${tableName}" ADD CONSTRAINT "${tableName}_${colDiff.colName}_fk" FOREIGN KEY ("${colDiff.colName}") REFERENCES "${fk.referencedTableName}" ("${fk.referencedColumnName}");`;
}
getCreateIndex(index, schema, tableName) {
return `CREATE INDEX "${index.name}" ON "${schema}"."${tableName}" (${index.properties.map(prop => `"${prop}"`).join(', ')});`;
}
getAddColumn(schema, tableName, colName, colDiff, colDiffInstructions) {
let beforeSql = ``;
let sql = ``;
if (colDiff.colChanges?.enumItems) {
beforeSql += `CREATE TYPE "${schema}_${tableName}_${colDiff.colName}_enum" AS ENUM (${colDiff.colChanges.enumItems.map(item => `'${item}'`).join(', ')});`;
colDiffInstructions.push(beforeSql);
sql += `ALTER TABLE "${schema}"."${tableName}" ADD COLUMN "${colDiff.colName}" "${schema}_${tableName}_${colDiff.colName}_enum"`;
}
else {
sql += `ALTER TABLE "${schema}"."${tableName}" ADD COLUMN "${colName}" ${colDiff.colType}${(colDiff.colLength ? `(${colDiff.colLength})` : '')}`;
}
if (!colDiff.colChanges?.nullable) {
sql += ' NOT NULL';
}
if (colDiff.colChanges?.primary) {
sql += ' PRIMARY KEY';
}
if (colDiff.colChanges?.unique) {
sql += ' UNIQUE';
}
if (colDiff.colChanges?.default) {
sql += ` DEFAULT ${colDiff.colChanges.default}`;
}
colDiffInstructions.push(sql.concat(';'));
if (colDiff.colChanges?.foreignKeys) {
colDiff.colChanges.foreignKeys.forEach(fk => {
colDiffInstructions.push(`ALTER TABLE "${schema}"."${tableName}" ADD CONSTRAINT "${tableName}_${colName}_fk" FOREIGN KEY ("${colName}") REFERENCES "${fk.referencedTableName}" ("${fk.referencedColumnName}");`);
});
}
}
getDropColumn(colDiffInstructions, schema, tableName, colName) {
colDiffInstructions.push(`ALTER TABLE "${schema}"."${tableName}" DROP COLUMN IF EXISTS "${colName}";`);
}
getDropIndex(index, schema, tableName) {
return `${this.getDropConstraint(index, schema, tableName)}`;
}
getAlterTableType(schema, tableName, colName, colDiff) {
return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" TYPE ${colDiff.colType}${(colDiff.colLength ? `(${colDiff.colLength})` : '')};`;
}
getAlterTableDefaultInstruction(schema, tableName, colName, colDiff) {
return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" SET DEFAULT ${colDiff.colChanges.default};`;
}
getAlterTablePrimaryKeyInstruction(schema, tableName, colName, colDiff) {
return `ALTER TABLE "${schema}"."${tableName}" ADD PRIMARY KEY ("${colName}");`;
}
getDropConstraint(param, schema, tableName) {
return `ALTER TABLE "${schema}"."${tableName}" DROP CONSTRAINT "${param.name}";`;
}
getAddUniqueConstraint(schema, tableName, colName) {
return `ALTER TABLE "${schema}"."${tableName}" ADD UNIQUE ("${colName}");`;
}
getAlterTableDropNullInstruction(schema, tableName, colName, colDiff) {
return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" DROP NOT NULL;`;
}
getAlterTableDropNotNullInstruction(schema, tableName, colName, colDiff) {
return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" SET NOT NULL;`;
}
getAlterTableEnumInstruction(schema, tableName, colName, colDiff) {
return `ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" TYPE varchar(255);DROP TYPE IF EXISTS "${schema}_${tableName}_${colName}_enum";CREATE TYPE "${schema}_${tableName}_${colName}_enum" AS ENUM (${colDiff.colChanges.enumItems.map(item => `'${item}'`).join(', ')});ALTER TABLE "${schema}"."${tableName}" ALTER COLUMN "${colName}" TYPE "${schema}_${tableName}_${colName}_enum" USING "${colName}"::text::"${schema}_${tableName}_${colName}_enum"`;
}
getDropTypeEnumInstruction(param, schema, tableName) {
return `DROP TYPE IF EXISTS "${param.name}";`;
}
async startTransaction() {
await this.client.query('BEGIN;');
}
async commitTransaction() {
await this.client.query('COMMIT;');
}
async rollbackTransaction() {
await this.client.query('ROLLBACK;');
}
async executeStatement(statement) {
let { statement: statementType, table, columns, where, limit, alias } = statement;
let sql = '';
switch (statementType) {
case 'select':
sql = `SELECT ${columns ? columns.join(', ') : '*'} FROM ${table} ${alias}`;
break;
case 'insert': // TODO: Tratar corretamente os valores string, number
const fields = Object.keys(statement.values).map(v => `"${v}"`).join(', ');
const values = Object.values(statement.values).map(value => this.toDatabaseValue(value)).join(', ');
sql = `INSERT INTO ${table} (${fields}) VALUES (${values}) RETURNING ${statement.columns.join(', ').replaceAll(`${alias}.`, '')}`;
break;
case 'update':
sql = `UPDATE ${table} as ${alias} SET ${Object.entries(statement.values).map(([key, value]) => `${key} = ${this.toDatabaseValue(value)}`).join(', ')}`;
break;
case 'delete':
break;
}
if (statement.join) {
statement.join.forEach(join => {
sql += ` ${join.type} JOIN ${join.joinSchema}.${join.joinTable} ${join.joinAlias} ON ${join.on}`;
});
}
if (statementType !== 'insert') {
if (where) {
sql += ` WHERE ${where}`;
}
if (statement.orderBy) {
sql += ` ORDER BY ${statement.orderBy.join(', ')}`;
}
if (statement.offset) {
sql += ` OFFSET ${statement.offset}`;
}
if (limit) {
sql += ` LIMIT ${statement.limit}`;
}
}
const startTime = Date.now();
return {
query: await this.client.query(sql),
startTime,
sql
};
}
connect() {
return this.client.connect();
}
disconnect() {
return this.client.end();
}
executeSql(s) {
return this.client.query(s);
}
async snapshot(tableName, options) {
const schema = (options && options.schema) || 'public';
const sql = `SELECT * FROM information_schema.columns WHERE table_name = '${tableName}' AND table_schema = '${schema}'`;
const result = await this.client.query(sql);
if (!result.rows || result.rows.length === 0) {
return;
}
const indexes = await this.index(tableName, options) || [];
const constraints = await this.constraints(tableName, options) || [];
let enums = await this.getEnums(tableName, schema);
// @ts-ignore
enums = enums.reduce((acc, curr) => {
if (!acc[curr.type]) {
acc[curr.type] = [];
}
acc[curr.type].push(curr.label);
return acc;
}, {});
return {
tableName,
schema,
indexes,
columns: result.rows.map(row => {
// console.log(this.getForeignKeys(constraints, row), row.column_name)
return {
default: row.column_default,
length: row.character_maximum_length || row.numeric_precision || row.datetime_precision,
name: row.column_name,
nullable: row.is_nullable === 'YES',
primary: constraints.some(c => c.type === 'PRIMARY KEY' && c.consDef.includes(row.column_name)),
unique: constraints.some(c => (c.type === 'UNIQUE' || c.type === 'PRIMARY KEY') && c.consDef.includes(row.column_name)),
type: row.data_type,
foreignKeys: this.getForeignKeys(constraints, row),
isEnum: row.data_type === 'USER-DEFINED',
enumItems: row.data_type === 'USER-DEFINED' ? enums[`${schema}_${tableName}_${row.column_name}_enum`] : undefined,
precision: row.numeric_precision,
scale: row.numeric_scale,
isDecimal: row.data_type === 'numeric',
};
})
};
}
getForeignKeys(constraints, row) {
const name = row.column_name;
return constraints.filter(c => c.type === 'FOREIGN KEY' && c.consDef.match(new RegExp(`FOREIGN KEY \\("${row.column_name}"\\)`))).map(c => {
const filter = c.consDef.match(/REFERENCES\s+"([^"]+)"\s*\(([^)]+)\)/);
if (!filter)
throw new Error('Invalid constraint definition');
return {
referencedColumnName: filter[2].split(',')[0].trim(),
referencedTableName: filter[1],
};
});
}
async index(tableName, options) {
const schema = (options && options.schema) || 'public';
let result = await this.client.query(`SELECT indexname AS index_name, indexdef AS column_name, tablename AS table_name
FROM pg_indexes
WHERE tablename = '${tableName}' AND schemaname = '${schema}'`);
//@ts-ignore
result.rows = result.rows.filter(row => row.index_name.includes('_pkey') || !row.column_name.includes('UNIQUE INDEX'));
return result.rows.map(row => {
return {
table: tableName,
indexName: row.index_name,
columnName: row.column_name
};
});
}
async constraints(tableName, options) {
const schema = (options && options.schema) || 'public';
const result = await this.client.query(`SELECT
conname AS index_name,
pg_get_constraintdef(pg_constraint.oid) as consdef,
CASE contype
WHEN 'c' THEN 'CHECK'
WHEN 'f' THEN 'FOREIGN KEY'
WHEN 'p' THEN 'PRIMARY KEY'
WHEN 'u' THEN 'UNIQUE'
WHEN 't' THEN 'TRIGGER'
WHEN 'x' THEN 'EXCLUSION'
ELSE 'UNKNOWN'
END AS constraint_type
FROM pg_constraint
where conrelid = (
SELECT oid
FROM pg_class
WHERE relname = '${tableName}'
AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = '${schema}')
)
AND conkey @> ARRAY(
SELECT attnum
FROM pg_attribute
WHERE attrelid = conrelid
AND attname = '${schema}'
)`);
return result.rows.map(row => {
return {
indexName: row.index_name,
type: row.constraint_type,
consDef: row.consdef
};
});
}
toDatabaseValue(value) {
if (value instanceof Date) {
return `'${value.toISOString()}'`;
}
switch (typeof value) {
case 'string':
return `'${value}'`;
case 'number':
return value;
case 'boolean':
return value;
case 'object':
return `'${JSON.stringify(value)}'`;
default:
return `'${value}'`;
}
}
async getEnums(tableName, schema) {
const result = await this.client.query(`SELECT e.enumlabel as label, t.typname as type
FROM pg_type t
JOIN pg_enum e ON t.oid = e.enumtypid
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE n.nspname = '${schema}'`);
return result.rows.map(row => {
return {
label: row.label,
type: row.type
};
});
}
}
exports.PgDriver = PgDriver;
//# sourceMappingURL=pg-driver.js.map
;