postgrejs
Version:
Professional PostgreSQL client NodeJS
227 lines (226 loc) • 9.23 kB
JavaScript
;
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;