UNPKG

ts-sql-query

Version:

Type-safe SQL query builder like QueryDSL or JOOQ in Java or Linq in .Net for TypeScript with MariaDB, MySql, Oracle, PostgreSql, Sqlite and SqlServer support.

412 lines (411 loc) 20.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostgreSqlSqlBuilder = void 0; const TypeAdapter_1 = require("../TypeAdapter"); const AbstractSqlBuilder_1 = require("./AbstractSqlBuilder"); const Column_1 = require("../utils/Column"); const values_1 = require("../expressions/values"); class PostgreSqlSqlBuilder extends AbstractSqlBuilder_1.AbstractSqlBuilder { constructor() { super(); this.postgreSql = true; this._operationsThatNeedParenthesis._getMonth = true; this._operationsThatNeedParenthesis._getMilliseconds = true; } _isReservedKeyword(word) { return word.toUpperCase() in reservedWords; } _appendColumnAlias(name, params) { if (!this._isWithGeneratedFinished(params)) { // Avoid quote identifiers when the with clause is generated return this._escape(name, true); } if (this._isAggregateArrayWrapped(params)) { // Avoid quote identifiers when the aggregate array query wrapper is generated return this._escape(name, true); } if (name.toLocaleLowerCase() !== name) { // Avoid automatically lowercase identifiers by postgresql when it contains uppercase characters return this._forceAsIdentifier(name); } return this._escape(name, true); } _buildWithValues(withValues, params) { let result = withValues.__name; let columns = ''; for (var columnName in withValues) { const column = (0, Column_1.__getColumnOfObject)(withValues.__getTableOrView(), columnName); if (!column) { continue; } if (columns) { columns += ', '; } const columnPrivate = (0, Column_1.__getColumnPrivate)(column); columns += this._appendColumnAlias(columnPrivate.__name, params); } result += '(' + columns + ')'; result += ' as (values '; const values = withValues.__values; let valuesSql = ''; for (let i = 0, length = values.length; i < length; i++) { if (valuesSql) { valuesSql += ', '; } const value = values[i]; let valueSql = ''; for (var columnName in withValues) { const column = (0, Column_1.__getColumnOfObject)(withValues.__getTableOrView(), columnName); if (!column) { continue; } if (valueSql) { valueSql += ', '; } valueSql += this._appendValueForColumn(column, value[columnName], params, true); } valuesSql += '(' + valueSql + ')'; } result += valuesSql; result += ')'; return result; } _buildSelectLimitOffset(query, params) { let result = ''; const limit = query.__limit; if (limit !== null && limit !== undefined) { result += ' limit ' + this._appendValue(limit, params, 'int', 'int', undefined); } const offset = query.__offset; if (offset !== null && offset !== undefined) { result += ' offset ' + this._appendValue(offset, params, 'int', 'int', undefined); } return result; } _appendCustomBooleanRemapForColumnIfRequired(column, value, params) { const columnPrivate = (0, Column_1.__getColumnPrivate)(column); const columnTypeAdapter = columnPrivate.__typeAdapter; const columnTypeName = columnPrivate.__valueTypeName; const columnType = columnPrivate.__valueType; if (!(0, values_1.__isBooleanValueSource)(columnPrivate)) { return null; // non-boolean } if (columnTypeAdapter instanceof TypeAdapter_1.CustomBooleanTypeAdapter) { if ((0, Column_1.isColumn)(value)) { const valuePrivate = (0, Column_1.__getColumnPrivate)(value); const valueTypeAdapter = valuePrivate.__typeAdapter; if (valueTypeAdapter instanceof TypeAdapter_1.CustomBooleanTypeAdapter) { if (columnTypeAdapter.trueValue === valueTypeAdapter.trueValue && columnTypeAdapter.falseValue === valueTypeAdapter.falseValue) { return this._appendRawColumnName(value, params); // same boolean as column } if (columnPrivate.__optionalType === 'required') { // remapped return 'case when ' + this._appendRawColumnName(value, params) + ' = ' + this._appendLiteralValue(valueTypeAdapter.trueValue, params) + ' then ' + this._appendLiteralValue(columnTypeAdapter.trueValue, params) + ' else ' + this._appendLiteralValue(columnTypeAdapter.falseValue, params) + ' end'; } else { // remapped return 'case ' + this._appendRawColumnName(value, params) + ' when ' + this._appendLiteralValue(valueTypeAdapter.trueValue, params) + ' then ' + this._appendLiteralValue(columnTypeAdapter.trueValue, params) + ' when ' + this._appendLiteralValue(valueTypeAdapter.falseValue, params) + ' then ' + this._appendLiteralValue(columnTypeAdapter.falseValue, params) + ' else null end'; } } else { if (columnPrivate.__optionalType === 'required') { // remapped return 'case when ' + this._appendConditionValue(value, params, columnType, columnTypeName, columnTypeAdapter) + ' then ' + this._appendLiteralValue(columnTypeAdapter.trueValue, params) + ' else ' + this._appendLiteralValue(columnTypeAdapter.falseValue, params) + ' end'; } else { // remapped return 'case ' + this._appendConditionValue(value, params, columnType, columnTypeName, columnTypeAdapter) + ' when ' + this._trueValue + ' then ' + this._appendLiteralValue(columnTypeAdapter.trueValue, params) + ' when ' + this._falseValue + ' then ' + this._appendLiteralValue(columnTypeAdapter.falseValue, params) + ' else null end'; } } } else if ((0, values_1.isValueSource)(value)) { // There are some boolean expressions involved if (columnPrivate.__optionalType === 'required') { // remapped return 'case when ' + this._appendConditionValue(value, params, columnType, columnTypeName, columnTypeAdapter) + ' then ' + this._appendLiteralValue(columnTypeAdapter.trueValue, params) + ' else ' + this._appendLiteralValue(columnTypeAdapter.falseValue, params) + ' end'; } else { // remapped return 'case when ' + this._appendConditionValue(value, params, columnType, columnTypeName, columnTypeAdapter) + ' then ' + this._appendLiteralValue(columnTypeAdapter.trueValue, params) + ' when not ' + this._appendConditionValue(value, params, columnType, columnTypeName, columnTypeAdapter) + ' then ' + this._appendLiteralValue(columnTypeAdapter.falseValue, params) + ' else null end'; } } else { if (columnPrivate.__optionalType === 'required') { // remapped return 'case when ' + this._appendConditionValue(value, params, columnType, columnTypeName, columnTypeAdapter, true) + ' then ' + this._appendLiteralValue(columnTypeAdapter.trueValue, params) + ' else ' + this._appendLiteralValue(columnTypeAdapter.falseValue, params) + ' end'; } else { // remapped return 'case ' + this._appendConditionValue(value, params, columnType, columnTypeName, columnTypeAdapter, true) + ' when ' + this._trueValue + ' then ' + this._appendLiteralValue(columnTypeAdapter.trueValue, params) + ' when ' + this._falseValue + ' then ' + this._appendLiteralValue(columnTypeAdapter.falseValue, params) + ' else null end'; } } } // if value is column and its type adapter is CustomBooleanTypeAdapter append value will be required to normalize value // if not it is same boolean, nothing to transform here return null; } _asDouble(params, valueSource) { return this._appendSqlParenthesis(valueSource, params) + '::float'; } _asString(params, valueSource) { // Transform an uuid to string return this._appendSqlParenthesis(valueSource, params) + '::text'; } _asNullValue(_params, _columnType, columnTypeName, typeAdapter) { if (typeAdapter && typeAdapter.transformPlaceholder) { return typeAdapter.transformPlaceholder('null', columnTypeName, true, null, this._defaultTypeAdapter); } else { return this._defaultTypeAdapter.transformPlaceholder('null', columnTypeName, true, null); } } _divide(params, valueSource, value, columnType, columnTypeName, typeAdapter) { return this._appendSqlParenthesis(valueSource, params) + '::float / ' + this._appendValueParenthesis(value, params, this._getMathArgumentType(columnType, columnTypeName, value), this._getMathArgumentTypeName(columnType, columnTypeName, value), typeAdapter) + '::float'; } _equalsInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + ' = ' + this._appendValueParenthesis(value, params, columnType, columnTypeName, typeAdapter) + ' collate "' + collation + '"'; } else if (collation === '') { return this._appendSqlParenthesis(valueSource, params) + ' = ' + this._appendValueParenthesis(value, params, columnType, columnTypeName, typeAdapter); } else { return 'lower(' + this._appendSql(valueSource, params) + ') = lower(' + this._appendValue(value, params, columnType, columnTypeName, typeAdapter) + ')'; } } _notEqualsInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + ' <> ' + this._appendValueParenthesis(value, params, columnType, columnTypeName, typeAdapter) + ' collate "' + collation + '"'; } else if (collation === '') { return this._appendSqlParenthesis(valueSource, params) + ' <> ' + this._appendValueParenthesis(value, params, columnType, columnTypeName, typeAdapter); } else { return 'lower(' + this._appendSql(valueSource, params) + ') <> lower(' + this._appendValue(value, params, columnType, columnTypeName, typeAdapter) + ')'; } } _likeInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + ' ilike ' + this._appendValueParenthesis(value, params, columnType, columnTypeName, typeAdapter) + ' collate "' + collation + '"'; } else { return this._appendSqlParenthesis(valueSource, params) + ' ilike ' + this._appendValueParenthesis(value, params, columnType, columnTypeName, typeAdapter); } } _notLikeInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + ' not ilike ' + this._appendValueParenthesis(value, params, columnType, columnTypeName, typeAdapter) + ' collate "' + collation + '"'; } else { return this._appendSqlParenthesis(valueSource, params) + ' not ilike ' + this._appendValueParenthesis(value, params, columnType, columnTypeName, typeAdapter); } } _startsWithInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + ' ilike (' + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + " || '%')" + ' collate "' + collation + '"'; } else { return this._appendSqlParenthesis(valueSource, params) + ' ilike (' + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + " || '%')"; } } _notStartsWithInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + ' not ilike (' + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + " || '%')" + ' collate "' + collation + '"'; } else { return this._appendSqlParenthesis(valueSource, params) + ' not ilike (' + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + " || '%')"; } } _endsWithInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + " ilike ('%' || " + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + ') collate "' + collation + '"'; } else { return this._appendSqlParenthesis(valueSource, params) + " ilike ('%' || " + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + ')'; } } _notEndsWithInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + " not ilike ('%' || " + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + ') collate "' + collation + '"'; } else { return this._appendSqlParenthesis(valueSource, params) + " not ilike ('%' || " + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + ')'; } } _containsInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + " ilike ('%' || " + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + " || '%')" + ' collate "' + collation + '"'; } else { return this._appendSqlParenthesis(valueSource, params) + " ilike ('%' || " + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + " || '%')"; } } _notContainsInsensitive(params, valueSource, value, columnType, columnTypeName, typeAdapter) { const collation = this._connectionConfiguration.insesitiveCollation; if (collation) { return this._appendSqlParenthesis(valueSource, params) + " not ilike ('%' || " + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + " || '%')" + ' collate "' + collation + '"'; } else { return this._appendSqlParenthesis(valueSource, params) + " not ilike ('%' || " + this._escapeLikeWildcard(params, value, columnType, columnTypeName, typeAdapter) + " || '%')"; } } _in(params, valueSource, value, columnType, columnTypeName, typeAdapter) { if (Array.isArray(value) && value.length <= 0) { return this._falseValueForCondition; } return super._in(params, valueSource, value, columnType, columnTypeName, typeAdapter); } _notIn(params, valueSource, value, columnType, columnTypeName, typeAdapter) { if (Array.isArray(value) && value.length <= 0) { return this._trueValueForCondition; } return super._notIn(params, valueSource, value, columnType, columnTypeName, typeAdapter); } _stringConcat(params, separator, value) { if (separator === undefined || separator === null) { return 'string_agg(' + this._appendSql(value, params) + ", ',')"; } else if (separator === '') { return 'string_agg(' + this._appendSql(value, params) + ", '')"; } else { return 'string_agg(' + this._appendSql(value, params) + ', ' + this._appendValue(separator, params, 'string', 'string', undefined) + ')'; } } _stringConcatDistinct(params, separator, value) { if (separator === undefined || separator === null) { return 'string_agg(distinct ' + this._appendSql(value, params) + ", ',')"; } else if (separator === '') { return 'string_agg(distinct ' + this._appendSql(value, params) + ", '')"; } else { return 'string_agg(distinct ' + this._appendSql(value, params) + ', ' + this._appendValue(separator, params, 'string', 'string', undefined) + ')'; } } _getTime(params, valueSource) { return 'round(extract(epoch from ' + this._appendSql(valueSource, params) + ') * 1000)'; } _getSeconds(params, valueSource) { return 'extract(second from ' + this._appendSql(valueSource, params) + ')::integer'; } _getMonth(params, valueSource) { return 'extract(month from ' + this._appendSql(valueSource, params) + ') - 1'; } _getMilliseconds(params, valueSource) { return 'extract(millisecond from ' + this._appendSql(valueSource, params) + ')::integer % 1000'; } } exports.PostgreSqlSqlBuilder = PostgreSqlSqlBuilder; // Source: https://www.postgresql.org/docs/12/sql-keywords-appendix.html (version: 12, only the ones marked as reserved by postgreSql) const reservedWords = { ALL: true, ANALYSE: true, ANALYZE: true, AND: true, ANY: true, ARRAY: true, AS: true, ASC: true, ASYMMETRIC: true, BOTH: true, CASE: true, CAST: true, CHECK: true, COLLATE: true, COLUMN: true, CONSTRAINT: true, CREATE: true, CURRENT_CATALOG: true, CURRENT_DATE: true, CURRENT_ROLE: true, CURRENT_TIME: true, CURRENT_TIMESTAMP: true, CURRENT_USER: true, DEFAULT: true, DEFERRABLE: true, DESC: true, DISTINCT: true, DO: true, ELSE: true, END: true, EXCEPT: true, FALSE: true, FETCH: true, FOR: true, FOREIGN: true, FROM: true, GRANT: true, GROUP: true, HAVING: true, IN: true, INITIALLY: true, INTERSECT: true, INTO: true, LATERAL: true, LEADING: true, LIMIT: true, LOCALTIME: true, LOCALTIMESTAMP: true, NOT: true, NULL: true, OFFSET: true, ON: true, ONLY: true, OR: true, ORDER: true, PLACING: true, PRIMARY: true, REFERENCES: true, RETURNING: true, SELECT: true, SESSION_USER: true, SOME: true, SYMMETRIC: true, TABLE: true, THEN: true, TO: true, TRAILING: true, TRUE: true, UNION: true, UNIQUE: true, USER: true, USING: true, VARIADIC: true, WHEN: true, WHERE: true, WINDOW: true, WITH: true, AUTHORIZATION: true, BINARY: true, COLLATION: true, CONCURRENTLY: true, CROSS: true, CURRENT_SCHEMA: true, FREEZE: true, FULL: true, ILIKE: true, INNER: true, IS: true, ISNULL: true, JOIN: true, LEFT: true, LIKE: true, NATURAL: true, NOTNULL: true, OUTER: true, OVERLAPS: true, RIGHT: true, SIMILAR: true, TABLESAMPLE: true, VERBOSE: true };