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.

435 lines (434 loc) 24.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AbstractSqliteConnection = void 0; const AbstractConnection_1 = require("./AbstractConnection"); class AbstractSqliteConnection extends AbstractConnection_1.AbstractConnection { constructor(queryRunner, sqlBuilder) { super(queryRunner, sqlBuilder); this.uuidStrategy = 'uuid-extension'; /** * The compatibility mode avoid to use the newer syntax introduces in the newer versions of sqlite * * The newer syntax are: * - Sqlite 3.30.0 (2019-10-04): Add support for the NULLS FIRST and NULLS LAST syntax in ORDER BY clauses. * In the copatibility mode their are emulated * - Sqlite 3.35.0 (2021-03-12): Add support for the RETURNING clause on DELETE, INSERT, and UPDATE statements. * In the compatibility mode last_insert_id() is used to get the last inserted id. * When the compatibility mode is disabled the RETURNING clause on the insert statement is used. */ this.compatibilityMode = true; this.treatUnexpectedIntegerDateTimeAsJulian = false; this.treatUnexpectedStringDateTimeAsUTC = false; this.unexpectedUnixDateTimeAreMilliseconds = false; queryRunner.useDatabase('sqlite'); } getDateTimeFormat(_type) { return 'localdate as text'; } transformValueFromDB(value, type) { if (value === undefined || value == null) { return super.transformValueFromDB(value, type); } switch (type) { case 'localDate': { if (typeof value === 'string') { const valueAsNumber = +value; if (!isNaN(valueAsNumber)) { value = valueAsNumber; } } const dateTimeFormat = this.getDateTimeFormat('date'); let result; if (value instanceof Date) { result = new Date(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate())); } else if (typeof value === 'string') { if (containsTimezone(value)) { result = new Date(value); } else switch (dateTimeFormat) { case 'localdate as text': case 'localdate as text using T separator': if (value.length <= 10) { result = new Date(value + ' 00:00'); // If time is omited, UTC timezone will be used instead the local one } else { result = new Date(value); } result = new Date(Date.UTC(result.getFullYear(), result.getMonth(), result.getDate())); break; case 'Julian day as real number': case 'Unix time seconds as integer': case 'Unix time milliseconds as integer': if (!this.treatUnexpectedStringDateTimeAsUTC) { if (value.length <= 10) { result = new Date(value + ' 00:00'); // If time is omited, UTC timezone will be used instead the local one } else { result = new Date(value); } result = new Date(Date.UTC(result.getFullYear(), result.getMonth(), result.getDate())); } else { result = new Date(value + 'Z'); } break; case 'UTC as text': case 'UTC as text using T separator': case 'UTC as text using Z timezone': case 'UTC as text using T separator and Z timezone': result = new Date(value + 'Z'); break; default: throw new Error('Invalid sqlite date time format: ' + dateTimeFormat); } } else if (typeof value === 'number' || typeof value === 'bigint') { let number = (typeof value === 'bigint') ? Number(value) : value; if (dateTimeFormat === 'Julian day as real number') { result = new Date(julianToMilliseconds(number)); // Timezone is not compesated due down time is overwrited } else if (dateTimeFormat === 'Unix time seconds as integer') { result = new Date(number * 1000); } else if (dateTimeFormat === 'Unix time milliseconds as integer') { result = new Date(number); } else { // Try to automatically detect if it is a julian or a unix time // If it have decimal, it will be considered julian, otherwise unix time if (this.treatUnexpectedIntegerDateTimeAsJulian || !Number.isInteger(value)) { result = new Date(julianToMilliseconds(number)); } else if (this.unexpectedUnixDateTimeAreMilliseconds) { result = new Date(number); } else { result = new Date(number * 1000); } } result.setUTCHours(0, 0, 0, 0); } else { throw new Error(`Invalid localDate value received from the db: ${value} (type ${typeof value})`); } if (isNaN(result.getTime())) { throw new Error(`Invalid localDate value received from the db: ${value} (.getTime() returns NaN)`); } result.___type___ = 'localDate'; // This time fix works in almost every timezone (from -10 to +13, but not +14, -11, -12, almost uninhabited) result.setUTCMinutes(600); return result; } case 'localTime': { if (typeof value === 'string') { const valueAsNumber = +value; if (!isNaN(valueAsNumber)) { value = valueAsNumber; } } const dateTimeFormat = this.getDateTimeFormat('time'); let result; if (value instanceof Date) { result = new Date(value.getTime()); } else if (typeof value === 'string') { if (containsTimezone(value)) { if (containsDate(value)) { result = new Date(value); } else { result = new Date('1970-01-01 ' + value); } } else switch (dateTimeFormat) { case 'localdate as text': case 'localdate as text using T separator': if (containsDate(value)) { result = new Date(value); } else { result = new Date('1970-01-01 ' + value); } break; case 'Julian day as real number': case 'Unix time seconds as integer': case 'Unix time milliseconds as integer': if (containsDate(value)) { if (this.treatUnexpectedStringDateTimeAsUTC) { result = new Date(value + 'Z'); } else { result = new Date(value); } } else { if (this.treatUnexpectedStringDateTimeAsUTC) { result = new Date('1970-01-01 ' + value + 'Z'); } else { result = new Date('1970-01-01 ' + value); } } break; case 'UTC as text': case 'UTC as text using T separator': case 'UTC as text using Z timezone': case 'UTC as text using T separator and Z timezone': if (containsDate(value)) { result = new Date(value + 'Z'); } else { result = new Date('1970-01-01 ' + value + 'Z'); } break; default: throw new Error('Invalid sqlite date time format: ' + dateTimeFormat); } } else if (typeof value === 'number' || typeof value === 'bigint') { let number = (typeof value === 'bigint') ? Number(value) : value; if (dateTimeFormat === 'Julian day as real number') { result = new Date(julianToMilliseconds(number + 2440587.5 /* 1970-01-01 */)); } else if (dateTimeFormat === 'Unix time seconds as integer') { result = new Date(number * 1000); } else if (dateTimeFormat === 'Unix time milliseconds as integer') { result = new Date(number); } else { // Try to automatically detect if it is a julian or a unix time // If it have decimal, it will be considered julian, otherwise unix time if (this.treatUnexpectedIntegerDateTimeAsJulian || !Number.isInteger(number)) { if (number >= -1 && number <= 1) { result = new Date(julianToMilliseconds(number + 2440587.5 /* 1970-01-01 */)); } else { result = new Date(julianToMilliseconds(number)); } } else if (this.unexpectedUnixDateTimeAreMilliseconds) { result = new Date(number); } else { result = new Date(number * 1000); } } } else { throw new Error(`Invalid localTime value received from the db: ${value} (type ${typeof value})`); } if (isNaN(result.getTime())) { throw new Error(`Invalid localTime value received from the db: ${value} (.getTime() returns NaN)`); } result.___type___ = 'localTime'; result.setFullYear(1970, 0, 1); return result; } case 'localDateTime': { if (typeof value === 'string') { const valueAsNumber = +value; if (!isNaN(valueAsNumber)) { value = valueAsNumber; } } const dateTimeFormat = this.getDateTimeFormat('dateTime'); let result; if (value instanceof Date) { result = value; } else if (typeof value === 'string') { if (containsTimezone(value)) { result = new Date(value); } else switch (dateTimeFormat) { case 'localdate as text': case 'localdate as text using T separator': result = new Date(value); break; case 'Julian day as real number': case 'Unix time seconds as integer': if (this.treatUnexpectedStringDateTimeAsUTC) { result = new Date(value + 'Z'); } else { result = new Date(value); } break; case 'UTC as text': case 'UTC as text using T separator': case 'UTC as text using Z timezone': case 'UTC as text using T separator and Z timezone': result = new Date(value + 'Z'); break; default: throw new Error('Invalid sqlite date time format: ' + dateTimeFormat); } } else if (typeof value === 'number' || typeof value === 'bigint') { let number = (typeof value === 'bigint') ? Number(value) : value; if (dateTimeFormat === 'Julian day as real number') { result = new Date(julianToMilliseconds(number)); // Timezone is not compesated due down time is overwrited } else if (dateTimeFormat === 'Unix time seconds as integer') { result = new Date(number * 1000); } else if (dateTimeFormat === 'Unix time milliseconds as integer') { result = new Date(number); } else { // Try to automatically detect if it is a julian or a unix time // If it have decimal, it will be considered julian, otherwise unix time if (this.treatUnexpectedIntegerDateTimeAsJulian || !Number.isInteger(value)) { result = new Date(julianToMilliseconds(number)); } else if (this.unexpectedUnixDateTimeAreMilliseconds) { result = new Date(number); } else { result = new Date(number * 1000); } } } else { throw new Error(`Invalid localDateTime value received from the db: ${value} (type ${typeof value})`); } if (isNaN(result.getTime())) { throw new Error(`Invalid localDateTime value received from the db: ${value} (.getTime() returns NaN)`); } result.___type___ = 'LocalDateTime'; return result; } } return super.transformValueFromDB(value, type); } transformValueToDB(value, type) { if (value === undefined || value == null) { return super.transformValueToDB(value, type); } switch (type) { case 'localDate': if (value instanceof Date && !isNaN(value.getTime())) { const dateTimeFormat = this.getDateTimeFormat('date'); switch (dateTimeFormat) { case 'localdate as text': case 'localdate as text using T separator': case 'UTC as text': case 'UTC as text using T separator': case 'UTC as text using Z timezone': case 'UTC as text using T separator and Z timezone': return value.getFullYear() + '-' + doubleDigit(value.getMonth() + 1) + '-' + doubleDigit(value.getDate()); case 'Julian day as real number': return millisecondsToJulian(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate())); case 'Unix time seconds as integer': return Math.trunc(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()) / 1000); case 'Unix time milliseconds as integer': return Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()); default: throw new Error('Invalid sqlite date time format: ' + dateTimeFormat); } } throw new Error(`Invalid localDate value to send to the db: ${value} (type ${typeof value})`); case 'localTime': if (value instanceof Date && !isNaN(value.getTime())) { const dateTimeFormat = this.getDateTimeFormat('time'); switch (dateTimeFormat) { case 'localdate as text': case 'localdate as text using T separator': return doubleDigit(value.getHours()) + ':' + doubleDigit(value.getMinutes()) + ':' + doubleDigit(value.getSeconds()) + tripleDigitFraction(value.getMilliseconds()); case 'UTC as text': case 'UTC as text using T separator': return doubleDigit(value.getUTCHours()) + ':' + doubleDigit(value.getUTCMinutes()) + ':' + doubleDigit(value.getUTCSeconds()) + tripleDigitFraction(value.getUTCMilliseconds()); case 'UTC as text using Z timezone': case 'UTC as text using T separator and Z timezone': return doubleDigit(value.getUTCHours()) + ':' + doubleDigit(value.getUTCMinutes()) + ':' + doubleDigit(value.getUTCSeconds()) + tripleDigitFraction(value.getUTCMilliseconds()) + 'Z'; case 'Julian day as real number': return millisecondsToJulian(Date.UTC(1970, 0, 1, value.getUTCHours(), value.getUTCMinutes(), value.getUTCSeconds(), value.getUTCMilliseconds())) - 2440587.5; /* 1970-01-01 */ case 'Unix time seconds as integer': return Math.trunc(Date.UTC(1970, 0, 1, value.getUTCHours(), value.getUTCMinutes(), value.getUTCSeconds(), value.getUTCMilliseconds()) / 1000); case 'Unix time milliseconds as integer': return Date.UTC(1970, 0, 1, value.getUTCHours(), value.getUTCMinutes(), value.getUTCSeconds(), value.getUTCMilliseconds()); default: throw new Error('Invalid sqlite date time format: ' + dateTimeFormat); } } throw new Error(`Invalid localTime value to send to the db: ${value} (type ${typeof value})`); case 'localDateTime': if (value instanceof Date && !isNaN(value.getTime())) { const dateTimeFormat = this.getDateTimeFormat('dateTime'); switch (dateTimeFormat) { case 'localdate as text': return value.getFullYear() + '-' + doubleDigit(value.getMonth() + 1) + '-' + doubleDigit(value.getDate()) + ' ' + doubleDigit(value.getHours()) + ':' + doubleDigit(value.getMinutes()) + ':' + doubleDigit(value.getSeconds()) + tripleDigitFraction(value.getMilliseconds()); case 'localdate as text using T separator': return value.getFullYear() + '-' + doubleDigit(value.getMonth() + 1) + '-' + doubleDigit(value.getDate()) + 'T' + doubleDigit(value.getHours()) + ':' + doubleDigit(value.getMinutes()) + ':' + doubleDigit(value.getSeconds()) + tripleDigitFraction(value.getMilliseconds()); case 'UTC as text': return value.getUTCFullYear() + '-' + doubleDigit(value.getUTCMonth() + 1) + '-' + doubleDigit(value.getUTCDate()) + ' ' + doubleDigit(value.getUTCHours()) + ':' + doubleDigit(value.getUTCMinutes()) + ':' + doubleDigit(value.getUTCSeconds()) + tripleDigitFraction(value.getUTCMilliseconds()); case 'UTC as text using T separator': return value.getUTCFullYear() + '-' + doubleDigit(value.getUTCMonth() + 1) + '-' + doubleDigit(value.getUTCDate()) + 'T' + doubleDigit(value.getUTCHours()) + ':' + doubleDigit(value.getUTCMinutes()) + ':' + doubleDigit(value.getUTCSeconds()) + tripleDigitFraction(value.getUTCMilliseconds()); case 'UTC as text using Z timezone': return value.getUTCFullYear() + '-' + doubleDigit(value.getUTCMonth() + 1) + '-' + doubleDigit(value.getUTCDate()) + ' ' + doubleDigit(value.getUTCHours()) + ':' + doubleDigit(value.getUTCMinutes()) + ':' + doubleDigit(value.getUTCSeconds()) + tripleDigitFraction(value.getUTCMilliseconds()) + 'Z'; case 'UTC as text using T separator and Z timezone': return value.getUTCFullYear() + '-' + doubleDigit(value.getUTCMonth() + 1) + '-' + doubleDigit(value.getUTCDate()) + 'T' + doubleDigit(value.getUTCHours()) + ':' + doubleDigit(value.getUTCMinutes()) + ':' + doubleDigit(value.getUTCSeconds()) + tripleDigitFraction(value.getUTCMilliseconds()) + 'Z'; case 'Julian day as real number': return millisecondsToJulian(value.getTime()); case 'Unix time seconds as integer': return Math.trunc(value.getTime() / 1000); case 'Unix time milliseconds as integer': return value.getTime(); default: throw new Error('Invalid sqlite date time format: ' + dateTimeFormat); } } throw new Error(`Invalid localDateTime value to send to the db: ${value} (type ${typeof value})`); } return super.transformValueToDB(value, type); } } exports.AbstractSqliteConnection = AbstractSqliteConnection; function doubleDigit(value) { if (value > 9) { return '' + value; } else { return '0' + value; } } function tripleDigit(value) { if (value > 99) { return '' + value; } else if (value > 9) { return '0' + value; } else { return '00' + value; } } function tripleDigitFraction(value) { if (value > 0) { return '.' + tripleDigit(value); } return ''; } function millisecondsToJulian(value) { return value / 86400000.0 + 2440587.5; } function julianToMilliseconds(value) { return (value - 2440587.5) * 86400000.0; } function containsTimezone(value) { return /(([\+\-]\d\d?(:\d\d?)?)|Z)$/.test(value); } function containsDate(value) { return /^\d+-\d\d?-\d\d?(\s|T)/.test(value); }