@smallprod/models
Version:
300 lines (299 loc) • 14 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const pg_1 = __importStar(require("pg"));
const global_sql_1 = __importDefault(require("./global.sql"));
const getPool = async (config, quitOnFail = false) => {
pg_1.default.types.setTypeParser(20, Number);
return new Promise(async (resolve, reject) => {
const pool = new pg_1.Pool(config);
pool.on('error', (err) => {
if (GlobalPostgreModel.debug) {
console.error('Unexpected error on postgres database');
console.error(err);
}
if (quitOnFail) {
process.exit(-1);
}
else {
reject(err);
}
});
resolve(pool);
});
};
class GlobalPostgreModel extends global_sql_1.default {
constructor() {
super(...arguments);
this.pool = null;
this.transactionConnection = null;
this.transactionDone = null;
this.disconnect = async () => {
var _a;
await ((_a = this.pool) === null || _a === void 0 ? void 0 : _a.end());
};
this.query = async (query, params, throwErrors = false) => {
if (!this.pool) {
if (throwErrors) {
throw Error('No pool');
}
return null;
}
try {
if (!this.transactionConnection) {
return await this.pool.query(query, params);
}
return await this.transactionConnection.query(query, params);
}
catch (err) {
if (GlobalPostgreModel.debug) {
console.error(`An error occured while executing ${query} with parameters:`, params);
console.error(err);
}
if (throwErrors) {
throw err;
}
return null;
}
};
this.insert = async (tableName, attributes) => {
const columns = attributes.map((a) => `"${a.column}"`).join(', ');
const params = attributes.map((a, index) => `$${index + 1}`).join(', ');
const query = `INSERT INTO "${tableName}" (${columns}) VALUES (${params}) RETURNING *`;
const res = await this.query(query, attributes.map((a) => a.value));
return res === null || res === void 0 ? void 0 : res.rows[0].id;
};
this.select = async (tableName, distinct = false, attributes = [], wheres = [], sorts = [], tableAlias = '', limit = -1, offset = 0, joins = [], groups = [], havings = []) => {
var _a;
const query = `SELECT${distinct ? ' DISTINCT' : ''}${this.computeAttributes(attributes, '"')} FROM "${tableName}" ${tableAlias ? `${tableAlias}` : ''} ${this.computeJoins(joins, '"', '')}${this.computeWhere(wheres, '$', true, '"')}${this.computeGroupBy(groups)}${this.computeWhere(havings, '$', true, '"', 'HAVING', wheres.filter((w) => !w.keyword).length)}${this.computeSort(sorts, '"')}${limit !== -1 ? ` LIMIT ${limit} OFFSET ${offset}` : ''}`;
const havingAttr = this.getWhereAttributes(havings);
return (_a = (await this.query(query, havingAttr.concat(this.getWhereAttributes(wheres))))) === null || _a === void 0 ? void 0 : _a.rows;
};
this.update = async (tableName, attributes, wheres) => {
var _a;
const columns = attributes
.map((a, index) => `"${a.column}" = $${index + 1}`)
.join(', ');
const query = `UPDATE "${tableName}" SET ${columns} ${this.computeWhere(wheres, '$', true, '"', 'WHERE', attributes.length)}`;
return (_a = (await this.query(query, attributes.map((a) => a.value).concat(this.getWhereAttributes(wheres))))) === null || _a === void 0 ? void 0 : _a.rowCount;
};
this.delete = async (tableName, wheres) => {
var _a;
const query = `DELETE FROM "${tableName}" ${this.computeWhere(wheres, '$', true, '"')}`;
return (_a = (await this.query(query, this.getWhereAttributes(wheres)))) === null || _a === void 0 ? void 0 : _a.rowCount;
};
this.createTable = async (tableName, fields) => {
const query = `CREATE TABLE ${tableName} (${fields
.map((f) => this.formatFieldForTableManagement(f))
.join(', ')})`;
await this.query(query);
await fields.reduce(async (prevField, curField) => {
await prevField;
const constraints = this.getFieldConstraintsForTableManagement(curField, tableName);
await constraints.reduce(async (prevConst, curConst) => {
await prevConst;
await this.query(curConst);
}, Promise.resolve());
}, Promise.resolve());
};
this.removeTable = async (tableName) => {
const query = `DROP TABLE ${tableName}`;
return await this.query(query);
};
this.alterTable = async (tableName, fieldsToAdd, fieldsToRemove) => {
await fieldsToRemove.reduce(async (prev, cur) => {
await prev;
const query = `ALTER TABLE ${tableName} DROP COLUMN ${cur}`;
await this.query(query);
}, Promise.resolve());
await fieldsToAdd.reduce(async (prev, cur) => {
await prev;
const query = `ALTER TABLE ${tableName} ADD ${this.formatFieldForTableManagement(cur)}`;
await this.query(query);
const constraints = this.getFieldConstraintsForTableManagement(cur, tableName);
await constraints.reduce(async (prevConst, curConst) => {
await prevConst;
await this.query(curConst);
}, Promise.resolve());
}, Promise.resolve());
};
this.startTransaction = async () => {
return new Promise((resolve, reject) => {
if (this.transactionConnection) {
if (GlobalPostgreModel.debug)
console.error('Already in a transaction');
return resolve(false);
}
if (!this.pool) {
if (GlobalPostgreModel.debug)
console.error('No pool');
return resolve(false);
}
this.pool.connect((err, client, done) => {
if (err) {
if (GlobalPostgreModel.debug)
console.error(err);
return resolve(false);
}
client.query('BEGIN', (error) => {
if (error) {
done();
return resolve(false);
}
this.transactionDone = done;
this.transactionConnection = client;
resolve(true);
});
});
});
};
this.commit = async () => {
return new Promise((resolve, reject) => {
if (!this.transactionConnection) {
if (GlobalPostgreModel.debug)
console.error('Not in a transaction');
return resolve();
}
this.transactionConnection.query('COMMIT', (err) => {
var _a;
if (err) {
if (GlobalPostgreModel.debug)
console.error(err);
(_a = this.transactionConnection) === null || _a === void 0 ? void 0 : _a.query('ROLLBACK', (e) => {
this.transactionConnection = null;
if (e) {
if (GlobalPostgreModel.debug)
console.error(e);
}
resolve();
});
}
if (this.transactionDone) {
this.transactionDone();
this.transactionDone = null;
}
this.transactionConnection = null;
resolve();
});
});
};
this.rollback = async () => {
return new Promise((resolve) => {
if (!this.transactionConnection) {
if (GlobalPostgreModel.debug)
console.error('Not in a transaction');
return resolve();
}
this.transactionConnection.query('ROLLBACK', (err) => {
this.transactionConnection = null;
if (err) {
if (GlobalPostgreModel.debug)
console.error(err);
}
if (this.transactionDone) {
this.transactionDone();
this.transactionDone = null;
}
resolve();
});
});
};
this.checkMigrationTable = async () => {
const res = await this.query('SELECT table_name FROM information_schema.tables WHERE table_name = $1', ['migrations']);
return res && res.rowCount ? true : false;
};
this.setPool = async (config) => {
const p = await getPool(config);
if (p) {
this.pool = p;
}
};
this.formatFieldForTableManagement = (field) => {
const fInfos = field.getAll();
if (fInfos.ai) {
return `${fInfos.name} ${fInfos.type === 'bigint'
? 'BIGSERIAL'
: fInfos.type === 'smallint'
? 'SMALLSERIAL'
: 'SERIAL'}${fInfos.len !== 0 ? `(${fInfos.len})` : ''}${fInfos.null ? '' : ' NOT NULL'}`;
}
return `${fInfos.name} ${this.getCorrespondingType(fInfos.type)}${fInfos.len !== 0 ? `(${fInfos.len})` : ''}${fInfos.null ? '' : ' NOT NULL'}`;
};
this.getFieldConstraintsForTableManagement = (field, tableName) => {
const fieldInfos = field.getAll();
const constraints = [];
if (fieldInfos.mustBeUnique || fieldInfos.primaryKey) {
constraints.push(`ALTER TABLE ${tableName} ADD CONSTRAINT unique_${tableName.toLowerCase()}_${fieldInfos.name.toLowerCase()} UNIQUE (${fieldInfos.name})`);
}
if (fieldInfos.checkValue) {
constraints.push(`ALTER TABLE ${tableName} ADD CONSTRAINT check_${tableName.toLowerCase()}_${fieldInfos.name.toLowerCase()} CHECK(${fieldInfos.name}${fieldInfos.checkValue})`);
}
if (fieldInfos.defaultValue) {
constraints.push(`ALTER TABLE ${tableName} ALTER ${fieldInfos.name} SET DEFAULT ${fieldInfos.defaultValue.isSystem
? `${fieldInfos.defaultValue.value}`
: `'${fieldInfos.defaultValue.value}'`};`);
}
if (fieldInfos.primaryKey) {
constraints.push(`ALTER TABLE ${tableName} ADD CONSTRAINT pk_${tableName.toLowerCase()}_${fieldInfos.name.toLowerCase()} PRIMARY KEY (${fieldInfos.name})`);
}
if (fieldInfos.foreignKey) {
constraints.push(`ALTER TABLE ${tableName} ADD CONSTRAINT fk_${tableName.toLowerCase()}_${fieldInfos.name.toLowerCase()} FOREIGN KEY (${fieldInfos.name}) REFERENCES ${fieldInfos.foreignKey.table}(${fieldInfos.foreignKey.column})`);
}
return constraints;
};
this.getCorrespondingType = (type) => {
switch (type) {
case 'binary':
return 'bytea';
case 'blob':
return 'bytea';
case 'datetime':
return 'timestamp';
case 'float':
return 'double';
case 'longblob':
return 'bytea';
case 'longtext':
return 'text';
case 'mediumblob':
return 'bytea';
case 'mediumint':
return 'int';
case 'tinyint':
return 'smallint';
case 'mediumtext':
return 'text';
case 'tinyblob':
return 'bytea';
case 'tinytext':
return 'text';
case 'varbinary':
return 'bytea';
default:
return type;
}
};
}
}
exports.default = GlobalPostgreModel;