UNPKG

@freemework/sql.postgres

Version:

Postgres SQL Facade library of the Freemework Project.

934 lines (933 loc) 57.1 kB
import { FCancellationToken, FDisposableBase, FExceptionAggregate, FExceptionArgument, FExceptionInvalidOperation, FExecutionContext, FInitableBase, FLogger, FDecimal, FSqlExceptionConstraint, FSqlExceptionPermission, FSqlExceptionSyntax, FSqlException, FException, FSqlExceptionNoSuchRecord, FCancellationExecutionContext, FCancellationException, FLoggerLabelsExecutionContext, FLoggerLabel } from "@freemework/common"; import pg from "pg"; /** * Package oid contains OID constants as defined by the Postgres server. * @description SELECT CASE WHEN "typcategory" = 'A' THEN CONCAT('array', "typname", ' = ', "oid", ',') ELSE CONCAT("typname", ' = ', "oid", ',') END FROM "pg_type" WHERE "oid" < 10000 ORDER BY "oid" * @see PG type codes - https://www.postgresql.org/docs/current/catalog-pg-type.html#CATALOG-TYPCATEGORY-TABLE * @see https://github.com/postgres/postgres/blob/2e4db241bfd3206bad8286f8ffc2db6bbdaefcdf/src/include/catalog/pg_type.dat */ var PostgresObjectID; (function (PostgresObjectID) { PostgresObjectID[PostgresObjectID["bool"] = 16] = "bool"; PostgresObjectID[PostgresObjectID["bytea"] = 17] = "bytea"; PostgresObjectID[PostgresObjectID["char"] = 18] = "char"; PostgresObjectID[PostgresObjectID["name"] = 19] = "name"; PostgresObjectID[PostgresObjectID["int8"] = 20] = "int8"; PostgresObjectID[PostgresObjectID["int2"] = 21] = "int2"; PostgresObjectID[PostgresObjectID["arrayint2vector"] = 22] = "arrayint2vector"; PostgresObjectID[PostgresObjectID["int4"] = 23] = "int4"; PostgresObjectID[PostgresObjectID["regproc"] = 24] = "regproc"; PostgresObjectID[PostgresObjectID["text"] = 25] = "text"; PostgresObjectID[PostgresObjectID["oid"] = 26] = "oid"; PostgresObjectID[PostgresObjectID["tid"] = 27] = "tid"; PostgresObjectID[PostgresObjectID["xid"] = 28] = "xid"; PostgresObjectID[PostgresObjectID["cid"] = 29] = "cid"; PostgresObjectID[PostgresObjectID["arrayoidvector"] = 30] = "arrayoidvector"; PostgresObjectID[PostgresObjectID["pg_ddl_command"] = 32] = "pg_ddl_command"; PostgresObjectID[PostgresObjectID["pg_type"] = 71] = "pg_type"; PostgresObjectID[PostgresObjectID["pg_attribute"] = 75] = "pg_attribute"; PostgresObjectID[PostgresObjectID["pg_proc"] = 81] = "pg_proc"; PostgresObjectID[PostgresObjectID["pg_class"] = 83] = "pg_class"; PostgresObjectID[PostgresObjectID["json"] = 114] = "json"; PostgresObjectID[PostgresObjectID["xml"] = 142] = "xml"; PostgresObjectID[PostgresObjectID["array_xml"] = 143] = "array_xml"; PostgresObjectID[PostgresObjectID["pg_node_tree"] = 194] = "pg_node_tree"; PostgresObjectID[PostgresObjectID["array_json"] = 199] = "array_json"; PostgresObjectID[PostgresObjectID["array_pg_type"] = 210] = "array_pg_type"; PostgresObjectID[PostgresObjectID["table_am_handler"] = 269] = "table_am_handler"; PostgresObjectID[PostgresObjectID["array_pg_attribute"] = 270] = "array_pg_attribute"; PostgresObjectID[PostgresObjectID["array_xid8"] = 271] = "array_xid8"; PostgresObjectID[PostgresObjectID["array_pg_proc"] = 272] = "array_pg_proc"; PostgresObjectID[PostgresObjectID["array_pg_class"] = 273] = "array_pg_class"; PostgresObjectID[PostgresObjectID["index_am_handler"] = 325] = "index_am_handler"; PostgresObjectID[PostgresObjectID["point"] = 600] = "point"; PostgresObjectID[PostgresObjectID["lseg"] = 601] = "lseg"; PostgresObjectID[PostgresObjectID["path"] = 602] = "path"; PostgresObjectID[PostgresObjectID["box"] = 603] = "box"; PostgresObjectID[PostgresObjectID["polygon"] = 604] = "polygon"; PostgresObjectID[PostgresObjectID["line"] = 628] = "line"; PostgresObjectID[PostgresObjectID["array_line"] = 629] = "array_line"; PostgresObjectID[PostgresObjectID["cidr"] = 650] = "cidr"; PostgresObjectID[PostgresObjectID["array_cidr"] = 651] = "array_cidr"; PostgresObjectID[PostgresObjectID["float4"] = 700] = "float4"; PostgresObjectID[PostgresObjectID["float8"] = 701] = "float8"; PostgresObjectID[PostgresObjectID["unknown"] = 705] = "unknown"; PostgresObjectID[PostgresObjectID["circle"] = 718] = "circle"; PostgresObjectID[PostgresObjectID["array_circle"] = 719] = "array_circle"; PostgresObjectID[PostgresObjectID["macaddr8"] = 774] = "macaddr8"; PostgresObjectID[PostgresObjectID["array_macaddr8"] = 775] = "array_macaddr8"; PostgresObjectID[PostgresObjectID["money"] = 790] = "money"; PostgresObjectID[PostgresObjectID["array_money"] = 791] = "array_money"; PostgresObjectID[PostgresObjectID["macaddr"] = 829] = "macaddr"; PostgresObjectID[PostgresObjectID["inet"] = 869] = "inet"; PostgresObjectID[PostgresObjectID["array_bool"] = 1000] = "array_bool"; PostgresObjectID[PostgresObjectID["array_bytea"] = 1001] = "array_bytea"; PostgresObjectID[PostgresObjectID["array_char"] = 1002] = "array_char"; PostgresObjectID[PostgresObjectID["array_name"] = 1003] = "array_name"; PostgresObjectID[PostgresObjectID["array_int2"] = 1005] = "array_int2"; PostgresObjectID[PostgresObjectID["array_int2vector"] = 1006] = "array_int2vector"; PostgresObjectID[PostgresObjectID["array_int4"] = 1007] = "array_int4"; PostgresObjectID[PostgresObjectID["array_regproc"] = 1008] = "array_regproc"; PostgresObjectID[PostgresObjectID["array_text"] = 1009] = "array_text"; PostgresObjectID[PostgresObjectID["array_tid"] = 1010] = "array_tid"; PostgresObjectID[PostgresObjectID["array_xid"] = 1011] = "array_xid"; PostgresObjectID[PostgresObjectID["array_cid"] = 1012] = "array_cid"; PostgresObjectID[PostgresObjectID["array_oidvector"] = 1013] = "array_oidvector"; PostgresObjectID[PostgresObjectID["array_bpchar"] = 1014] = "array_bpchar"; PostgresObjectID[PostgresObjectID["array_varchar"] = 1015] = "array_varchar"; PostgresObjectID[PostgresObjectID["array_int8"] = 1016] = "array_int8"; PostgresObjectID[PostgresObjectID["array_point"] = 1017] = "array_point"; PostgresObjectID[PostgresObjectID["array_lseg"] = 1018] = "array_lseg"; PostgresObjectID[PostgresObjectID["array_path"] = 1019] = "array_path"; PostgresObjectID[PostgresObjectID["array_box"] = 1020] = "array_box"; PostgresObjectID[PostgresObjectID["array_float4"] = 1021] = "array_float4"; PostgresObjectID[PostgresObjectID["array_float8"] = 1022] = "array_float8"; PostgresObjectID[PostgresObjectID["array_polygon"] = 1027] = "array_polygon"; PostgresObjectID[PostgresObjectID["array_oid"] = 1028] = "array_oid"; PostgresObjectID[PostgresObjectID["aclitem"] = 1033] = "aclitem"; PostgresObjectID[PostgresObjectID["array_aclitem"] = 1034] = "array_aclitem"; PostgresObjectID[PostgresObjectID["array_macaddr"] = 1040] = "array_macaddr"; PostgresObjectID[PostgresObjectID["array_inet"] = 1041] = "array_inet"; PostgresObjectID[PostgresObjectID["bpchar"] = 1042] = "bpchar"; PostgresObjectID[PostgresObjectID["varchar"] = 1043] = "varchar"; PostgresObjectID[PostgresObjectID["date"] = 1082] = "date"; PostgresObjectID[PostgresObjectID["time"] = 1083] = "time"; PostgresObjectID[PostgresObjectID["timestamp"] = 1114] = "timestamp"; PostgresObjectID[PostgresObjectID["array_timestamp"] = 1115] = "array_timestamp"; PostgresObjectID[PostgresObjectID["array_date"] = 1182] = "array_date"; PostgresObjectID[PostgresObjectID["array_time"] = 1183] = "array_time"; PostgresObjectID[PostgresObjectID["timestamptz"] = 1184] = "timestamptz"; PostgresObjectID[PostgresObjectID["array_timestamptz"] = 1185] = "array_timestamptz"; PostgresObjectID[PostgresObjectID["interval"] = 1186] = "interval"; PostgresObjectID[PostgresObjectID["array_interval"] = 1187] = "array_interval"; PostgresObjectID[PostgresObjectID["array_numeric"] = 1231] = "array_numeric"; PostgresObjectID[PostgresObjectID["pg_database"] = 1248] = "pg_database"; PostgresObjectID[PostgresObjectID["array_cstring"] = 1263] = "array_cstring"; PostgresObjectID[PostgresObjectID["timetz"] = 1266] = "timetz"; PostgresObjectID[PostgresObjectID["array_timetz"] = 1270] = "array_timetz"; PostgresObjectID[PostgresObjectID["bit"] = 1560] = "bit"; PostgresObjectID[PostgresObjectID["array_bit"] = 1561] = "array_bit"; PostgresObjectID[PostgresObjectID["varbit"] = 1562] = "varbit"; PostgresObjectID[PostgresObjectID["array_varbit"] = 1563] = "array_varbit"; PostgresObjectID[PostgresObjectID["numeric"] = 1700] = "numeric"; PostgresObjectID[PostgresObjectID["refcursor"] = 1790] = "refcursor"; PostgresObjectID[PostgresObjectID["array_refcursor"] = 2201] = "array_refcursor"; PostgresObjectID[PostgresObjectID["regprocedure"] = 2202] = "regprocedure"; PostgresObjectID[PostgresObjectID["regoper"] = 2203] = "regoper"; PostgresObjectID[PostgresObjectID["regoperator"] = 2204] = "regoperator"; PostgresObjectID[PostgresObjectID["regclass"] = 2205] = "regclass"; PostgresObjectID[PostgresObjectID["regtype"] = 2206] = "regtype"; PostgresObjectID[PostgresObjectID["array_regprocedure"] = 2207] = "array_regprocedure"; PostgresObjectID[PostgresObjectID["array_regoper"] = 2208] = "array_regoper"; PostgresObjectID[PostgresObjectID["array_regoperator"] = 2209] = "array_regoperator"; PostgresObjectID[PostgresObjectID["array_regclass"] = 2210] = "array_regclass"; PostgresObjectID[PostgresObjectID["array_regtype"] = 2211] = "array_regtype"; PostgresObjectID[PostgresObjectID["record"] = 2249] = "record"; PostgresObjectID[PostgresObjectID["cstring"] = 2275] = "cstring"; PostgresObjectID[PostgresObjectID["any"] = 2276] = "any"; PostgresObjectID[PostgresObjectID["anyarray"] = 2277] = "anyarray"; PostgresObjectID[PostgresObjectID["void"] = 2278] = "void"; PostgresObjectID[PostgresObjectID["trigger"] = 2279] = "trigger"; PostgresObjectID[PostgresObjectID["language_handler"] = 2280] = "language_handler"; PostgresObjectID[PostgresObjectID["internal"] = 2281] = "internal"; PostgresObjectID[PostgresObjectID["anyelement"] = 2283] = "anyelement"; PostgresObjectID[PostgresObjectID["_record"] = 2287] = "_record"; PostgresObjectID[PostgresObjectID["anynonarray"] = 2776] = "anynonarray"; PostgresObjectID[PostgresObjectID["pg_authid"] = 2842] = "pg_authid"; PostgresObjectID[PostgresObjectID["pg_auth_members"] = 2843] = "pg_auth_members"; PostgresObjectID[PostgresObjectID["array_txid_snapshot"] = 2949] = "array_txid_snapshot"; PostgresObjectID[PostgresObjectID["uuid"] = 2950] = "uuid"; PostgresObjectID[PostgresObjectID["array_uuid"] = 2951] = "array_uuid"; PostgresObjectID[PostgresObjectID["txid_snapshot"] = 2970] = "txid_snapshot"; PostgresObjectID[PostgresObjectID["fdw_handler"] = 3115] = "fdw_handler"; PostgresObjectID[PostgresObjectID["pg_lsn"] = 3220] = "pg_lsn"; PostgresObjectID[PostgresObjectID["array_pg_lsn"] = 3221] = "array_pg_lsn"; PostgresObjectID[PostgresObjectID["tsm_handler"] = 3310] = "tsm_handler"; PostgresObjectID[PostgresObjectID["pg_ndistinct"] = 3361] = "pg_ndistinct"; PostgresObjectID[PostgresObjectID["pg_dependencies"] = 3402] = "pg_dependencies"; PostgresObjectID[PostgresObjectID["anyenum"] = 3500] = "anyenum"; PostgresObjectID[PostgresObjectID["tsvector"] = 3614] = "tsvector"; PostgresObjectID[PostgresObjectID["tsquery"] = 3615] = "tsquery"; PostgresObjectID[PostgresObjectID["gtsvector"] = 3642] = "gtsvector"; PostgresObjectID[PostgresObjectID["array_tsvector"] = 3643] = "array_tsvector"; PostgresObjectID[PostgresObjectID["array_gtsvector"] = 3644] = "array_gtsvector"; PostgresObjectID[PostgresObjectID["array_tsquery"] = 3645] = "array_tsquery"; PostgresObjectID[PostgresObjectID["regconfig"] = 3734] = "regconfig"; PostgresObjectID[PostgresObjectID["array_regconfig"] = 3735] = "array_regconfig"; PostgresObjectID[PostgresObjectID["regdictionary"] = 3769] = "regdictionary"; PostgresObjectID[PostgresObjectID["array_regdictionary"] = 3770] = "array_regdictionary"; PostgresObjectID[PostgresObjectID["jsonb"] = 3802] = "jsonb"; PostgresObjectID[PostgresObjectID["array_jsonb"] = 3807] = "array_jsonb"; PostgresObjectID[PostgresObjectID["anyrange"] = 3831] = "anyrange"; PostgresObjectID[PostgresObjectID["event_trigger"] = 3838] = "event_trigger"; PostgresObjectID[PostgresObjectID["int4range"] = 3904] = "int4range"; PostgresObjectID[PostgresObjectID["array_int4range"] = 3905] = "array_int4range"; PostgresObjectID[PostgresObjectID["numrange"] = 3906] = "numrange"; PostgresObjectID[PostgresObjectID["array_numrange"] = 3907] = "array_numrange"; PostgresObjectID[PostgresObjectID["tsrange"] = 3908] = "tsrange"; PostgresObjectID[PostgresObjectID["array_tsrange"] = 3909] = "array_tsrange"; PostgresObjectID[PostgresObjectID["tstzrange"] = 3910] = "tstzrange"; PostgresObjectID[PostgresObjectID["array_tstzrange"] = 3911] = "array_tstzrange"; PostgresObjectID[PostgresObjectID["daterange"] = 3912] = "daterange"; PostgresObjectID[PostgresObjectID["array_daterange"] = 3913] = "array_daterange"; PostgresObjectID[PostgresObjectID["int8range"] = 3926] = "int8range"; PostgresObjectID[PostgresObjectID["array_int8range"] = 3927] = "array_int8range"; PostgresObjectID[PostgresObjectID["pg_shseclabel"] = 4066] = "pg_shseclabel"; PostgresObjectID[PostgresObjectID["jsonpath"] = 4072] = "jsonpath"; PostgresObjectID[PostgresObjectID["array_jsonpath"] = 4073] = "array_jsonpath"; PostgresObjectID[PostgresObjectID["regnamespace"] = 4089] = "regnamespace"; PostgresObjectID[PostgresObjectID["array_regnamespace"] = 4090] = "array_regnamespace"; PostgresObjectID[PostgresObjectID["regrole"] = 4096] = "regrole"; PostgresObjectID[PostgresObjectID["array_regrole"] = 4097] = "array_regrole"; PostgresObjectID[PostgresObjectID["regcollation"] = 4191] = "regcollation"; PostgresObjectID[PostgresObjectID["array_regcollation"] = 4192] = "array_regcollation"; PostgresObjectID[PostgresObjectID["int4multirange"] = 4451] = "int4multirange"; PostgresObjectID[PostgresObjectID["nummultirange"] = 4532] = "nummultirange"; PostgresObjectID[PostgresObjectID["tsmultirange"] = 4533] = "tsmultirange"; PostgresObjectID[PostgresObjectID["tstzmultirange"] = 4534] = "tstzmultirange"; PostgresObjectID[PostgresObjectID["datemultirange"] = 4535] = "datemultirange"; PostgresObjectID[PostgresObjectID["int8multirange"] = 4536] = "int8multirange"; PostgresObjectID[PostgresObjectID["anymultirange"] = 4537] = "anymultirange"; PostgresObjectID[PostgresObjectID["anycompatiblemultirange"] = 4538] = "anycompatiblemultirange"; PostgresObjectID[PostgresObjectID["pg_brin_bloom_summary"] = 4600] = "pg_brin_bloom_summary"; PostgresObjectID[PostgresObjectID["pg_brin_minmax_multi_summary"] = 4601] = "pg_brin_minmax_multi_summary"; PostgresObjectID[PostgresObjectID["pg_mcv_list"] = 5017] = "pg_mcv_list"; PostgresObjectID[PostgresObjectID["pg_snapshot"] = 5038] = "pg_snapshot"; PostgresObjectID[PostgresObjectID["array_pg_snapshot"] = 5039] = "array_pg_snapshot"; PostgresObjectID[PostgresObjectID["xid8"] = 5069] = "xid8"; PostgresObjectID[PostgresObjectID["anycompatible"] = 5077] = "anycompatible"; PostgresObjectID[PostgresObjectID["anycompatiblearray"] = 5078] = "anycompatiblearray"; PostgresObjectID[PostgresObjectID["anycompatiblenonarray"] = 5079] = "anycompatiblenonarray"; PostgresObjectID[PostgresObjectID["anycompatiblerange"] = 5080] = "anycompatiblerange"; PostgresObjectID[PostgresObjectID["pg_subscription"] = 6101] = "pg_subscription"; PostgresObjectID[PostgresObjectID["array_int4multirange"] = 6150] = "array_int4multirange"; PostgresObjectID[PostgresObjectID["array_nummultirange"] = 6151] = "array_nummultirange"; PostgresObjectID[PostgresObjectID["array_tsmultirange"] = 6152] = "array_tsmultirange"; PostgresObjectID[PostgresObjectID["array_tstzmultirange"] = 6153] = "array_tstzmultirange"; PostgresObjectID[PostgresObjectID["array_datemultirange"] = 6155] = "array_datemultirange"; PostgresObjectID[PostgresObjectID["array_int8multirange"] = 6157] = "array_int8multirange"; })(PostgresObjectID || (PostgresObjectID = {})); pg.types.setTypeParser(PostgresObjectID.timestamp, function (stringValue) { return stringValue; }); export class FSqlConnectionFactoryPostgresLoggerLabel extends FLoggerLabel { static CONNECTION_NUMBER = new FSqlConnectionFactoryPostgresLoggerLabel("sql.postgres.connection", "Describes a number (counter) of connection"); static HOST = new FSqlConnectionFactoryPostgresLoggerLabel("sql.postgres.host", "PostgreSQL server hostname/IP"); static PORT = new FSqlConnectionFactoryPostgresLoggerLabel("sql.postgres.port", "PostgreSQL server port"); static DATABASE = new FSqlConnectionFactoryPostgresLoggerLabel("sql.postgres.db", "Name of a database"); } export class FSqlConnectionFactoryPostgres extends FInitableBase { loggingSqlLabels; _url; _pool; _defaultSchema; _databaseName; _log; _connectionCounter; // This implementation wrap package https://www.npmjs.com/package/pg constructor(opts) { super(); this._connectionCounter = 0; this._log = opts.log !== undefined ? opts.log : FLogger.create(this.constructor.name); this.loggingSqlLabels = opts.loggingSqlLabels !== undefined ? opts.loggingSqlLabels : true; switch (opts.url.protocol) { case "postgres:": case "postgres+ssl:": this._url = opts.url; break; default: throw new FExceptionArgument("Expected URL schema 'postgres:' or 'postgres+ssl:'.", "opts.url"); } this._log.trace(FExecutionContext.Empty, "PostgresProviderPoolFactory constructed"); const poolConfig = { host: this._url.hostname }; if (this._url.port !== "") { poolConfig.port = Number.parseInt(this._url.port); } if (this._url.username !== "") { poolConfig.user = this._url.username; } if (this._url.password !== "") { poolConfig.password = this._url.password; } // DB name let pathname = this._url.pathname; while (pathname.length > 0 && pathname[0] === "/") { pathname = pathname.substring(1); } poolConfig.database = pathname; this._databaseName = pathname; // Timeouts poolConfig.connectionTimeoutMillis = (opts.connectionTimeoutMillis !== undefined) ? opts.connectionTimeoutMillis : 5000; poolConfig.idleTimeoutMillis = (opts.idleTimeoutMillis !== undefined) ? opts.idleTimeoutMillis : 30000; // Keep-alive poolConfig.keepAlive = (opts.keepAlive !== undefined) ? opts.keepAlive : true; poolConfig.keepAliveInitialDelayMillis = (opts.keepAliveInitialDelayMillis !== undefined) ? opts.keepAliveInitialDelayMillis : 5000; // App name if (opts.applicationName !== "") { poolConfig.application_name = opts.applicationName; } else { const appNameFromUrl = this._url.searchParams.get("app"); if (appNameFromUrl !== null && appNameFromUrl !== "") { poolConfig.application_name = appNameFromUrl; } } const schemaFromUrl = this._url.searchParams.get("schema"); this._defaultSchema = opts.defaultSchema !== undefined ? opts.defaultSchema : schemaFromUrl !== null ? schemaFromUrl : "public"; // SSL if (this._url.protocol === "postgres+ssl:") { poolConfig.ssl = {}; if (opts.ssl !== undefined) { if (opts.ssl !== undefined) { if (opts.ssl === "prefer") { poolConfig.ssl.rejectUnauthorized = false; this._log.debug(FExecutionContext.Empty, "Using partial secured connection without checking identity (SSL-prefer, no certificate validation)."); } else { poolConfig.ssl.rejectUnauthorized = true; this._log.debug(FExecutionContext.Empty, "Using full secured connection (with certificate validation)."); if (opts.ssl.caCert !== undefined) { poolConfig.ssl.ca = opts.ssl.caCert; this._log.debug(FExecutionContext.Empty, "CA certificate was provided."); } if (opts.ssl.clientCert !== undefined) { poolConfig.ssl.cert = opts.ssl.clientCert.cert; poolConfig.ssl.key = opts.ssl.clientCert.key; this._log.debug(FExecutionContext.Empty, "Client certificate was provided."); } } } } else { poolConfig.ssl.rejectUnauthorized = false; this._log.debug(FExecutionContext.Empty, "Using partial secured connection without checking identity (SSL-prefer, no certificate validation)."); } } else { this._log.debug(FExecutionContext.Empty, "Using unsecured connection (non-SSL)."); } this._pool = new pg.Pool(poolConfig); this._pool.on("error", (e, _connection) => { /* https://node-postgres.com/api/pool When a client is sitting idly in the pool it can still emit errors because it is connected to a live backend. If the backend goes down or a network partition is encountered all the idle, connected clients in your application will emit an error through the pool's error event emitter. The error listener is passed the error as the first argument and the client upon which the error occurred as the 2nd argument. The client will be automatically terminated and removed from the pool, it is only passed to the error handler in case you want to inspect it. */ const ex = FException.wrapIfNeeded(e); this._log.debug(FExecutionContext.Empty, ex.message); this._log.trace(FExecutionContext.Empty, ex.message, ex); }); } get defaultSchema() { return this._defaultSchema; } async create(executionContext) { this.verifyInitializedAndNotDisposed(); const connectionNumber = this._connectionCounter++; const cancellationToken = FCancellationExecutionContext.of(executionContext).cancellationToken; const pgClient = await this._pool.connect(); try { cancellationToken.throwIfCancellationRequested(); if (this._defaultSchema !== null) { await pgClient.query(`SET search_path TO ${this._defaultSchema}`); } await pgClient.query("SET TIME ZONE '00:00'"); const FSqlConnection = new FSqlConnectionPostgres(executionContext, this, connectionNumber, pgClient, async () => { // dispose callback pgClient.release(); }, this._log); return FSqlConnection; } catch (e) { pgClient.release(); throw e; } } _createSqlLoggerLabelsExecutionContext(executionContext) { if (this.loggingSqlLabels) { executionContext = new FLoggerLabelsExecutionContext(executionContext, FSqlConnectionFactoryPostgresLoggerLabel.HOST.value(this._url.hostname), FSqlConnectionFactoryPostgresLoggerLabel.PORT.value(this._url.port), FSqlConnectionFactoryPostgresLoggerLabel.DATABASE.value(this._databaseName)); } return executionContext; } usingConnection(executionContext, worker) { const executionPromise = (async () => { const connectionInitExecutionContext = this._createSqlLoggerLabelsExecutionContext(executionContext); const sqlConnection = await this.create(connectionInitExecutionContext); const workerExecutionContext = this.loggingSqlLabels ? new FLoggerLabelsExecutionContext(connectionInitExecutionContext, FSqlConnectionFactoryPostgresLoggerLabel.CONNECTION_NUMBER.value(sqlConnection.connectionNumber.toString())) : connectionInitExecutionContext; try { let workerResult; if (worker.length === 1) { workerResult = await worker(sqlConnection); } else if (worker.length === 2) { workerResult = await worker(workerExecutionContext, sqlConnection); } else { throw new FExceptionArgument("Wrong worker function. Expect a function with 1 or 2 arguments.", "worker"); } return workerResult; } finally { await sqlConnection.dispose(); } })(); return executionPromise; } usingConnectionWithTransaction(executionContext, worker) { return this.usingConnection(executionContext, async (sqlConnection) => { const connectionInitExecutionContext = this._createSqlLoggerLabelsExecutionContext(executionContext); const uncancellableExecutionContext = new FCancellationExecutionContext(executionContext, FCancellationToken.Dummy); await sqlConnection.statement("BEGIN TRANSACTION").execute(executionContext); try { const workerExecutionContext = this.loggingSqlLabels ? new FLoggerLabelsExecutionContext(connectionInitExecutionContext, FSqlConnectionFactoryPostgresLoggerLabel.CONNECTION_NUMBER.value(sqlConnection.connectionNumber.toString())) : connectionInitExecutionContext; let workerResult; if (worker.length === 1) { workerResult = await worker(sqlConnection); } else if (worker.length === 2) { workerResult = await worker(workerExecutionContext, sqlConnection); } else { throw new FExceptionArgument("Wrong worker function. Expect a function with 1 or 2 arguments.", "worker"); } // We have not to cancel this operation, so pass uncancellableExecutionContext await sqlConnection.statement("COMMIT TRANSACTION").execute(uncancellableExecutionContext); return workerResult; } catch (e) { try { // We have not to cancel this operation, so pass uncancellableExecutionContext await sqlConnection.statement("ROLLBACK TRANSACTION").execute(uncancellableExecutionContext); } catch (e2) { throw new FExceptionAggregate([ FException.wrapIfNeeded(e), FException.wrapIfNeeded(e2) ]); } throw e; } }); } onInit() { const logger = this._log; if (logger.isTraceEnabled) { logger.trace(this.initExecutionContext, `Initializing instance of ${this.constructor.name} ...`); } } async onDispose() { const logger = this._log; if (logger.isTraceEnabled) { logger.trace(this.initExecutionContext, `Disposing instance of ${this.constructor.name} ...`); } // Dispose never raise error try { await this._pool.end(); } catch (e) { const ex = FException.wrapIfNeeded(e); if (logger.isWarnEnabled) { logger.warn(this.initExecutionContext, "Module 'pg' ends pool with error. " + ex.message); } else { console.error("Module 'pg' ends pool with error", e); } logger.debug(this.initExecutionContext, "Module 'pg' ends pool with error", ex); } } } class FSqlConnectionPostgres extends FDisposableBase { connectionNumber; pgClient; log; _factory; _initExecutionContext; _disposer; constructor(executionContext, factory, connectionNumber, pgClient, disposer, log) { super(); this.connectionNumber = connectionNumber; this._factory = factory; this.pgClient = pgClient; this._disposer = disposer; this.log = log; this._initExecutionContext = this._factory.loggingSqlLabels ? new FLoggerLabelsExecutionContext(executionContext, FSqlConnectionFactoryPostgresLoggerLabel.CONNECTION_NUMBER.value(this.connectionNumber.toString())) : executionContext; this.log.trace(this._initExecutionContext, "FSqlConnectionPostgres constructed"); } get factory() { return this._factory; } statement(sqlText) { super.verifyNotDisposed(); if (!sqlText) { throw new FExceptionArgument("sql"); } if (this.log.isTraceEnabled) { const trimmedSqlText = helpers.trimSqlTextForException(sqlText); this.log.trace(this._initExecutionContext, "FSqlConnectionPostgres Statement: " + trimmedSqlText); } return new FSqlStatementPostgres(this, sqlText); } async createTempTable(executionContext, tableName, columnsDefinitions) { const tempTable = new FSqlTemporaryTablePostgres(this, executionContext, tableName, columnsDefinitions); await tempTable.init(executionContext); return tempTable; } async onDispose() { this.log.trace(this._initExecutionContext, "FSqlConnectionPostgres disposing..."); await this._disposer(); this.log.trace(this._initExecutionContext, "FSqlConnectionPostgres disposed"); } } class FSqlStatementPostgres { _sqlText; _owner; constructor(owner, sqlText) { this._owner = owner; this._sqlText = sqlText; } async execute(executionContext, ...values) { await helpers.executeRunQuery(executionContext, this._owner.pgClient, this._sqlText, helpers.statementArgumentsAdapter(values)); } async executeQuery(executionContext, ...values) { const underlyingResult = await helpers.executeRunQuery(executionContext, this._owner.pgClient, this._sqlText, helpers.statementArgumentsAdapter(values)); const underlyingResultRows = underlyingResult.rows; const underlyingResultFields = underlyingResult.fields; if (underlyingResultFields[0].dataTypeID === PostgresObjectID.refcursor) { throw new FExceptionInvalidOperation("executeQuery: does not support multiset request yet"); } if (underlyingResultRows.length > 0 && !(underlyingResultFields[0].dataTypeID === PostgresObjectID.void)) { return underlyingResultRows.map(row => new FSqlResultRecordPostgres(row, underlyingResultFields)); } else { return []; } } // tslint:disable-next-line:max-line-length async executeQueryMultiSets(executionContext, ...values) { const cancellationToken = FCancellationExecutionContext.of(executionContext).cancellationToken; // Executing: BEGIN await helpers.executeRunQuery(executionContext, this._owner.pgClient, "BEGIN TRANSACTION", []); try { cancellationToken.throwIfCancellationRequested(); const resultFetchs = await helpers.executeRunQuery(executionContext, this._owner.pgClient, this._sqlText, helpers.statementArgumentsAdapter(values)); cancellationToken.throwIfCancellationRequested(); // Verify that this is a multi-request if (resultFetchs.fields[0].dataTypeID !== PostgresObjectID.refcursor) { // This is not a multi request. Raise exception. const trimmedSqlText = helpers.trimSqlTextForException(this._sqlText); throw new FExceptionInvalidOperation(`executeQueryMultiSets: cannot execute this script: ${trimmedSqlText}`); } const resultFetchsValue = helpers.parsingValue(resultFetchs); const friendlyResult = []; for (let i = 0; i < resultFetchsValue.length; i++) { const fetch = resultFetchsValue[i]; const queryFetchs = await helpers.executeRunQuery(executionContext, this._owner.pgClient, `FETCH ALL IN "${fetch}";`, []); cancellationToken.throwIfCancellationRequested(); friendlyResult.push(queryFetchs.rows.map(row => new FSqlResultRecordPostgres(row, queryFetchs.fields))); } // Executing: COMMIT await helpers.executeRunQuery(executionContext, this._owner.pgClient, "COMMIT", []); return friendlyResult; } catch (e) { // Executing: ROLLBACK await helpers.executeRunQuery(executionContext, this._owner.pgClient, "ROLLBACK", []); throw e; } } async executeScalar(executionContext, ...values) { const underlyingResult = await helpers.executeRunQuery(executionContext, this._owner.pgClient, this._sqlText, helpers.statementArgumentsAdapter(values)); const underlyingRows = underlyingResult.rows; if (underlyingRows.length === 0) { const trimmedSqlText = helpers.trimSqlTextForException(this._sqlText); throw new FSqlExceptionNoSuchRecord(`executeScalar: No record for query ${trimmedSqlText}`); } const underlyingFields = underlyingResult.fields; if (underlyingFields.length === 0) { throw new FExceptionInvalidOperation("executeScalar: SQL query returns no result"); } if (underlyingFields[0].dataTypeID === PostgresObjectID.refcursor) { throw new FExceptionInvalidOperation("executeScalar: does not support multiset request yet"); } const underlyingFirstRow = underlyingRows[0]; const value = underlyingFirstRow[Object.keys(underlyingFirstRow)[0]]; const fi = underlyingFields[0]; if (value !== undefined || fi !== undefined) { return new FSqlDataPostgres(value, fi); } else { throw new FSqlException(`executeScalar: Bad argument ${value} and ${fi}`); } } async executeScalarOrNull(executionContext, ...values) { const underlyingResult = await helpers.executeRunQuery(executionContext, this._owner.pgClient, this._sqlText, helpers.statementArgumentsAdapter(values)); const underlyingRows = underlyingResult.rows; const underlyingFields = underlyingResult.fields; if (underlyingRows.length > 0) { if (underlyingFields[0].dataTypeID === PostgresObjectID.refcursor) { throw new FExceptionInvalidOperation("executeScalarOrNull: does not support multiset request yet"); } const underlyingFirstRow = underlyingRows[0]; const value = underlyingFirstRow[Object.keys(underlyingFirstRow)[0]]; const fi = underlyingFields[0]; if (value !== undefined || fi !== undefined) { return new FSqlDataPostgres(value, fi); } else { throw new FSqlException(`executeScalarOrNull: Bad argument ${value} and ${fi}`); } } else { return null; } } async executeSingle(executionContext, ...values) { const underlyingResult = await helpers.executeRunQuery(executionContext, this._owner.pgClient, this._sqlText, helpers.statementArgumentsAdapter(values)); const underlyingResultRows = underlyingResult.rows; const underlyingResultFields = underlyingResult.fields; if (underlyingResultFields.length === 0) { throw new FExceptionInvalidOperation("executeSingle: SQL query returns no result"); } if (underlyingResultFields[0].dataTypeID === PostgresObjectID.refcursor) { throw new FExceptionInvalidOperation("executeSingle: does not support multi request"); } if (underlyingResultRows.length === 0) { const trimmedSqlText = helpers.trimSqlTextForException(this._sqlText); throw new FSqlExceptionNoSuchRecord(`executeSingle: No record for query ${trimmedSqlText}`); } else if (underlyingResultRows.length === 1 && !(underlyingResultFields[0].dataTypeID === PostgresObjectID.void)) { return new FSqlResultRecordPostgres(underlyingResultRows[0], underlyingResultFields); } else { throw new FExceptionInvalidOperation(`executeSingle: SQL query returns non-single result`); } } async executeSingleOrNull(executionContext, ...values) { const underlyingResult = await helpers.executeRunQuery(executionContext, this._owner.pgClient, this._sqlText, helpers.statementArgumentsAdapter(values)); const underlyingResultRows = underlyingResult.rows; const underlyingResultFields = underlyingResult.fields; if (underlyingResultFields.length === 0) { throw new FExceptionInvalidOperation("executeSingleOrNull: SQL query returns no result"); } if (underlyingResultFields[0].dataTypeID === PostgresObjectID.refcursor) { throw new FExceptionInvalidOperation("executeSingleOrNull: does not support multi request"); } if (underlyingResultRows.length === 0) { return null; } else if (underlyingResultRows.length === 1 && !(underlyingResultFields[0].dataTypeID === PostgresObjectID.void)) { return new FSqlResultRecordPostgres(underlyingResultRows[0], underlyingResultFields); } else { throw new FExceptionInvalidOperation("executeSingleOrNull: SQL query returns non-single result"); } } } class FSqlResultRecordPostgres { _fieldsData; _fieldsInfo; _nameMap; constructor(fieldsData, fieldsInfo) { if (Object.keys(fieldsData).length !== fieldsInfo.length) { throw new Error("Internal error. Fields count is not equal to data columns."); } this._fieldsData = fieldsData; this._fieldsInfo = fieldsInfo; } get(nameOrIndex) { if (typeof nameOrIndex === "string") { return this.getByName(nameOrIndex); } else { return this.getByIndex(nameOrIndex); } } get nameMap() { if (this._nameMap === undefined) { const nameMap = {}; const total = this._fieldsInfo.length; for (let index = 0; index < total; ++index) { const fi = this._fieldsInfo[index]; if (fi.name in nameMap) { throw new Error("Cannot access FSqlResultRecord by name due result set has name duplicates"); } nameMap[fi.name] = fi; } this._nameMap = nameMap; } return this._nameMap; } getByIndex(index) { const fi = this._fieldsInfo[index]; if (fi === undefined) { throw new FExceptionArgument(`PostgresSqlResultRecord does not have field with index '${index}'`, "index"); } const value = this._fieldsData[fi.name]; return new FSqlDataPostgres(value, fi); } getByName(name) { const fi = this.nameMap[name]; if (fi === undefined) { throw new FExceptionArgument(`PostgresSqlResultRecord does not have field with name '${name}'`, "name"); } const value = this._fieldsData[fi.name]; return new FSqlDataPostgres(value, fi); } } class FSqlTemporaryTablePostgres extends FInitableBase { _owner; _executionContext; _tableName; _columnsDefinitions; constructor(owner, executionContext, tableName, columnsDefinitions) { super(); this._owner = owner; this._executionContext = executionContext; this._tableName = tableName; this._columnsDefinitions = columnsDefinitions; } bulkInsert(executionContext, bulkValues) { return this._owner.statement(`INSERT INTO \`${this._tableName}\``).execute(executionContext, bulkValues); } clear(executionContext) { return this._owner.statement(`DELETE FROM \`${this._tableName}\``).execute(executionContext); } insert(executionContext, values) { return this._owner.statement(`INSERT INTO \`${this._tableName}\``).execute(executionContext, ...values); } async onInit() { await this._owner.statement(`CREATE TEMPORARY TABLE ${this._tableName} (${this._columnsDefinitions})`).execute(this._executionContext); } async onDispose() { try { await this._owner.statement(`DROP TABLE ${this._tableName}`).execute(this._executionContext); } catch (e) { // dispose never raise error if (e instanceof FCancellationException) { return; // skip error message if task was cancelled } // Dispose never raise errors console.error(e); // we cannot do anymore here, just log } } } class FSqlDataPostgres { _postgresValue; _fi; get asBoolean() { if (typeof this._postgresValue === "boolean") { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asBoolean")); } } get asBooleanNullable() { if (this._postgresValue === null) { return null; } else if (typeof this._postgresValue === "boolean") { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asBooleanNullable")); } } get asString() { if (this._postgresValue === null) { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asString")); } else if (typeof this._postgresValue === "string") { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asString")); } } get asStringNullable() { if (this._postgresValue === null) { return null; } else if (typeof this._postgresValue === "string") { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asStringNullable")); } } get asInteger() { if (this._postgresValue === null) { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asInteger")); } else if (typeof this._postgresValue === "number" && Number.isInteger(this._postgresValue)) { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asInteger")); } } get asIntegerNullable() { if (this._postgresValue === null) { return null; } else if (typeof this._postgresValue === "number" && Number.isInteger(this._postgresValue)) { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asIntegerNullable")); } } get asNumber() { if (this._postgresValue === null) { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asNumber")); } else if (typeof this._postgresValue === "number") { return this._postgresValue; } else if (this._fi.dataTypeID === PostgresObjectID.numeric && typeof this._postgresValue === "string") { return Number.parseFloat(this._postgresValue); } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asNumber")); } } get asNumberNullable() { if (this._postgresValue === null) { return null; } else if (typeof this._postgresValue === "number") { return this._postgresValue; } else if (this._fi.dataTypeID === PostgresObjectID.numeric && typeof this._postgresValue === "string") { return Number.parseFloat(this._postgresValue); } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asNumberNullable")); } } get asDecimal() { if (this._postgresValue === null) { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asDecimal")); } else if (typeof this._postgresValue === "number") { return FDecimal.fromFloat(this._postgresValue); } else if (typeof this._postgresValue === "string") { return FDecimal.parse(this._postgresValue); } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asDecimal")); } } get asDecimalNullable() { if (this._postgresValue === null) { return null; } else if (typeof this._postgresValue === "number") { return FDecimal.fromFloat(this._postgresValue); } else if (typeof this._postgresValue === "string") { return FDecimal.parse(this._postgresValue); } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asDecimalNullable")); } } get asDate() { if (this._postgresValue === null) { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asDate")); // } else if (this._fi.dataTypeID === PostgresObjectID.timestamp && this._postgresValue instanceof Date) { // // `pg` library make Date with local zone shift, so we need to make opposite changes to retrieve correct date from UTC timestamp // return new Date(this._postgresValue.getTime() - this._postgresValue.getTimezoneOffset() * 60000); } else if (this._fi.dataTypeID === PostgresObjectID.timestamp && typeof this._postgresValue === "string") { // `pg` library make Date with local zone shift, so we need to make opposite changes to retrieve correct date from UTC timestamp return new Date(`${this._postgresValue}+0000`); } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asDate", `Right now the library supports TIMESTAMP WITHOUT TIME ZONE OID=${PostgresObjectID.timestamp} only. Got OID=${this._fi.dataTypeID}.`)); } } get asDateNullable() { if (this._postgresValue === null) { return null; // } else if (this._fi.dataTypeID === PostgresObjectID.timestamp && this._postgresValue instanceof Date) { // // `pg` library make Date with local zone shift, so we need to make opposite changes to retrieve correct date from UTC timestamp // return new Date(this._postgresValue.getTime() - this._postgresValue.getTimezoneOffset() * 60000); } else if (this._fi.dataTypeID === PostgresObjectID.timestamp && typeof this._postgresValue === "string") { // `pg` library make Date with local zone shift, so we need to make opposite changes to retrieve correct date from UTC timestamp return new Date(`${this._postgresValue}+0000`); } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asDateNullable", `Right now the library supports TIMESTAMP WITHOUT TIME ZONE OID=${PostgresObjectID.timestamp} only. Got OID=${this._fi.dataTypeID}.`)); } } get asBinary() { if (this._postgresValue === null) { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asBinary")); } else if (this._postgresValue instanceof Uint8Array) { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asBinary")); } } get asBinaryNullable() { if (this._postgresValue === null) { return null; } else if (this._postgresValue instanceof Uint8Array) { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asBinaryNullable")); } } get asObject() { if (this._postgresValue === null) { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asObject")); } else if (this._fi.dataTypeID === PostgresObjectID.jsonb) { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asObject", `Right now the library supports Binary JSON (OID=${PostgresObjectID.jsonb}) only.`)); } } get asObjectNullable() { if (this._postgresValue === null) { return null; } else if (this._fi.dataTypeID === PostgresObjectID.jsonb) { return this._postgresValue; } else { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asObjectNullable")); } } get asStringArray() { if (this._postgresValue === null) { throw new FExceptionInvalidOperation(this.formatWrongDataTypeMessage("asStringArray")); }