Files
lcbp3.np-dms.work/backend/node_modules/mariadb/lib/cmd/prepare.js
2025-09-21 20:29:15 +07:00

171 lines
5.9 KiB
JavaScript
Executable File

// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (c) 2015-2024 MariaDB Corporation Ab
'use strict';
const Parser = require('./parser');
const Parse = require('../misc/parse');
const BinaryEncoder = require('./encoder/binary-encoder');
const PrepareCacheWrapper = require('./class/prepare-cache-wrapper');
const PrepareResult = require('./class/prepare-result-packet');
const ServerStatus = require('../const/server-status');
const Errors = require('../misc/errors');
const ColumnDefinition = require('./column-definition');
/**
* send a COM_STMT_PREPARE: permits sending a prepare packet
* see https://mariadb.com/kb/en/com_stmt_prepare/
*/
class Prepare extends Parser {
constructor(resolve, reject, connOpts, cmdParam, conn) {
super(resolve, reject, connOpts, cmdParam);
this.encoder = new BinaryEncoder(this.opts);
this.binary = true;
this.conn = conn;
this.executeCommand = cmdParam.executeCommand;
}
/**
* Send COM_STMT_PREPARE
*
* @param out output writer
* @param opts connection options
* @param info connection information
*/
start(out, opts, info) {
// check in cache if enabled
if (this.conn.prepareCache) {
let cachedPrepare = this.conn.prepareCache.get(this.sql);
if (cachedPrepare) {
this.emit('send_end');
return this.successEnd(cachedPrepare);
}
}
if (opts.logger.query) opts.logger.query(`PREPARE: ${this.sql}`);
this.onPacketReceive = this.readPrepareResultPacket;
if (this.opts.namedPlaceholders) {
const res = Parse.searchPlaceholder(this.sql);
this.sql = res.sql;
this.placeHolderIndex = res.placeHolderIndex;
}
out.startPacket(this);
out.writeInt8(0x16);
out.writeString(this.sql);
out.flush();
this.emit('send_end');
}
successPrepare(info, opts) {
let prepare = new PrepareResult(
this.statementId,
this.parameterCount,
this._columns,
info.database,
this.sql,
this.placeHolderIndex,
this.conn
);
if (this.conn.prepareCache) {
let cached = new PrepareCacheWrapper(prepare);
this.conn.prepareCache.set(this.sql, cached);
const cachedWrappedPrepared = cached.incrementUse();
if (this.executeCommand) this.executeCommand.prepare = cachedWrappedPrepared;
return this.successEnd(cachedWrappedPrepared);
}
if (this.executeCommand) this.executeCommand.prepare = prepare;
this.successEnd(prepare);
}
/**
* Read COM_STMT_PREPARE response Packet.
* see https://mariadb.com/kb/en/library/com_stmt_prepare/#com_stmt_prepare-response
*
* @param packet COM_STMT_PREPARE_OK packet
* @param opts connection options
* @param info connection information
* @param out output writer
* @returns {*} null or {Result.readResponsePacket} in case of multi-result-set
*/
readPrepareResultPacket(packet, out, opts, info) {
switch (packet.peek()) {
//*********************************************************************************************************
//* PREPARE response
//*********************************************************************************************************
case 0x00:
packet.skip(1); //skip header
this.statementId = packet.readInt32();
this.columnNo = packet.readUInt16();
this.parameterCount = packet.readUInt16();
this._parameterNo = this.parameterCount;
this._columns = [];
if (this._parameterNo > 0) return (this.onPacketReceive = this.skipPrepareParameterPacket);
if (this.columnNo > 0) return (this.onPacketReceive = this.readPrepareColumnsPacket);
return this.successPrepare(info, opts);
//*********************************************************************************************************
//* ERROR response
//*********************************************************************************************************
case 0xff:
const err = packet.readError(info, this.displaySql(), this.stack);
//force in transaction status, since query will have created a transaction if autocommit is off
//goal is to avoid unnecessary COMMIT/ROLLBACK.
info.status |= ServerStatus.STATUS_IN_TRANS;
this.onPacketReceive = this.readResponsePacket;
return this.throwError(err, info);
//*********************************************************************************************************
//* Unexpected response
//*********************************************************************************************************
default:
info.status |= ServerStatus.STATUS_IN_TRANS;
this.onPacketReceive = this.readResponsePacket;
return this.throwError(Errors.ER_UNEXPECTED_PACKET, info);
}
}
readPrepareColumnsPacket(packet, out, opts, info) {
this.columnNo--;
this._columns.push(new ColumnDefinition(packet, info, opts.rowsAsArray));
if (this.columnNo === 0) {
if (info.eofDeprecated) {
return this.successPrepare(info, opts);
}
this.onPacketReceive = this.skipEofPacket;
}
}
skipEofPacket(packet, out, opts, info) {
if (this.columnNo > 0) return (this.onPacketReceive = this.readPrepareColumnsPacket);
this.successPrepare(info, opts);
}
skipPrepareParameterPacket(packet, out, opts, info) {
this._parameterNo--;
if (this._parameterNo === 0) {
if (info.eofDeprecated) {
if (this.columnNo > 0) return (this.onPacketReceive = this.readPrepareColumnsPacket);
return this.successPrepare(info, opts);
}
this.onPacketReceive = this.skipEofPacket;
}
}
/**
* Display current SQL with parameters (truncated if too big)
*
* @returns {string}
*/
displaySql() {
if (this.opts) {
if (this.sql.length > this.opts.debugLen) {
return this.sql.substring(0, this.opts.debugLen) + '...';
}
}
return this.sql;
}
}
module.exports = Prepare;