Skip to content

Commit

Permalink
[CONJS-240] ensuring PREPARE state when caching
Browse files Browse the repository at this point in the history
  • Loading branch information
rusher committed Feb 7, 2023
1 parent ba70356 commit 65327c7
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 168 deletions.
35 changes: 0 additions & 35 deletions lib/cmd/class/cached-prepare-result-packet.js

This file was deleted.

43 changes: 43 additions & 0 deletions lib/cmd/class/prepare-cache-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

const PrepareWrapper = require('./prepare-wrapper');

/**
* Prepare cache wrapper
* see https://mariadb.com/kb/en/com_stmt_prepare/#com_stmt_prepare_ok
*/
class PrepareCacheWrapper {
#use = 0;
#cached;
#prepare;

constructor(prepare) {
this.#prepare = prepare;
this.#cached = true;
}

incrementUse() {
this.#use += 1;
return new PrepareWrapper(this, this.#prepare);
}

unCache() {
this.#cached = false;
if (this.#use === 0) {
this.#prepare.close();
}
}

decrementUse() {
this.#use -= 1;
if (this.#use === 0 && !this.#cached) {
this.#prepare.close();
}
}

toString() {
return 'Prepare{use:' + this.#use + ',cached:' + this.#cached + '}';
}
}

module.exports = PrepareCacheWrapper;
25 changes: 18 additions & 7 deletions lib/cmd/class/prepare-result-packet.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ class PrepareResultPacket {
this.#conn = conn;
}

get conn() {
return this.#conn;
}

execute(values, opts, cb, stack) {
let _opts = opts,
_cb = cb;
Expand All @@ -29,7 +33,7 @@ class PrepareResultPacket {
_opts = undefined;
}

if (this.closed) {
if (this.isClose()) {
const error = Errors.createError(
`Execute fails, prepare command as already been closed`,
Errors.ER_PREPARE_CLOSED,
Expand All @@ -46,9 +50,9 @@ class PrepareResultPacket {
}
}

const cmdParam = new CommandParameter(this.query, values, _opts, cb);
const cmdParam = new CommandParameter(this.query, values, _opts, _cb);
if (stack) cmdParam.stack = stack;
const conn = this.#conn;
const conn = this.conn;
const promise = new Promise((resolve, reject) => conn.executePromise.call(conn, cmdParam, this, resolve, reject));
if (!_cb) {
return promise;
Expand All @@ -70,7 +74,7 @@ class PrepareResultPacket {
_opts = undefined;
}

if (this.closed) {
if (this.isClose()) {
const error = Errors.createError(
`Execute fails, prepare command as already been closed`,
Errors.ER_PREPARE_CLOSED,
Expand All @@ -90,18 +94,25 @@ class PrepareResultPacket {
const cmdParam = new CommandParameter(this.query, values, _opts, cb);
if (stack) cmdParam.stack = stack;

const cmd = new ExecuteStream(cmdParam, this.#conn.opts, this, this.#conn.socket);
if (this.#conn.opts.logger.error) cmd.on('error', this.#conn.opts.logger.error);
this.#conn.addCommand(cmd);
const cmd = new ExecuteStream(cmdParam, this.conn.opts, this, this.conn.socket);
if (this.conn.opts.logger.error) cmd.on('error', this.conn.opts.logger.error);
this.conn.addCommand(cmd);
return cmd.inStream;
}

isClose() {
return this.closed;
}

close() {
if (!this.closed) {
this.closed = true;
this.#conn.emit('close_prepare', this);
}
}
toString() {
return 'Prepare{closed:' + this.closed + '}';
}
}

module.exports = PrepareResultPacket;
67 changes: 67 additions & 0 deletions lib/cmd/class/prepare-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict';

/**
* Prepare result wrapper
* This permit to ensure that cache can be close only one time cache.
*/
class PrepareWrapper {
#closed = false;
#cacheWrapper;
#prepare;
#conn;

constructor(cacheWrapper, prepare) {
this.#cacheWrapper = cacheWrapper;
this.#prepare = prepare;
this.#conn = prepare.conn;
this.execute = this.#prepare.execute;
this.executeStream = this.#prepare.executeStream;
}
get conn() {
return this.#conn;
}

get id() {
return this.#prepare.id;
}

get parameterCount() {
return this.#prepare.parameterCount;
}

get _placeHolderIndex() {
return this.#prepare._placeHolderIndex;
}

get columns() {
return this.#prepare.columns;
}

set columns(columns) {
this.#prepare.columns = columns;
}
get database() {
return this.#prepare.database;
}

get query() {
return this.#prepare.query;
}

isClose() {
return this.#closed;
}

close() {
if (!this.#closed) {
this.#closed = true;
this.#cacheWrapper.decrementUse();
}
}

toString() {
return 'PrepareWrapper{closed:' + this.#closed + ',cache:' + this.#cacheWrapper + '}';
}
}

module.exports = PrepareWrapper;
56 changes: 22 additions & 34 deletions lib/cmd/prepare.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const Parser = require('./parser');
const Parse = require('../misc/parse');
const BinaryEncoder = require('./encoder/binary-encoder');
const CachedPrepareResultPacket = require('./class/cached-prepare-result-packet');
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');
Expand All @@ -29,13 +29,11 @@ class Prepare extends Parser {
*/
start(out, opts, info) {
// check in cache if enabled
if (info._prepareCache) {
const key = info.database + '|' + this.sql;
const cachedItem = info._prepareCache.get(key);
if (cachedItem) {
cachedItem.incrementUse();
if (this.conn.prepareCache) {
let cachedPrepare = this.conn.prepareCache.get(this.sql);
if (cachedPrepare) {
this.emit('send_end');
return this.successEnd(cachedItem);
return this.successEnd(cachedPrepare);
}
}
if (opts.logger.query) opts.logger.query(`PREPARE: ${this.sql}`);
Expand All @@ -55,32 +53,22 @@ class Prepare extends Parser {
}

successPrepare(info, opts) {
let prepare;
if (info._prepareCache) {
const key = info.database + '|' + this.sql;
prepare = new CachedPrepareResultPacket(
this.statementId,
this.parameterCount,
this._columnsPrepare,
info.database,
this.sql,
this.placeHolderIndex,
this.conn
);
info._prepareCache.set(key, prepare);
} else {
prepare = new PrepareResult(
this.statementId,
this.parameterCount,
this._columnsPrepare,
info.database,
this.sql,
this.placeHolderIndex,
this.conn
);
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);
return this.successEnd(cached.incrementUse());
}
this._columnsPrepare = null;
return this.success(prepare);
return this.successEnd(prepare);
}

/**
Expand All @@ -104,7 +92,7 @@ class Prepare extends Parser {
this.columnNo = packet.readUInt16();
this.parameterCount = packet.readUInt16();
this._parameterNo = this.parameterCount;
this._columnsPrepare = [];
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);
Expand Down Expand Up @@ -132,7 +120,7 @@ class Prepare extends Parser {

readPrepareColumnsPacket(packet, out, opts, info) {
this.columnNo--;
this._columnsPrepare.push(new ColumnDefinition(packet, info, opts.rowsAsArray));
this._columns.push(new ColumnDefinition(packet, info, opts.rowsAsArray));
if (this.columnNo === 0) {
if (info.eofDeprecated) {
return this.successPrepare(info, opts);
Expand Down
10 changes: 4 additions & 6 deletions lib/connection-promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class ConnectionPromise {
return this.#conn.info;
}

get prepareCache() {
return this.#conn.prepareCache;
}

/**
* Permit to change user during connection.
* All user variables will be reset, Prepare commands will be released.
Expand Down Expand Up @@ -107,12 +111,6 @@ class ConnectionPromise {
}

static _EXECUTE_CMD(conn, cmdParam) {
let prepareFromCache;
if ((prepareFromCache = conn.info.prepareFromCache(cmdParam.sql)) != null) {
return prepareFromCache
.execute(cmdParam.values, cmdParam.opts, null, cmdParam.stack)
.finally(() => prepareFromCache.close());
}
return new Promise(conn.prepare.bind(conn, cmdParam))
.then((prepare) => {
return prepare.execute(cmdParam.values, cmdParam.opts, null, cmdParam.stack).finally(() => prepare.close());
Expand Down
Loading

0 comments on commit 65327c7

Please sign in to comment.