UNPKG

postgrejs

Version:

Professional PostgreSQL client NodeJS

227 lines (226 loc) 9.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PreparedStatement = void 0; const putil_varhelpers_1 = require("putil-varhelpers"); const constants_js_1 = require("../constants.js"); const data_type_map_js_1 = require("../data-type-map.js"); const protocol_js_1 = require("../protocol/protocol.js"); const safe_event_emitter_js_1 = require("../safe-event-emitter.js"); const convert_row_to_object_js_1 = require("../util/convert-row-to-object.js"); const get_parsers_js_1 = require("../util/get-parsers.js"); const parse_row_js_1 = require("../util/parse-row.js"); const wrap_row_description_js_1 = require("../util/wrap-row-description.js"); const cursor_js_1 = require("./cursor.js"); const intl_connection_js_1 = require("./intl-connection.js"); const portal_js_1 = require("./portal.js"); let statementCounter = 0; let portalCounter = 0; class PreparedStatement extends safe_event_emitter_js_1.SafeEventEmitter { constructor(connection, sql, paramTypes) { super(); this._sql = ''; this._name = ''; this._refCount = 0; this._connection = connection; this._name = 'S_' + ++statementCounter; this._sql = sql; this._paramTypes = paramTypes; this._onErrorSavePoint = 'SP_' + Math.round(Math.random() * 100000000); } static async prepare(connection, sql, options) { const intoCon = (0, intl_connection_js_1.getIntlConnection)(connection); intoCon.assertConnected(); const socket = intoCon.socket; const statement = new PreparedStatement(connection, sql, options?.paramTypes); await intoCon.statementQueue .enqueue(async () => { intoCon.ref(); try { socket.sendParseMessage({ statement: statement.name, sql: statement.sql, paramTypes: statement.paramTypes, }); socket.sendFlushMessage(); try { await socket.capture(async (code, msg, done) => { if (code === protocol_js_1.Protocol.BackendMessageCode.ParseComplete) done(); // May be Protocol.BackendMessageCode.NoticeResponse }); } finally { socket.sendSyncMessage(); await socket.capture(async (code, msg, done) => { if (code === protocol_js_1.Protocol.BackendMessageCode.ReadyForQuery) done(); // May be Protocol.BackendMessageCode.NoticeResponse }); } } finally { intoCon.unref(); } }) .toPromise(); statement._refCount = 1; return statement; } get connection() { return this._connection; } get name() { return this._name; } get sql() { return this._sql; } get paramTypes() { return this._paramTypes; } async execute(options = {}) { const intlCon = (0, intl_connection_js_1.getIntlConnection)(this.connection); const transactionCommand = this.sql.match(/^(\bBEGIN\b|\bCOMMIT\b|\bSTART\b|\bROLLBACK|SAVEPOINT|RELEASE\b)/i); let beginFirst = false; let commitLast = false; if (!transactionCommand) { if (!intlCon.inTransaction && (options?.autoCommit != null ? options?.autoCommit : intlCon.config.autoCommit) === false) { beginFirst = true; } if (intlCon.inTransaction && options?.autoCommit) commitLast = true; } if (beginFirst) await intlCon.execute('BEGIN'); const rollbackOnError = !transactionCommand && (options?.rollbackOnError != null ? options.rollbackOnError : (0, putil_varhelpers_1.coerceToBoolean)(intlCon.config.rollbackOnError, true)); if (intlCon.inTransaction && rollbackOnError) await intlCon.execute('SAVEPOINT ' + this._onErrorSavePoint); try { const result = await intlCon.statementQueue .enqueue(() => this._execute(options)) .toPromise(); if (commitLast) await intlCon.execute('COMMIT'); else if (intlCon.inTransaction && rollbackOnError) { await intlCon.execute('RELEASE ' + this._onErrorSavePoint + ';'); } return result; } catch (e) { if (intlCon.inTransaction && rollbackOnError) { await intlCon.execute('ROLLBACK TO ' + this._onErrorSavePoint + ';'); } throw e; } } async close() { --this._refCount; if (this._refCount > 0) return; const intoCon = (0, intl_connection_js_1.getIntlConnection)(this.connection); await intoCon.statementQueue.enqueue(() => this._close()).toPromise(); } async cancel() { throw new Error('Not implemented yet'); } async _execute(options = {}) { let portal; const intlCon = (0, intl_connection_js_1.getIntlConnection)(this.connection); intlCon.ref(); try { const result = { command: undefined }; const startTime = Date.now(); const t = Date.now(); // Create portal const portalName = 'P_' + ++portalCounter; portal = new portal_js_1.Portal(this, portalName); await portal.bind(options.params, options); const fields = await portal.retrieveFields(); const typeMap = options.typeMap || data_type_map_js_1.GlobalTypeMap; let parsers; let resultFields; if (fields) { parsers = (0, get_parsers_js_1.getParsers)(typeMap, fields); resultFields = (0, wrap_row_description_js_1.wrapRowDescription)(typeMap, fields, options.columnFormat || constants_js_1.DEFAULT_COLUMN_FORMAT); result.fields = resultFields; result.rowType = options.objectRows ? 'object' : 'array'; if (options.cursor) { result.cursor = new cursor_js_1.Cursor(this, portal, resultFields, parsers, options); this._refCount++; portal = undefined; return result; } } const executeResult = await portal.execute(options.fetchCount); result.executeTime = Date.now() - t; if (executeResult.command) result.command = executeResult.command; if (resultFields && parsers && executeResult.rows) { if (!result.command) result.command = 'SELECT'; const rows = (result.rows = executeResult.rows); const l = rows.length; let row; for (let i = 0; i < l; i++) { row = rows[i]; (0, parse_row_js_1.parseRow)(parsers, row, options); if (options.objectRows) { rows[i] = (0, convert_row_to_object_js_1.convertRowToObject)(resultFields, row); } } } if (result.command === 'DELETE' || result.command === 'INSERT' || result.command === 'UPDATE') { result.rowsAffected = executeResult.rowCount; } result.executeTime = Date.now() - startTime; return result; } finally { intlCon.unref(); if (portal) await portal.close(); } } async _close() { if (--this._refCount > 0) return; const intoCon = (0, intl_connection_js_1.getIntlConnection)(this.connection); intoCon.ref(); try { const socket = intoCon.socket; socket.sendCloseMessage({ type: 'S', name: this.name }); socket.sendSyncMessage(); await socket.capture(async (code, msg, done) => { switch (code) { case protocol_js_1.Protocol.BackendMessageCode.NoticeResponse: this.emit('notice', msg); break; case protocol_js_1.Protocol.BackendMessageCode.CloseComplete: break; case protocol_js_1.Protocol.BackendMessageCode.ReadyForQuery: intoCon.transactionStatus = msg.status; done(); break; default: done(new Error(`Server returned unexpected response message (0x${code.toString(16)})`)); } }); } finally { intoCon.unref(); } this.emit('close'); } [Symbol.asyncDispose]() { return this.close(); } } exports.PreparedStatement = PreparedStatement;