diff --git a/.travis.yml b/.travis.yml index 8f40f588..49e76881 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,7 +52,6 @@ jobs: fast_finish: true allow_failures: - env: srv=build - - env: srv=xpand RUN_LONG_TEST=0 include: - stage: Minimal env: srv=mariadb v=10.6 local=1 diff --git a/CHANGELOG.md b/CHANGELOG.md index f8731642..f18d9227 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Change Log -## [3.1.0](https://github.com/mariadb-corporation/mariadb-connector-nodejs/tree/3.0.2) (Feb 2023) +## [3.1.1](https://github.com/mariadb-corporation/mariadb-connector-nodejs/tree/3.1.1) (Mar 2023) +[Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-nodejs/compare/3.1.0...3.1.1) + +## Issues Fixed +* CONJS-246 pool not listening to 'error' event might exit application on error +* CONJS-240 Repeating calling the same procedure gets a release prepare error. +* CONJS-244 correction for node.js 12 compatibility +* CONJS-245 batch failing when using bulk and metaAsArray + + +## [3.1.0](https://github.com/mariadb-corporation/mariadb-connector-nodejs/tree/3.1.0) (Feb 2023) [Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-nodejs/compare/3.0.2...3.1.0) ## Notable changes diff --git a/callback.js b/callback.js index 52d2c277..69199ed5 100644 --- a/callback.js +++ b/callback.js @@ -41,7 +41,10 @@ module.exports.createConnection = function createConnection(opts) { exports.createPool = function createPool(opts) { const options = new PoolOptions(opts); - return new PoolCallback(options); + const pool = new PoolCallback(options); + // adding a default error handler to avoid exiting application on connection error. + pool.on('error', (err) => {}); + return pool; }; exports.createPoolCluster = function createPoolCluster(opts) { diff --git a/lib/cluster.js b/lib/cluster.js index 288b7829..eb4a3fac 100644 --- a/lib/cluster.js +++ b/lib/cluster.js @@ -145,11 +145,15 @@ class Cluster extends EventEmitter { } _createPool(options) { - return new PoolPromise(options); + const pool = new PoolPromise(options); + pool.on('error', (err) => {}); + return pool; } _createPoolCallback(options) { - return new PoolCallback(options); + const pool = new PoolCallback(options); + pool.on('error', (err) => {}); + return pool; } /** diff --git a/lib/cmd/batch-bulk.js b/lib/cmd/batch-bulk.js index 23457d2e..31b4f919 100644 --- a/lib/cmd/batch-bulk.js +++ b/lib/cmd/batch-bulk.js @@ -372,7 +372,13 @@ class BatchBulk extends Parser { success(val) { this.bulkPacketNo--; - if (val instanceof OkPacket) this._rows.push(val); + + // fast path doesn't push OkPacket if ony one results + if (this._responseIndex === 0) { + if (this.opts.metaAsArray) { + if (val[0] instanceof OkPacket) this._rows.push(val[0]); + } else if (val instanceof OkPacket) this._rows.push(val); + } if (!this.sending && this.bulkPacketNo === 0) { this.packet = null; @@ -397,19 +403,37 @@ class BatchBulk extends Parser { this._rows[0].insertId, this._rows[this._rows.length - 1].warningStatus ); - this.successEnd(rs); + this.successEnd(this.opts.metaAsArray ? [rs, []] : rs); } else { - // insert with returning if (this._rows.length === 1) { - this.successEnd(this._rows[0]); + this.successEnd(this.opts.metaAsArray ? [this._rows[0], this._columns] : this._rows[0]); + } + if (this.opts.metaAsArray) { + if (this._rows.length === 1) { + this.successEnd([this._rows[0], this._columns]); + } else { + const rs = []; + this._rows.forEach((row) => { + rs.push(...row); + }); + this.successEnd([rs, this._columns]); + } } else { - const rs = []; - rs.meta = this._rows[0].meta; - this._rows.forEach((row) => { - Array.prototype.push.apply(rs, row); - }); - rs.meta = this._rows[0].meta; - this.successEnd(rs); + // insert with returning + if (this._rows.length === 1) { + this.successEnd(this._rows[0]); + } else { + const rs = []; + this._rows.forEach((row) => { + rs.push(...row); + }); + Object.defineProperty(rs, 'meta', { + value: this._columns, + writable: true, + enumerable: this.opts.metaEnumerable + }); + this.successEnd(rs); + } } } this._columns = null; diff --git a/lib/cmd/column-definition.js b/lib/cmd/column-definition.js index e3cf366c..3baf91d9 100644 --- a/lib/cmd/column-definition.js +++ b/lib/cmd/column-definition.js @@ -111,7 +111,7 @@ class BaseStringParser { this.readString = readFct; } - #readIdentifier(skip) { + _readIdentifier(skip) { let pos = 0; while (skip-- > 0) { const type = this.buf[pos++] & 0xff; @@ -152,11 +152,11 @@ class BaseStringParser { } name() { - return this.#readIdentifier(3); + return this._readIdentifier(3); } db() { - return this.#readIdentifier(0); + return this._readIdentifier(0); } schema() { @@ -164,15 +164,15 @@ class BaseStringParser { } table() { - return this.#readIdentifier(1); + return this._readIdentifier(1); } orgTable() { - return this.#readIdentifier(2); + return this._readIdentifier(2); } orgName() { - return this.#readIdentifier(4); + return this._readIdentifier(4); } } diff --git a/lib/cmd/parser.js b/lib/cmd/parser.js index 2839d3d9..3a612e7b 100644 --- a/lib/cmd/parser.js +++ b/lib/cmd/parser.js @@ -179,7 +179,7 @@ class Parser extends Command { this._responseIndex++; return (this.onPacketReceive = this.readResponsePacket); } - return this.success(this.opts.metaAsArray ? [okPacket, [null]] : okPacket); + return this.success(this.opts.metaAsArray ? [okPacket, []] : okPacket); } this._rows.push(okPacket); diff --git a/lib/cmd/query.js b/lib/cmd/query.js index da340f77..cc1362b0 100644 --- a/lib/cmd/query.js +++ b/lib/cmd/query.js @@ -89,7 +89,7 @@ class Query extends Parser { // param is stream, // now all params will be written by event //******************************************** - this.paramWritten = this.#paramWritten.bind(this, out, info); + this.paramWritten = this._paramWritten.bind(this, out, info); out.writeInt8(QUOTE); //' value.on('data', out.writeBufferEscape.bind(out)); @@ -128,7 +128,7 @@ class Query extends Parser { return true; } else { const err = Errors.createError( - `Cannot use timeout for MariaDB server before 10.1.2. timeout value: ${this.opts.timeout}`, + `Cannot use timeout for xpand/MariaDB server before 10.1.2. timeout value: ${this.opts.timeout}`, Errors.ER_TIMEOUT_NOT_SUPPORTED, info, 'HY000', @@ -194,7 +194,7 @@ class Query extends Parser { return true; } - #paramWritten(out, info) { + _paramWritten(out, info) { while (true) { if (this.valueIdx === this.paramPositions.length / 2) { //******************************************** @@ -225,7 +225,7 @@ class Query extends Parser { 'end', function () { out.writeInt8(QUOTE); - this.#paramWritten(out, info); + this._paramWritten(out, info); }.bind(this) ); value.on('data', out.writeBufferEscape.bind(out)); diff --git a/lib/connection.js b/lib/connection.js index 69e0d4d0..073143ab 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -205,6 +205,7 @@ class Connection extends EventEmitter { return new Promise(this.executeBulkPromise.bind(this, cmdParam, prepare, this.opts)); } else { const executes = []; + const cmdOpt = Object.assign({}, this.opts, cmdParam.opts); for (let i = 0; i < vals.length; i++) { executes.push(prepare.execute(vals[i], cmdParam.opts, null, cmdParam.stack)); } @@ -215,25 +216,44 @@ class Connection extends EventEmitter { return Promise.resolve(res); } else { // aggregate results - const firstResult = res[0]; + let firstResult = res[0]; + if (cmdOpt.metaAsArray) firstResult = firstResult[0]; if (firstResult instanceof OkPacket) { let affectedRows = 0; const insertId = firstResult.insertId; const warningStatus = firstResult.warningStatus; - for (let i = 0; i < res.length; i++) { - affectedRows += res[i].affectedRows; + if (cmdOpt.metaAsArray) { + for (let i = 0; i < res.length; i++) { + affectedRows += res[i][0].affectedRows; + } + return Promise.resolve([new OkPacket(affectedRows, insertId, warningStatus), []]); + } else { + for (let i = 0; i < res.length; i++) { + affectedRows += res[i].affectedRows; + } + return Promise.resolve(new OkPacket(affectedRows, insertId, warningStatus)); } - return Promise.resolve(new OkPacket(affectedRows, insertId, warningStatus)); } else { // results have result-set. example :'INSERT ... RETURNING' // aggregate results - const rs = []; - rs.meta = res.meta; - res.forEach((row) => { - Array.prototype.push.apply(rs, row); - }); - rs.meta = res.meta; - return Promise.resolve(rs); + if (cmdOpt.metaAsArray) { + const rs = []; + res.forEach((row) => { + rs.push(...row[0]); + }); + return Promise.resolve([rs, res[0][1]]); + } else { + const rs = []; + res.forEach((row) => { + rs.push(...row); + }); + Object.defineProperty(rs, 'meta', { + value: res[0].meta, + writable: true, + enumerable: this.opts.metaEnumerable + }); + return Promise.resolve(rs); + } } } }.bind(this) @@ -337,6 +357,7 @@ class Connection extends EventEmitter { const resetCmd = new Reset( cmdParam, () => { + conn.prepareCache.reset(); let prom = Promise.resolve(); // re-execute init query / session query timeout prom diff --git a/lib/lru-prepare-cache.js b/lib/lru-prepare-cache.js index 5b683d54..e63d0a58 100644 --- a/lib/lru-prepare-cache.js +++ b/lib/lru-prepare-cache.js @@ -38,6 +38,10 @@ class LruPrepareCache { if (keyStr.length > 1) keyStr = keyStr.substring(0, keyStr.length - 1); return 'info{cache:' + keyStr + '}'; } + + reset() { + this.#lruCache.clear(); + } } module.exports = LruPrepareCache; diff --git a/lib/pool-promise.js b/lib/pool-promise.js index 4ee85a87..a5dca617 100644 --- a/lib/pool-promise.js +++ b/lib/pool-promise.js @@ -82,16 +82,12 @@ class PoolPromise extends EventEmitter { if (this.#pool.opts.connOptions.trace) Error.captureStackTrace(cmdParam); const baseConn = await this.#pool.getConnection(cmdParam); const conn = new ConnectionPromise(baseConn); - conn.release = this.#releaseConnection.bind(this, baseConn); + conn.release = () => new Promise(baseConn.release); conn.end = conn.release; conn.close = conn.release; return conn; } - #releaseConnection(baseConn) { - return new Promise(baseConn.release); - } - /** * Execute query using text protocol with callback emit columns/data/end/error * events to permit streaming big result-set diff --git a/lib/pool.js b/lib/pool.js index 3442b8a5..2b7d88e3 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -28,7 +28,6 @@ class Pool extends EventEmitter { this.on('_idle', this._requestsHandler); this.on('validateSize', this._sizeHandler); - this._sizeHandler(); } diff --git a/package.json b/package.json index 2f661b56..5f094db5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mariadb", - "version": "3.1.0", + "version": "3.1.1", "description": "fast mariadb or mysql connector.", "main": "promise.js", "types": "types/index.d.ts", diff --git a/promise.js b/promise.js index 96721b9f..ba4b1fe3 100644 --- a/promise.js +++ b/promise.js @@ -39,7 +39,10 @@ module.exports.createConnection = function createConnection(opts) { module.exports.createPool = function createPool(opts) { const options = new PoolOptions(opts); - return new PoolPromise(options); + const pool = new PoolPromise(options); + // adding a default error handler to avoid exiting application on connection error. + pool.on('error', (err) => {}); + return pool; }; module.exports.createPoolCluster = function createPoolCluster(opts) { diff --git a/test/base.js b/test/base.js index aa9980b1..2c26aa04 100644 --- a/test/base.js +++ b/test/base.js @@ -17,11 +17,6 @@ before('share initialization', async () => { // retry global.shareConn = await basePromise.createConnection(Conf.baseConfig); } - - // https://jira.mariadb.org/browse/XPT-266 - if (isXpandFct()) { - global.shareConn.query('SET NAMES UTF8'); - } } }); diff --git a/test/integration/datatype/test-time.js b/test/integration/datatype/test-time.js index 744f4615..14056b68 100644 --- a/test/integration/datatype/test-time.js +++ b/test/integration/datatype/test-time.js @@ -15,12 +15,13 @@ describe('time', () => { await shareConn.query('INSERT INTO time_data VALUES (?, ?)', ['-1:00:00', '25:00:00']); let results = await shareConn.query('SELECT * FROM time_data'); assert.equal(results[0].t1, '-838:59:58.000000'); - assert.equal(results[0].t2, '-838:59:59.999999'); + + assert.equal(results[0].t2, isXpand() ? '-838:59:59.000000' : '-838:59:59.999999'); assert.equal(results[1].t1, '-01:00:00.000000'); assert.equal(results[1].t2, '25:00:00.000000'); results = await shareConn.execute('SELECT * FROM time_data'); assert.equal(results[0].t1, '-838:59:58'); - assert.equal(results[0].t2, '-838:59:59.999999'); + assert.equal(results[0].t2, isXpand() ? '-838:59:59' : '-838:59:59.999999'); assert.equal(results[1].t1, '-01:00:00'); assert.equal(results[1].t2, '25:00:00'); }); diff --git a/test/integration/test-batch-callback.js b/test/integration/test-batch-callback.js index 3f53d186..4df8a9f5 100644 --- a/test/integration/test-batch-callback.js +++ b/test/integration/test-batch-callback.js @@ -79,10 +79,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS simpleBatch'); conn.query( 'CREATE TABLE simpleBatch(' + @@ -222,10 +218,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS simpleBatchWithOptions'); conn.query('CREATE TABLE simpleBatchWithOptions(id int, d datetime)'); conn.query('FLUSH TABLES'); @@ -293,10 +285,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS simpleBatchCP1251'); conn.query('CREATE TABLE simpleBatchCP1251(t varchar(128), id int) CHARSET utf8mb4'); conn.query('FLUSH TABLES'); @@ -345,10 +333,6 @@ describe('batch callback', function () { const conn = base.createCallbackConnection({ trace: true, bulk: useBulk }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.batch( 'INSERT INTO simpleBatchErrorMsg values (1, ?, 2, ?, 3)', [ @@ -385,10 +369,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS simpleBatch'); conn.query( 'CREATE TABLE simpleBatch(id int, id2 boolean, id3 int, t varchar(8), d datetime, d2 datetime(6), g POINT, id4 int) CHARSET utf8mb4' @@ -474,10 +454,6 @@ describe('batch callback', function () { conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS nonRewritableBatch'); conn.query('CREATE TABLE nonRewritableBatch(id int, t varchar(256))'); conn.batch( @@ -518,10 +494,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } const values = []; for (let i = 0; i < 1000000; i++) { values.push([i, 'abcdefghijkflmnopqrtuvwxyz🤘💪']); @@ -557,10 +529,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS batchWithStream'); conn.query( 'CREATE TABLE batchWithStream(id int, id2 int, id3 int, t varchar(128), id4 int, id5 int) CHARSET utf8mb4' @@ -625,10 +593,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.batch( 'INSERT INTO batchErrorWithStream values (1, ?, 2, ?, ?, 3)', [ @@ -664,10 +628,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS simpleNamedPlaceHolders'); conn.query( 'CREATE TABLE simpleNamedPlaceHolders(id int, id2 int, id3 int, t varchar(128), id4 int) CHARSET utf8mb4' @@ -729,10 +689,6 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8', () => {}); - } conn.batch( 'INSERT INTO blabla values (1, :param_1, 2, :param_2, 3)', [ @@ -768,10 +724,7 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } + conn.query('DROP TABLE IF EXISTS nonRewritableHoldersErr'); conn.query('CREATE TABLE nonRewritableHoldersErr(id int, t varchar(256))'); conn.batch( @@ -814,10 +767,7 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } + conn.query('DROP TABLE IF EXISTS streamNamedPlaceHolders'); conn.query( 'CREATE TABLE streamNamedPlaceHolders(id int, id2 int, id3 int, t varchar(128), id4 int, id5 int) CHARSET utf8mb4' @@ -880,10 +830,7 @@ describe('batch callback', function () { }); conn.connect(function (err) { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } + conn.batch( 'INSERT INTO blabla values (1, :id1, 2, :id3, :id4, 3)', [ @@ -913,6 +860,8 @@ describe('batch callback', function () { describe('standard question mark using bulk', () => { const useCompression = false; it('simple batch, local date', function (done) { + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (!base.utf8Collation() || isXpand()) this.skip(); if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); simpleBatch(useCompression, true, 'local', done); @@ -973,6 +922,7 @@ describe('batch callback', function () { }); it('simple batch encoding CP1251', function (done) { + // xpand doesn't support CP1251 encoding if (isXpand()) this.skip(); simpleBatchEncodingCP1251(useCompression, true, 'local', done); }); @@ -988,6 +938,8 @@ describe('batch callback', function () { it('simple batch error message packet split', function (done) { if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (isXpand()) this.skip(); simpleBatchErrorSplit(useCompression, true, 'local', done); }); @@ -1063,10 +1015,6 @@ describe('batch callback', function () { it('immediate batch after callback', function (done) { let conn = base.createCallbackConnection(); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS contacts'); conn.query( 'CREATE TABLE contacts(' + diff --git a/test/integration/test-batch-geometry-type.js b/test/integration/test-batch-geometry-type.js index 16f4ff5d..d97f22de 100644 --- a/test/integration/test-batch-geometry-type.js +++ b/test/integration/test-batch-geometry-type.js @@ -15,6 +15,8 @@ describe('batch geometry type', () => { }); it('Point format', async function () { + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (!shareConn.info.isMariaDB() || isXpand()) this.skip(); await shareConn.query('DROP TABLE IF EXISTS gis_point_batch'); await shareConn.query('CREATE TABLE gis_point_batch (g POINT)'); @@ -106,6 +108,8 @@ describe('batch geometry type', () => { }); it('LineString insert', async function () { + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (!shareConn.info.isMariaDB() || isXpand()) this.skip(); await shareConn.query('DROP TABLE IF EXISTS gis_line_batch'); await shareConn.query('CREATE TABLE gis_line_batch (g LINESTRING)'); @@ -208,6 +212,8 @@ describe('batch geometry type', () => { }); it('Polygon insert', async function () { + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (!shareConn.info.isMariaDB() || isXpand()) this.skip(); await shareConn.query('DROP TABLE IF EXISTS gis_polygon_batch'); await shareConn.query('CREATE TABLE gis_polygon_batch (g POLYGON)'); @@ -388,6 +394,8 @@ describe('batch geometry type', () => { }); it('MultiPoint insert', async function () { + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (!shareConn.info.isMariaDB() || isXpand()) this.skip(); await shareConn.query('DROP TABLE IF EXISTS gis_multi_point_batch'); await shareConn.query('CREATE TABLE gis_multi_point_batch (g MULTIPOINT)'); @@ -485,6 +493,8 @@ describe('batch geometry type', () => { }); it('Multi-line insert', async function () { + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (!shareConn.info.isMariaDB() || isXpand()) this.skip(); await shareConn.query('DROP TABLE IF EXISTS gis_multi_line_batch'); await shareConn.query('CREATE TABLE gis_multi_line_batch (g MULTILINESTRING)'); @@ -639,6 +649,8 @@ describe('batch geometry type', () => { }); it('Multi-polygon insert', async function () { + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (!shareConn.info.isMariaDB() || isXpand()) this.skip(); await shareConn.query('DROP TABLE IF EXISTS gis_multi_polygon_batch'); await shareConn.query('CREATE TABLE gis_multi_polygon_batch (g MULTIPOLYGON)'); @@ -906,6 +918,8 @@ describe('batch geometry type', () => { }); it('Geometry collection insert', async function () { + // xpand doesn't support geometry + // https://jira.mariadb.org/browse/XPT-12 if (!shareConn.info.isMariaDB() || isXpand()) this.skip(); const conn = await base.createConnection(); diff --git a/test/integration/test-batch.js b/test/integration/test-batch.js index 684ec633..104df8b5 100644 --- a/test/integration/test-batch.js +++ b/test/integration/test-batch.js @@ -172,6 +172,210 @@ describe('batch', function () { } }; + const simpleBatchMeta = async () => { + try { + const conn = await base.createConnection({ + bulk: true, + metaAsArray: true + }); + conn.query('DROP TABLE IF EXISTS simpleBatch'); + conn.query( + 'CREATE TABLE simpleBatch(id int, id2 boolean, id3 int, t varchar(128), d datetime, d2 datetime(6), g POINT, id4 int) CHARSET utf8mb4' + ); + await shareConn.query('FLUSH TABLES'); + await conn.query('START TRANSACTION'); + + let res = await conn.batch('INSERT INTO `simpleBatch` values (1, ?, 2, ?, ?, ?, ?, 3)', [ + [ + true, + 'Ʉjo"h\u000An😎🌶\\\\', + new Date('2001-12-31 23:59:58+3'), + new Date('2018-01-01 12:30:20.456789+3'), + { + type: 'Point', + coordinates: [10, 10] + } + ], + [ + true, + 't1', + new Date('2001-12-31 23:59:58+3'), + new Date('2018-01-01 12:30:20.456789+3'), + { + type: 'Point', + coordinates: [10, 10] + } + ] + ]); + assert.equal(res[0].affectedRows, 2); + res = await conn.query('select * from `simpleBatch`'); + assert.deepEqual(res[0], [ + { + id: 1, + id2: 1, + id3: 2, + t: 'Ʉjo"h\u000An😎🌶\\\\', + d: new Date('2001-12-31 23:59:58+3'), + d2: new Date('2018-01-01 12:30:20.456789+3'), + g: { + type: 'Point', + coordinates: [10, 10] + }, + id4: 3 + }, + { + id: 1, + id2: 1, + id3: 2, + t: 't1', + d: new Date('2001-12-31 23:59:58+3'), + d2: new Date('2018-01-01 12:30:20.456789+3'), + g: { + type: 'Point', + coordinates: [10, 10] + }, + id4: 3 + } + ]); + await conn.query('ROLLBACK'); + + conn.query('DROP TABLE simpleBatch'); + const rows = await conn.query({ sql: 'select 1', bigIntAsNumber: true }); + assert.deepEqual(rows[0], [{ 1: 1 }]); + await conn.end(); + } catch (err) { + console.log(err); + assert.equal(err.errno, 45033); + } + }; + + const simpleBatchMeta2 = async () => { + try { + const conn = await base.createConnection({ + metaAsArray: true, + bulk: true + }); + conn.query('DROP TABLE IF EXISTS simpleBatch'); + conn.query( + 'CREATE TABLE simpleBatch(id int, id2 boolean, id3 int, t varchar(128), d datetime, d2 datetime(6), g POINT, id4 int) CHARSET utf8mb4' + ); + await shareConn.query('FLUSH TABLES'); + await conn.query('START TRANSACTION'); + + const f = {}; + f.toSqlString = () => { + return 'blabla'; + }; + let res = await conn.batch('INSERT INTO `simpleBatch` values (1, ?, 2, ?, ?, ?, ?, 3)', [ + [ + true, + 'Ʉjo"h\u000An😎🌶\\\\', + new Date('2001-12-31 23:59:58+3'), + new Date('2018-01-01 12:30:20.456789+3'), + { + type: 'Point', + coordinates: [10, 10] + } + ], + [ + true, + f, + new Date('2001-12-31 23:59:58+3'), + new Date('2018-01-01 12:30:20.456789+3'), + { + type: 'Point', + coordinates: [10, 10] + } + ], + [ + false, + { name: 'jack\u000Aमस्', val: 'tt' }, + null, + new Date('2018-01-21 11:30:20.123456+3'), + { + type: 'Point', + coordinates: [10, 20] + } + ], + [ + 0, + null, + new Date('2020-12-31 23:59:59+3'), + new Date('2018-01-21 11:30:20.123456+3'), + { + type: 'Point', + coordinates: [20, 20] + } + ] + ]); + assert.equal(res[0].affectedRows, 4); + res = await conn.query('select * from `simpleBatch`'); + assert.deepEqual(res[0], [ + { + id: 1, + id2: 1, + id3: 2, + t: 'Ʉjo"h\u000An😎🌶\\\\', + d: new Date('2001-12-31 23:59:58+3'), + d2: new Date('2018-01-01 12:30:20.456789+3'), + g: { + type: 'Point', + coordinates: [10, 10] + }, + id4: 3 + }, + { + id: 1, + id2: 1, + id3: 2, + t: 'blabla', + d: new Date('2001-12-31 23:59:58+3'), + d2: new Date('2018-01-01 12:30:20.456789+3'), + g: { + type: 'Point', + coordinates: [10, 10] + }, + id4: 3 + }, + { + id: 1, + id2: 0, + id3: 2, + t: '{"name":"jack\\nमस्","val":"tt"}', + d: null, + d2: new Date('2018-01-21 11:30:20.123456+3'), + g: { + type: 'Point', + coordinates: [10, 20] + }, + id4: 3 + }, + { + id: 1, + id2: 0, + id3: 2, + t: null, + d: new Date('2020-12-31 23:59:59+3'), + d2: new Date('2018-01-21 11:30:20.123456+3'), + g: { + type: 'Point', + coordinates: [20, 20] + }, + id4: 3 + } + ]); + await conn.query('ROLLBACK'); + + conn.query('DROP TABLE simpleBatch'); + const rows = await conn.query({ sql: 'select 1', bigIntAsNumber: true }); + assert.deepEqual(rows[0], [{ 1: 1 }]); + await conn.end(); + } catch (err) { + console.log(err); + assert.equal(err.errno, 45033); + } + }; + const batchWithReturning = async (useBulk) => { const conn = await base.createConnection({ bulk: useBulk }); await conn.query('drop table if exists bar'); @@ -194,14 +398,56 @@ describe('batch', function () { ]); assert.deepEqual(res, [['7'], ['8'], ['9'], ['10']]); + res = await conn.batch({ sql: 'insert into bar (id) values (?) returning id', metaAsArray: true }, [[11]]); + assert.deepEqual(res[0], [ + { + id: '11' + } + ]); + res = await conn.batch({ sql: 'insert into bar (id) values (?) returning id', metaAsArray: true }, [[24], [12]]); + assert.deepEqual(res[0], [ + { + id: '24' + }, + { + id: '12' + } + ]); + assert.equal(1, res[1].length); + res = await conn.batch({ sql: 'insert into bar (id) values (?) returning id', metaAsArray: true }, [ + [13], + [14], + ['15'], + ['16'], + [17] + ]); + assert.deepEqual(res[0], [ + { + id: '13' + }, + { + id: '14' + }, + { + id: '15' + }, + { + id: '16' + }, + { + id: '17' + } + ]); + assert.equal(1, res[1].length); + res = await conn.batch( { sql: 'insert into bar (id) values (?) returning id', supportBigNumbers: true, bigNumberStrings: true }, - [[11], ['12'], [13], [2147483650], [9223372036854775818n]] + [[20], ['21'], [22], [2147483650], [9223372036854775818n]] ); assert.deepEqual(res, [ - { id: '11' }, - { id: '12' }, - { id: '13' }, + { id: '20' }, + { id: '21' }, + { id: '22' }, { id: '2147483650' }, { id: '9223372036854775818' } ]); @@ -447,10 +693,6 @@ describe('batch', function () { maxAllowedPacket: 16 * 1024 * 1024, bulk: useBulk }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS bigBatchWith16mMaxAllowedPacket'); conn.query( 'CREATE TABLE bigBatchWith16mMaxAllowedPacket(id int, id2 int, id3 int, t varchar(128), id4 int) CHARSET utf8mb4' @@ -486,10 +728,6 @@ describe('batch', function () { bulk: useBulk, maxAllowedPacket: 4 * 1024 * 1024 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS bigBatchWith4mMaxAllowedPacket'); conn.query( 'CREATE TABLE bigBatchWith4mMaxAllowedPacket(id int, id2 int, id3 int, t varchar(128), id4 int) CHARSET utf8mb4' @@ -524,10 +762,6 @@ describe('batch', function () { compress: useCompression, bulk: useBulk }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } const values = []; for (let i = 0; i < 1000000; i++) { values.push([i, str]); @@ -627,10 +861,6 @@ describe('batch', function () { compress: useCompression, bulk: useBulk }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } try { await conn.batch('INSERT INTO `blabla` values (1, ?, 2, ?, ?, 3)', values); throw new Error('must have thrown error !'); @@ -647,10 +877,6 @@ describe('batch', function () { const simpleNamedPlaceHolders = async (useBulk) => { const conn = await base.createConnection({ namedPlaceholders: true, bulk: useBulk }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS simpleNamedPlaceHolders'); conn.query( 'CREATE TABLE simpleNamedPlaceHolders(id int, id2 int, id3 int, t varchar(128), id4 int) CHARSET utf8mb4' @@ -686,11 +912,6 @@ describe('batch', function () { const simpleNamedPlaceHoldersErr = async (useBulk) => { const conn = await base.createConnection({ namedPlaceholders: true, bulk: useBulk }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } - try { await conn.batch('INSERT INTO blabla values (1, :param_1, 2, :param_2, 3)', [ { param_1: 1, param_2: 'john' }, @@ -713,11 +934,6 @@ describe('batch', function () { const more16MNamedPlaceHolders = async function (useBulk) { const conn = await base.createConnection({ namedPlaceholders: true, bulk: useBulk }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } - conn.query('DROP TABLE IF EXISTS more16MNamedPlaceHolders'); conn.query( 'CREATE TABLE more16MNamedPlaceHolders(id int, id2 int, id3 int, t varchar(128), id4 int) CHARSET utf8mb4' @@ -788,10 +1004,6 @@ describe('batch', function () { const stream1 = fs.createReadStream(fileName); const stream2 = fs.createReadStream(fileName); const conn = await base.createConnection({ namedPlaceholders: true, bulk: useBulk }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } await conn.query('DROP TABLE IF EXISTS blabla'); await conn.query('CREATE TABLE blabla(i int, i2 int, i3 int, s1 TEXT, s2 TEXT, i4 int)'); @@ -920,6 +1132,17 @@ describe('batch', function () { await simpleBatch(useCompression, true, 'local'); }); + it('simple batch, meta as Array', async function () { + if (!base.utf8Collation() || isXpand()) { + this.skip(); + return; + } + this.timeout(30000); + if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); + await simpleBatchMeta(); + await simpleBatchMeta2(); + }); + it('simple batch with option', async function () { this.timeout(30000); if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); @@ -1190,10 +1413,6 @@ describe('batch', function () { const t = makeid(100); const conn = await base.createConnection({ bulk: false, maxAllowedPacket: 150 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } await conn.query('DROP TABLE IF EXISTS my_table'); await conn.query('CREATE TABLE my_table(id int, val LONGTEXT)'); await conn.query('FLUSH TABLES'); diff --git a/test/integration/test-call.js b/test/integration/test-call.js index 43d4a747..96439312 100644 --- a/test/integration/test-call.js +++ b/test/integration/test-call.js @@ -45,14 +45,13 @@ describe('stored procedure', () => { }); it('output call query', async function () { + //https://jira.mariadb.org/browse/XPT-268 + if (isXpand()) this.skip(); await shareConn.query('call someProc(?,@myOutputValue)', [2]); const res = await shareConn.query('SELECT @myOutputValue'); assert.equal(res[0]['@myOutputValue'], 4); - //https://jira.mariadb.org/browse/XPT-268 - if (!isXpand()) { - const res2 = await shareConn.execute('call someProc(?, ?)', [2, null]); - assert.equal(res2[0][0]['p2'], 4); - } + const res2 = await shareConn.execute('call someProc(?, ?)', [2, null]); + assert.equal(res2[0][0]['p2'], 4); }); it('simple function', function (done) { @@ -66,15 +65,14 @@ describe('stored procedure', () => { .catch(done); }); - it('call with out parameter query', async () => { + it('call with out parameter query', async function () { + //https://jira.mariadb.org/browse/XPT-268 + if (isXpand()) this.skip(); try { await shareConn.query('call stmtOutParam(?,?)', [2, 3]); throw new Error('must not be possible since output parameter is not a variable'); } catch (err) { - //https://jira.mariadb.org/browse/XPT-268 - if (!isXpand()) { - assert.ok(err.message.includes('is not a variable or NEW pseudo-variable in BEFORE trigger')); - } + assert.ok(err.message.includes('is not a variable or NEW pseudo-variable in BEFORE trigger')); } }); }); diff --git a/test/integration/test-cluster.js b/test/integration/test-cluster.js index 008694f7..2b7d71ef 100644 --- a/test/integration/test-cluster.js +++ b/test/integration/test-cluster.js @@ -232,7 +232,7 @@ describe('cluster', function () { cluster.add( 'node4', Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node4'"] : "set @node='node4'", + initSql: "set @node='node4'", connectionLimit: 1, resetAfterUse: false }) @@ -297,17 +297,17 @@ describe('cluster', function () { removedNode.push(node); }); const connOption1 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node1'"] : "set @node='node1'", + initSql: "set @node='node1'", connectionLimit: 1, resetAfterUse: false }); const connOption2 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node2'"] : "set @node='node2'", + initSql: "set @node='node2'", connectionLimit: 1, resetAfterUse: false }); const connOption3 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node3'"] : "set @node='node3'", + initSql: "set @node='node3'", user: 'wrong_user', connectTimeout: 100, acquireTimeout: 200, @@ -354,17 +354,17 @@ describe('cluster', function () { }); const connOption1 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node1'"] : "set @node='node1'", + initSql: "set @node='node1'", connectionLimit: 1, resetAfterUse: false }); const connOption2 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node2'"] : "set @node='node2'", + initSql: "set @node='node2'", connectionLimit: 1, resetAfterUse: false }); const connOption3 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node3'"] : "set @node='node3'", + initSql: "set @node='node3'", user: 'wrong_user', connectTimeout: 100, acquireTimeout: 200, @@ -395,12 +395,7 @@ describe('cluster', function () { }); it('one node failing', async function () { - if ( - process.env.srv === 'maxscale' || - process.env.srv === 'skysql' || - process.env.srv === 'skysql-ha' || - isXpand() - ) + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); this.timeout(30000); @@ -476,12 +471,7 @@ describe('cluster', function () { }); it('one node failing with blacklisted host', async function () { - if ( - process.env.srv === 'maxscale' || - process.env.srv === 'skysql' || - process.env.srv === 'skysql-ha' || - isXpand() - ) + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); this.timeout(30000); @@ -566,7 +556,7 @@ describe('cluster', function () { }); it('reusing node after timeout', async function () { - if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) this.skip(); + if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); this.timeout(30000); const cl = await get3NodeClusterWithProxy({ restoreNodeTimeout: 500 }, basePromise); const cluster = cl.cluster; @@ -907,6 +897,22 @@ describe('cluster', function () { }); }); }); + + it('ensure failing connection on pool not exiting application', async function () { + this.timeout(5000); + const cluster = basePromise.createPoolCluster(); + const connOption1 = Object.assign({}, Conf.baseConfig, { + port: 8888, + initializationTimeout: 100 + }); + cluster.add('node1', connOption1); + + // pool will throw an error after some time and must not exit test suite + await new Promise((resolve, reject) => { + new setTimeout(resolve, 3000); + }); + await cluster.end(); + }); }); describe('callback', () => { @@ -1241,7 +1247,6 @@ describe('cluster', function () { }); it('reusing node after timeout', function (done) { - if (isXpand()) this.skip(); get3NodeClusterWithProxy({ restoreNodeTimeout: 500 }, baseCallback).then((cl) => { const cluster = cl.cluster; const proxy = cl.proxy; @@ -1391,6 +1396,22 @@ describe('cluster', function () { }); }); }); + + it('ensure failing connection on pool not exiting application', async function () { + this.timeout(5000); + const cluster = baseCallback.createPoolCluster(); + const connOption1 = Object.assign({}, Conf.baseConfig, { + port: 8888, + initializationTimeout: 100 + }); + cluster.add('node1', connOption1); + + // pool will throw an error after some time and must not exit test suite + await new Promise((resolve, reject) => { + new setTimeout(resolve, 3000); + }); + await cluster.end(); + }); }); const get3NodeCallbackCluster = (opts) => { @@ -1425,19 +1446,19 @@ describe('cluster', function () { const cluster = basePromise.createPoolCluster(opts); const connOption1 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node1'"] : "set @node='node1'", + initSql: "set @node='node1'", connectionLimit: 1, resetAfterUse: false, trace: true }); const connOption2 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node2'"] : "set @node='node2'", + initSql: "set @node='node2'", connectionLimit: 1, resetAfterUse: false, trace: true }); const connOption3 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node3'"] : "set @node='node3'", + initSql: "set @node='node3'", connectionLimit: 1, resetAfterUse: false, trace: true @@ -1453,7 +1474,7 @@ describe('cluster', function () { const cluster = base.createPoolCluster(opts); const connOption1 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node1'"] : "set @node='node1'", + initSql: "set @node='node1'", connectionLimit: 1, resetAfterUse: false }); @@ -1471,7 +1492,7 @@ describe('cluster', function () { }); const connOption2 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node2'"] : "set @node='node2'", + initSql: "set @node='node2'", connectionLimit: 1, host: 'localhost', connectTimeout: 200, @@ -1483,7 +1504,7 @@ describe('cluster', function () { }); const connOption3 = Object.assign({}, Conf.baseConfig, { - initSql: isXpand() ? ['set NAMES utf8', "set @node='node3'"] : "set @node='node3'", + initSql: "set @node='node3'", connectionLimit: 1, resetAfterUse: false, trace: true diff --git a/test/integration/test-connection-opts.js b/test/integration/test-connection-opts.js index 232c088a..015c50a0 100644 --- a/test/integration/test-connection-opts.js +++ b/test/integration/test-connection-opts.js @@ -2,7 +2,6 @@ const base = require('../base.js'); const { assert } = require('chai'); -const { isXpand } = require('../base'); describe('connection option', () => { it('with undefined collation', function (done) { @@ -50,6 +49,9 @@ describe('connection option', () => { }); it('timezone Z', async function () { + // node.js before v13 doesn't permit to set TZ value repeatedly + if (parseInt(process.versions.node.split('.')[0]) <= 12) this.skip(); + const defaultTz = process.env.TZ; const conn = await base.createConnection({ timezone: 'Z' }); conn.query("SET SESSION time_zone = '+01:00'"); @@ -67,18 +69,18 @@ describe('connection option', () => { }); it('timezone +0h', async function () { - const conn = await base.createConnection({ timezone: '+00:00' }); + const conn = await base.createConnection({ timezone: '+00:00', debug: true }); let d = new Date('2000-01-01T00:00:00Z'); - let res = await conn.query('SELECT UNIX_TIMESTAMP(?) tt', [d]); + let res = await conn.query("SELECT UNIX_TIMESTAMP('2000-01-01T00:00:00') tt", [d]); assert.equal(Number(res[0].tt), d.getTime() / 1000); - res = await conn.query("SELECT TIMESTAMP('2003-12-31 12:00:00') tt1, FROM_UNIXTIME(UNIX_TIMESTAMP(?)) tt2", [d]); - assert.deepEqual(res[0].tt1, new Date('2003-12-31T12:00:00Z')); - assert.deepEqual(res[0].tt2, d); conn.end(); }); it('timezone +10h00', async function () { + // node.js before v13 doesn't permit to set TZ value repeatedly + if (parseInt(process.versions.node.split('.')[0]) <= 12) this.skip(); + const defaultTz = process.env.TZ; const conn = await base.createConnection({ timezone: '+10:00' }); process.env.TZ = 'Etc/GMT-10'; @@ -94,6 +96,9 @@ describe('connection option', () => { }); it('timezone Etc/GMT-10', async function () { + // node.js before v13 doesn't permit to set TZ value repeatedly + if (parseInt(process.versions.node.split('.')[0]) <= 12) this.skip(); + const defaultTz = process.env.TZ; const conn = await base.createConnection({ timezone: 'Etc/GMT-10' }); process.env.TZ = 'Etc/GMT-10'; @@ -109,6 +114,9 @@ describe('connection option', () => { }); it('timezone GMT+10', async function () { + // node.js before v13 doesn't permit to set TZ value repeatedly + if (parseInt(process.versions.node.split('.')[0]) <= 12) this.skip(); + const defaultTz = process.env.TZ; const conn = await base.createConnection({ timezone: 'GMT+10' }); process.env.TZ = 'Etc/GMT-10'; @@ -139,6 +147,9 @@ describe('connection option', () => { }); it('Server with different tz', async function () { + // node.js before v13 doesn't permit to set TZ value repeatedly + if (parseInt(process.versions.node.split('.')[0]) <= 12) this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); //MySQL 5.5 doesn't have milliseconds if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); @@ -343,7 +354,10 @@ describe('connection option', () => { done(new Error('must have thrown error')); }) .catch((err) => { - assert.isTrue(err.message.includes('Cannot use timeout for MySQL server')); + assert.isTrue( + err.message.includes('Cannot use timeout for MySQL server') || + err.message.includes('Cannot use timeout for xpand/MariaDB') + ); assert.equal(err.errno, 45038); assert.equal(err.sqlState, 'HY000'); assert.equal(err.code, 'ER_TIMEOUT_NOT_SUPPORTED'); diff --git a/test/integration/test-connection.js b/test/integration/test-connection.js index 2c755ff8..232eb339 100644 --- a/test/integration/test-connection.js +++ b/test/integration/test-connection.js @@ -207,7 +207,7 @@ describe('connection', () => { }); it('connection error event socket failed', function (done) { - if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) this.skip(); + if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); base .createConnection({ socketTimeout: 100 }) .then((conn) => { @@ -823,7 +823,8 @@ describe('connection', () => { it('error reaching max connection', async function () { // error occurs on handshake packet, with old error format - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) + this.skip(); this.timeout(10000); const res = await shareConn.query('select @@max_connections as a'); diff --git a/test/integration/test-debug.js b/test/integration/test-debug.js index 6b87559e..ffd80d5b 100644 --- a/test/integration/test-debug.js +++ b/test/integration/test-debug.js @@ -17,28 +17,29 @@ describe('debug', () => { let logger; before(function (done) { - if (isXpand()) this.skip(); - try { - fs.unlinkSync(tmpLogFile); - } catch (e) {} - shareConn - .query('select @@local_infile') - .then((rows) => { - permitLocalInfile = rows[0]['@@local_infile'] === 1 || rows[0]['@@local_infile'] === 1n; - return new Promise(function (resolve, reject) { - fs.writeFile(smallFileName, '1,hello\n2,world\n', 'utf8', function (err) { - if (err) reject(err); - else resolve(); + if (!isXpand()) { + try { + fs.unlinkSync(tmpLogFile); + } catch (e) {} + shareConn + .query('select @@local_infile') + .then((rows) => { + permitLocalInfile = rows[0]['@@local_infile'] === 1 || rows[0]['@@local_infile'] === 1n; + return new Promise(function (resolve, reject) { + fs.writeFile(smallFileName, '1,hello\n2,world\n', 'utf8', function (err) { + if (err) reject(err); + else resolve(); + }); }); - }); - }) - .then(() => { - //ensure that debug from previous test are written to console - setTimeout(() => { - done(); - }, 1000); - }) - .catch(done); + }) + .then(() => { + //ensure that debug from previous test are written to console + setTimeout(() => { + done(); + }, 1000); + }) + .catch(done); + } else done(); }); beforeEach(async function () { @@ -131,7 +132,7 @@ describe('debug', () => { const serverVersion = conn.serverVersion(); if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') compress = false; - const rangeWithEOF = compress ? [1500, 1900] : [1800, 3250]; + const rangeWithEOF = compress ? [1500, 1900] : [1800, 4150]; const rangeWithoutEOF = compress ? [1500, 1900] : [2350, 3150]; const data = fs.readFileSync(tmpLogFile, 'utf8'); console.log(data); @@ -257,7 +258,7 @@ describe('debug', () => { const conn = await base.createConnection({ debug: true }); const res = await conn.query("SELECT '1'"); conn.end(); - const range = [3200, 4800]; + const range = [3200, 5000]; assert( data.length > range[0] && data.length < range[1], 'wrong data length : ' + @@ -353,7 +354,7 @@ describe('debug', () => { const serverVersion = conn.serverVersion(); if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') compress = false; - const range = compress ? [60, 150] : [60, 120]; + const range = compress ? [60, 150] : [60, 140]; const data = fs.readFileSync(tmpLogFile, 'utf8'); console.log(data); assert.isTrue(data.includes('PING')); diff --git a/test/integration/test-execute-callback.js b/test/integration/test-execute-callback.js index 81ff9a9a..25ac8dc3 100644 --- a/test/integration/test-execute-callback.js +++ b/test/integration/test-execute-callback.js @@ -247,10 +247,6 @@ describe('prepare and execute callback', () => { conn.connect((err) => { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.prepare('select ? as a', (err, prepare) => { if (err) return done(err); assert.equal(prepare.parameterCount, 1); @@ -284,10 +280,6 @@ describe('prepare and execute callback', () => { const conn = base.createCallbackConnection({ prepareCacheLength: 0 }); conn.connect((err) => { if (err) return done(err); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.execute('select ? as a', [2], (err, res, meta) => { if (err) return done(err); assert.isTrue(res[0].a === 2 || res[0].a === 2n); diff --git a/test/integration/test-execute.js b/test/integration/test-execute.js index 986bba1b..c67de42e 100644 --- a/test/integration/test-execute.js +++ b/test/integration/test-execute.js @@ -236,10 +236,6 @@ describe('prepare and execute', () => { it('basic prepare and execute', async () => { const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } const prepare = await conn.prepare('select ? as a'); assert.equal(prepare.parameterCount, 1); assert.equal(prepare.columns.length, 1); @@ -261,10 +257,6 @@ describe('prepare and execute', () => { it('direct execution without cache', async () => { const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } let res = await conn.execute('select ? as a', [2]); assert.isTrue(res[0].a === 2 || res[0].a === 2n); @@ -280,10 +272,7 @@ describe('prepare and execute', () => { it('direct execution with cache', async () => { const conn = await base.createConnection({}); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + let res = await conn.execute('select ? as a', [2]); assert.isTrue(res[0].a === 2 || res[0].a === 2n); @@ -318,10 +307,7 @@ describe('prepare and execute', () => { if (maxAllowedSize < 20 * 1024 * 1024) this.skip(); this.timeout(30000); const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + await conn.query('START TRANSACTION'); const prepare = await conn.prepare('INSERT INTO big_test_table (a,b) VALUES (?, ?)'); await prepare.execute([bigVal, 2n]); @@ -367,10 +353,7 @@ describe('prepare and execute', () => { } const stVal = val.toString(); const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + await conn.query('START TRANSACTION'); const prepare = await conn.prepare('INSERT INTO big_test_table2 (a,b) VALUES (?, ?)'); await prepare.execute([bigVal, stVal]); @@ -391,10 +374,7 @@ describe('prepare and execute', () => { const stVal = val.toString(); const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + await conn.query('START TRANSACTION'); const prepare = await conn.prepare('INSERT INTO big_test_table2 (a,b) VALUES (?, ?)'); await prepare.execute([bigVal, stVal]); @@ -409,10 +389,7 @@ describe('prepare and execute', () => { if (maxAllowedSize < 20 * 1024 * 1024) this.skip(); this.timeout(30000); const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + await conn.query('START TRANSACTION'); const prepare = await conn.prepare('INSERT INTO big_test_table2 (a,b) VALUES (?, ?)'); await prepare.execute([bigVal, 2.156]); @@ -430,10 +407,7 @@ describe('prepare and execute', () => { if (maxAllowedSize < 20 * 1024 * 1024) this.skip(); this.timeout(30000); const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + await conn.query('START TRANSACTION'); const prepare = await conn.prepare('INSERT INTO big_test_table2 (a,b) VALUES (?, ?)'); await prepare.execute([bigVal, 'test']); @@ -449,10 +423,7 @@ describe('prepare and execute', () => { if (isXpand()) this.skip(); this.timeout(30000); const conn = await base.createConnection({ prepareCacheLength: 0, charset: 'big5' }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + await conn.query('START TRANSACTION'); const prepare = await conn.prepare('INSERT INTO big_test_table2 (a,b) VALUES (?, ?)'); await prepare.execute([bigVal, 'test']); @@ -468,10 +439,7 @@ describe('prepare and execute', () => { this.timeout(30000); const date3 = new Date('2001-12-31 23:59:59.123456'); const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + await conn.query('START TRANSACTION'); const prepare = await conn.prepare('INSERT INTO big_test_table2 (a,b) VALUES (?, ?)'); await prepare.execute([bigVal, date3]); @@ -486,10 +454,7 @@ describe('prepare and execute', () => { if (maxAllowedSize < 20 * 1024 * 1024) this.skip(); this.timeout(30000); const conn = await base.createConnection({ prepareCacheLength: 0 }); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } + await conn.query('START TRANSACTION'); const prepare = await conn.prepare('INSERT INTO big_test_table2 (a,b) VALUES (?, ?)'); await prepare.execute([bigVal, true]); diff --git a/test/integration/test-multi-results.js b/test/integration/test-multi-results.js index bd3aa524..641acbc5 100644 --- a/test/integration/test-multi-results.js +++ b/test/integration/test-multi-results.js @@ -10,7 +10,7 @@ describe('multi-results', () => { before(function (done) { base .createConnection({ multipleStatements: true }) - .then((con) => { + .then(async (con) => { multiStmtConn = con; done(); }) diff --git a/test/integration/test-pool-callback.js b/test/integration/test-pool-callback.js index a04ae660..b8cd5a8f 100644 --- a/test/integration/test-pool-callback.js +++ b/test/integration/test-pool-callback.js @@ -89,6 +89,7 @@ describe('Pool callback', () => { }); it('pool execute wrong param stack trace', function (done) { + this.timeout(20000); if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPoolCallback({ metaAsArray: true, @@ -560,7 +561,6 @@ describe('Pool callback', () => { }); it('connection fail handling', function (done) { - if (isXpand()) this.skip(); const pool = base.createPoolCallback({ connectionLimit: 2, minDelayValidation: 200 @@ -582,7 +582,7 @@ describe('Pool callback', () => { assert.equal(pool.taskQueueSize(), 0); conn.query('KILL CONNECTION_ID()', (err) => { - assert.equal(err.sqlState, 70100); + assert.equal(err.sqlState, isXpand() ? 'HY000' : '70100'); assert.equal(pool.activeConnections(), 1); assert.equal(pool.totalConnections(), 2); assert.equal(pool.idleConnections(), 1); @@ -601,7 +601,6 @@ describe('Pool callback', () => { }); it('query fail handling', function (done) { - if (isXpand()) this.skip(); const pool = base.createPoolCallback({ connectionLimit: 2, minDelayValidation: 200 @@ -614,7 +613,7 @@ describe('Pool callback', () => { assert.equal(pool.taskQueueSize(), 0); pool.query('KILL CONNECTION_ID()', (err) => { - assert.equal(err.sqlState, 70100); + assert.equal(err.sqlState, isXpand() ? 'HY000' : '70100'); setImmediate(() => { assert.equal(pool.taskQueueSize(), 0); @@ -639,7 +638,7 @@ describe('Pool callback', () => { }); it('connection end', function (done) { - if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) this.skip(); + if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPoolCallback({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -670,7 +669,6 @@ describe('Pool callback', () => { }); it('connection release alias', function (done) { - if (isXpand()) this.skip(); const pool = base.createPoolCallback({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -701,7 +699,6 @@ describe('Pool callback', () => { }); it('connection destroy', function (done) { - if (isXpand()) this.skip(); const pool = base.createPoolCallback({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -765,8 +762,6 @@ describe('Pool callback', () => { it('pool batch', function (done) { let params = { connectionLimit: 1, resetAfterUse: false }; - if (isXpand()) params['initSql'] = 'SET NAMES UTF8'; - const pool = base.createPoolCallback(params); pool.query('DROP TABLE IF EXISTS parse', (err, res) => { pool.query('CREATE TABLE parse(id int, id2 int, id3 int, t varchar(128), id4 int)', (err, res) => { @@ -870,7 +865,7 @@ describe('Pool callback', () => { }); it('test minimum idle decrease', function (done) { - if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) this.skip(); + if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); this.timeout(30000); const pool = base.createPoolCallback({ connectionLimit: 10, @@ -948,7 +943,7 @@ describe('Pool callback', () => { acquireTimeout: 400 }); assert.isFalse(pool.closed); - pool.query('DO SLEEP(1)'); + pool.query('SELECT SLEEP(1)'); pool.execute('SELECT 1', (err, res) => { pool.end(); assert.isTrue(pool.closed); @@ -968,7 +963,7 @@ describe('Pool callback', () => { connectionLimit: 1, acquireTimeout: 400 }); - pool.query('DO SLEEP(1)'); + pool.query('SELECT SLEEP(1)'); pool.batch('SELECT ?', [[1]], (err, res) => { pool.end(); if (err) { @@ -979,4 +974,18 @@ describe('Pool callback', () => { } }); }); + + it('ensure failing connection on pool not exiting application', async function () { + this.timeout(5000); + const pool = base.createPoolCallback({ + port: 8888, + initializationTimeout: 100 + }); + + // pool will throw an error after some time and must not exit test suite + await new Promise((resolve, reject) => { + new setTimeout(resolve, 3000); + }); + pool.end(); + }); }); diff --git a/test/integration/test-pool-event.js b/test/integration/test-pool-event.js index 98db679a..2ef10dd0 100644 --- a/test/integration/test-pool-event.js +++ b/test/integration/test-pool-event.js @@ -11,7 +11,7 @@ const { isXpand } = require('../base'); describe('Pool event', () => { before(function () { - if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) this.skip(); + if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); }); it('pool connection creation', function (done) { diff --git a/test/integration/test-pool.js b/test/integration/test-pool.js index ecb98dd2..0fb66e7a 100644 --- a/test/integration/test-pool.js +++ b/test/integration/test-pool.js @@ -9,6 +9,7 @@ const path = require('path'); const os = require('os'); const Proxy = require('../tools/proxy'); const { isXpand } = require('../base'); +const { baseConfig } = require('../conf'); describe('Pool', () => { const fileName = path.join(os.tmpdir(), Math.random() + 'tempStream.txt'); @@ -60,7 +61,7 @@ describe('Pool', () => { await pool.query('wrong query'); throw Error('must have thrown error'); } catch (err) { - assert.isTrue(err.stack.includes('test-pool.js:60:18'), err.stack); + assert.isTrue(err.stack.includes('test-pool.js:61:18'), err.stack); } finally { await pool.end(); } @@ -78,7 +79,7 @@ describe('Pool', () => { await pool.execute('wrong query'); throw Error('must have thrown error'); } catch (err) { - assert.isTrue(err.stack.includes('test-pool.js:78:18'), err.stack); + assert.isTrue(err.stack.includes('test-pool.js:79:18'), err.stack); } finally { await pool.end(); } @@ -96,12 +97,138 @@ describe('Pool', () => { await pool.execute('SELECT ?', []); throw Error('must have thrown error'); } catch (err) { - assert.isTrue(err.stack.includes('test-pool.js:96:7'), err.stack); + assert.isTrue(err.stack.includes('test-pool.js:97:7'), err.stack); } finally { await pool.end(); } }); + it('prepare cache reuse', async () => { + const pool = base.createPool({ + metaAsArray: true, + multipleStatements: true, + connectionLimit: 1, + prepareCacheLength: 2 + }); + await pool.execute('select ?', [1]); + const conn = await pool.getConnection(); + const prepareCache = conn.prepareCache; + assert.equal(prepareCache.toString(), `info{cache:[${baseConfig.database}|select ?]}`); + conn.release(); + + await pool.execute('select ?', [1]); + assert.equal(prepareCache.toString(), `info{cache:[${baseConfig.database}|select ?]}`); + + await pool.execute('select ? + 1', [1]); + assert.equal( + prepareCache.toString(), + `info{cache:[${baseConfig.database}|select ? + 1],[${baseConfig.database}|select ?]}` + ); + + await pool.execute('select ? + 2', [1]); + assert.equal( + `info{cache:[${baseConfig.database}|select ? + 2],[${baseConfig.database}|select ? + 1]}`, + prepareCache.toString() + ); + + await pool.execute('select ? + 3', [1]); + assert.equal( + `info{cache:[${baseConfig.database}|select ? + 3],[${baseConfig.database}|select ? + 2]}`, + prepareCache.toString() + ); + + await pool.execute({ sql: 'select ? + 2' }, [1]); + assert.equal( + `info{cache:[${baseConfig.database}|select ? + 2],[${baseConfig.database}|select ? + 3]}`, + prepareCache.toString() + ); + + await pool.execute({ sql: 'select 4' }); + assert.equal( + prepareCache.toString(), + `info{cache:[${baseConfig.database}|select 4],[${baseConfig.database}|select ? + 2]}` + ); + + await pool.execute('select ?', [1]); + assert.equal( + prepareCache.toString(), + `info{cache:[${baseConfig.database}|select ?],[${baseConfig.database}|select 4]}` + ); + for (let i = 0; i < 10; i++) { + pool.execute('select ?', [i]); + assert.equal( + prepareCache.toString(), + `info{cache:[${baseConfig.database}|select ?],[${baseConfig.database}|select 4]}` + ); + } + pool.end(); + }); + + it('prepare cache reuse with reset', async function () { + if (!shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(10, 3, 13)) this.skip(); + + const pool = base.createPool({ + metaAsArray: true, + multipleStatements: true, + connectionLimit: 1, + prepareCacheLength: 2, + resetAfterUse: true + }); + await pool.execute('select ?', [1]); + const conn = await pool.getConnection(); + const prepareCache = conn.prepareCache; + assert.equal(prepareCache.toString(), `info{cache:[${baseConfig.database}|select ?]}`); + await conn.release(); + assert.equal(prepareCache.toString(), `info{cache:}`); + + await pool.execute('select ?', [1]); + assert.equal(prepareCache.toString(), `info{cache:[${baseConfig.database}|select ?]}`); + + await pool.execute('select ? + 1', [1]); + assert.equal( + prepareCache.toString(), + `info{cache:[${baseConfig.database}|select ? + 1],[${baseConfig.database}|select ?]}` + ); + + await pool.execute('select ? + 2', [1]); + assert.equal( + `info{cache:[${baseConfig.database}|select ? + 2],[${baseConfig.database}|select ? + 1]}`, + prepareCache.toString() + ); + + await pool.execute('select ? + 3', [1]); + assert.equal( + `info{cache:[${baseConfig.database}|select ? + 3],[${baseConfig.database}|select ? + 2]}`, + prepareCache.toString() + ); + + await pool.execute({ sql: 'select ? + 2' }, [1]); + assert.equal( + `info{cache:[${baseConfig.database}|select ? + 2],[${baseConfig.database}|select ? + 3]}`, + prepareCache.toString() + ); + + await pool.execute({ sql: 'select 4' }); + assert.equal( + prepareCache.toString(), + `info{cache:[${baseConfig.database}|select 4],[${baseConfig.database}|select ? + 2]}` + ); + + await pool.execute('select ?', [1]); + assert.equal( + prepareCache.toString(), + `info{cache:[${baseConfig.database}|select ?],[${baseConfig.database}|select 4]}` + ); + for (let i = 0; i < 10; i++) { + pool.execute('select ?', [i]); + assert.equal( + prepareCache.toString(), + `info{cache:[${baseConfig.database}|select ?],[${baseConfig.database}|select 4]}` + ); + } + pool.end(); + }); + it('pool batch stack trace', async function () { if (process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPool({ @@ -114,7 +241,7 @@ describe('Pool', () => { await pool.batch('WRONG COMMAND', [[1], [1]]); throw Error('must have thrown error'); } catch (err) { - assert.isTrue(err.stack.includes('test-pool.js:114:18'), err.stack); + assert.isTrue(err.stack.includes('test-pool.js:241:18'), err.stack); } finally { await pool.end(); } @@ -133,7 +260,7 @@ describe('Pool', () => { await pool.batch('INSERT INTO test_batch VALUES (?,?)', [[1], [1]]); throw Error('must have thrown error'); } catch (err) { - assert.isTrue(err.stack.includes('test-pool.js:133:18'), err.stack); + assert.isTrue(err.stack.includes('test-pool.js:260:18'), err.stack); } finally { await pool.query('DROP TABLE test_batch'); await pool.end(); @@ -405,7 +532,7 @@ describe('Pool', () => { acquireTimeout: 400 }); assert.isFalse(pool.closed); - pool.query('DO SLEEP(1)'); + pool.query('SELECT SLEEP(1)'); try { await pool.execute('SELECT 1'); throw new Error('must have thrown error'); @@ -424,7 +551,7 @@ describe('Pool', () => { connectionLimit: 1, acquireTimeout: 400 }); - pool.query('DO SLEEP(1)'); + pool.query('SELECT SLEEP(1)'); try { await pool.batch('SELECT 1', [[1]]); throw new Error('must have thrown error'); @@ -708,8 +835,7 @@ describe('Pool', () => { }); it('pool getConnection timeout', function (done) { - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) - this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPool({ connectionLimit: 1, acquireTimeout: 200 }); let errorThrown = false; pool @@ -734,8 +860,7 @@ describe('Pool', () => { }); it('pool getConnection timeout with leak', function (done) { - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) - this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPool({ connectionLimit: 1, acquireTimeout: 200, leakDetectionTimeout: 10 }); let errorThrown = false; pool @@ -759,8 +884,7 @@ describe('Pool', () => { }); it('pool leakDetectionTimeout timeout', async function () { - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) - this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPool({ connectionLimit: 1, acquireTimeout: 200, @@ -773,7 +897,9 @@ describe('Pool', () => { }); it('pool reset validation', async function () { - const conf = { connectionLimit: 5, timezone: 'Z', initSql: 'set @aa= 1' }; + // xpand doesn't support timeout + if (isXpand()) this.skip(); + const conf = { connectionLimit: 5, timezone: 'Z', initSql: 'set @aa= 1', debug: true }; if (shareConn.info.isMariaDB()) { conf['queryTimeout'] = 10000; } @@ -952,7 +1078,7 @@ describe('Pool', () => { closed = true; try { await pool.end(); - if (Conf.baseConfig.host === 'localhost' && !isXpand()) { + if (Conf.baseConfig.host === 'localhost') { assert.equal(pool.activeConnections(), 0); assert.equal(pool.totalConnections(), 0); assert.equal(pool.idleConnections(), 0); @@ -968,8 +1094,7 @@ describe('Pool', () => { }); it('connection fail handling', function (done) { - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) - this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPool({ connectionLimit: 2, minDelayValidation: 200 @@ -991,7 +1116,7 @@ describe('Pool', () => { done(new Error('must have thrown error')); } catch (err) { try { - assert.equal(err.sqlState, 70100); + assert.equal(err.sqlState, isXpand() ? 'HY000' : '70100'); assert.equal(pool.activeConnections(), 1); assert.equal(pool.totalConnections(), 2); assert.equal(pool.idleConnections(), 1); @@ -1009,6 +1134,7 @@ describe('Pool', () => { }); it('query fail handling', function (done) { + this.timeout(20000); if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) this.skip(); const pool = base.createPool({ @@ -1052,8 +1178,7 @@ describe('Pool', () => { }); it('connection end', function (done) { - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) - this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPool({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -1087,8 +1212,7 @@ describe('Pool', () => { }); it('connection release alias', function (done) { - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) - this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPool({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -1122,8 +1246,7 @@ describe('Pool', () => { }); it('connection destroy', function (done) { - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) - this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); const pool = base.createPool({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -1211,7 +1334,6 @@ describe('Pool', () => { it('pool batch', function (done) { let params = { connectionLimit: 1, resetAfterUse: false }; - if (isXpand()) params['initSql'] = 'SET NAMES UTF8'; const pool = base.createPool(params); pool .query('DROP TABLE IF EXISTS parse') @@ -1363,8 +1485,7 @@ describe('Pool', () => { }); it('test minimum idle decrease', function (done) { - if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha' || isXpand()) - this.skip(); + if (process.env.srv === 'maxscale' || process.env.srv === 'skysql' || process.env.srv === 'skysql-ha') this.skip(); this.timeout(30000); const pool = base.createPool({ connectionLimit: 10, @@ -1510,4 +1631,45 @@ describe('Pool', () => { } } }); + + it('prepare cache reuse pool with reset', async function () { + if (!shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(10, 3, 13)) this.skip(); + + const pool = base.createPool({ + metaAsArray: true, + multipleStatements: true, + connectionLimit: 1, + prepareCacheLength: 2, + resetAfterUse: true + }); + await pool.execute('select ?', [1]); + let conn = await pool.getConnection(); + const prepareCache = conn.prepareCache; + assert.equal(prepareCache.toString(), `info{cache:[${baseConfig.database}|select ?]}`); + await conn.release(); + assert.equal(prepareCache.toString(), `info{cache:}`); + + await pool.execute('select ?', [1]); + assert.equal(prepareCache.toString(), `info{cache:[${baseConfig.database}|select ?]}`); + conn = await pool.getConnection(); + conn.execute('select ?', [2]); + assert.equal(prepareCache.toString(), `info{cache:[${baseConfig.database}|select ?]}`); + await conn.release(); + assert.equal(prepareCache.toString(), `info{cache:}`); + pool.end(); + }); + + it('ensure failing connection on pool not exiting application', async function () { + this.timeout(5000); + const pool = base.createPool({ + port: 8888, + initializationTimeout: 100 + }); + + // pool will throw an error after some time and must not exit test suite + await new Promise((resolve, reject) => { + new setTimeout(resolve, 3000); + }); + await pool.end(); + }); }); diff --git a/test/integration/test-query-callback.js b/test/integration/test-query-callback.js index 2895bb07..4c534276 100644 --- a/test/integration/test-query-callback.js +++ b/test/integration/test-query-callback.js @@ -107,7 +107,7 @@ describe('basic query callback', () => { conn.query('CREATE TABLE arrayParam (id int, val varchar(10))'); conn.beginTransaction(); conn.query("INSERT INTO arrayParam VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')"); - conn.query('SELECT * FROM arrayParam WHERE val IN (?)', [['b', 'c', 1]], (err, rows) => { + conn.query('SELECT * FROM arrayParam WHERE val IN (?)', [['b', 'c', '1']], (err, rows) => { conn.end(); if (err) { done(err); @@ -343,7 +343,7 @@ describe('basic query callback', () => { } table += ')'; insert += ')'; - const conn = base.createCallbackConnection(); + const conn = base.createCallbackConnection({ debug: true }); conn.connect((err) => { if (err) { done(err); @@ -426,6 +426,7 @@ describe('basic query callback', () => { }); it('timeout', function (done) { + if (isXpand()) this.skip(); this.timeout(20000); const initTime = Date.now(); const query = @@ -449,6 +450,7 @@ describe('basic query callback', () => { }); it('timeout with parameter', function (done) { + if (isXpand()) this.skip(); this.timeout(20000); const initTime = Date.now(); const query = diff --git a/test/integration/test-query-values-in-sql.js b/test/integration/test-query-values-in-sql.js index 3520b007..9dca41d6 100644 --- a/test/integration/test-query-values-in-sql.js +++ b/test/integration/test-query-values-in-sql.js @@ -26,10 +26,6 @@ describe('sql template strings', () => { it('batch with parameters', async () => { const conn = await base.createConnection(); - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - await conn.query('SET NAMES UTF8'); - } await conn.query('DROP TABLE IF EXISTS batch_with_parameters'); await conn.query('CREATE TABLE batch_with_parameters(t varchar(128))'); await conn.beginTransaction(); @@ -102,10 +98,6 @@ describe('sql template strings', () => { if (err) { done(err); } else { - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) { - conn.query('SET NAMES UTF8'); - } conn.query('DROP TABLE IF EXISTS callback_batch_with_parameters', (err) => { if (err) { conn.end(); @@ -186,9 +178,6 @@ describe('sql template strings', () => { it('pool batch with parameters', (done) => { let params = {}; - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) params['initSql'] = 'SET NAMES UTF8'; - const pool = base.createPool(params); pool .query('DROP TABLE IF EXISTS pool_parse_batch') @@ -238,9 +227,6 @@ describe('sql template strings', () => { it('pool callback batch with parameters', (done) => { let params = {}; - // https://jira.mariadb.org/browse/XPT-266 - if (isXpand()) params['initSql'] = 'SET NAMES UTF8'; - const pool = base.createPoolCallback(params); pool.query('drop table pool_batch_call', (err) => { pool.query('CREATE TABLE pool_batch_call(t varchar(128))', (err, res) => { diff --git a/test/integration/test-query.js b/test/integration/test-query.js index 9eb033fe..b6d128d4 100644 --- a/test/integration/test-query.js +++ b/test/integration/test-query.js @@ -36,18 +36,20 @@ describe('basic query', () => { assert.equal(rows.meta.length, 1); assert.equal(JSON.stringify(rows), '[{"a":null}]'); assert.deepStrictEqual(rows, [{ a: null }]); + let nb = 0; for (var propertyName in rows) { - console.log(propertyName); + nb++; } + assert.equal(nb, 1); rows = await shareConn.query({ sql: 'select ? as a', metaEnumerable: true }, null); assert.equal(rows.meta.length, 1); - console.log(rows); - console.log(JSON.stringify(rows)); + nb = 0; for (var propertyName in rows) { - console.log(propertyName); + nb++; } + assert.equal(nb, 2); }); it('parameter last', async () => { @@ -100,7 +102,7 @@ describe('basic query', () => { await conn.query('CREATE TABLE arrayParam (id int, val varchar(10))'); await conn.beginTransaction(); await conn.query("INSERT INTO arrayParam VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')"); - const rows = await conn.query('SELECT * FROM arrayParam WHERE val IN (?)', [['b', 'c', 1]]); + const rows = await conn.query('SELECT * FROM arrayParam WHERE val IN (?)', [['b', 'c', '1']]); assert.deepEqual(rows, [ { id: 2, @@ -358,6 +360,8 @@ describe('basic query', () => { }); it('timeout', function (done) { + // xpand doesn't support timeout + if (isXpand()) this.skip(); this.timeout(20000); const initTime = Date.now(); const query = @@ -372,6 +376,8 @@ describe('basic query', () => { }); it('timeout with parameter', function (done) { + // xpand doesn't support timeout + if (isXpand()) this.skip(); this.timeout(20000); const initTime = Date.now(); const query = diff --git a/test/integration/test-ssl.js b/test/integration/test-ssl.js index ee44a525..0b2cd180 100644 --- a/test/integration/test-ssl.js +++ b/test/integration/test-ssl.js @@ -5,6 +5,7 @@ const { assert } = require('chai'); const fs = require('fs'); const Conf = require('../conf'); const tls = require('tls'); +const { isXpand } = require('../base'); describe('ssl', function () { let ca = Conf.baseConfig.ssl && Conf.baseConfig.ssl.ca ? Conf.baseConfig.ssl.ca : null; @@ -15,6 +16,7 @@ describe('ssl', function () { let sslPort = Conf.baseConfig.port; before(function (done) { + if (isXpand()) this.skip(); if (process.env.TEST_MAXSCALE_TLS_PORT) sslPort = parseInt(process.env.TEST_MAXSCALE_TLS_PORT); if ( tls.DEFAULT_MIN_VERSION === 'TLSv1.2' && diff --git a/test/unit/config/test-options.js b/test/unit/config/test-options.js index 8b032f55..08585a7e 100644 --- a/test/unit/config/test-options.js +++ b/test/unit/config/test-options.js @@ -6,7 +6,7 @@ const baseCallback = require('../../../callback'); const Collations = require('../../../lib/const/collations.js'); describe('test options', () => { - it('default options', () => { + it('default options', function () { if (process.env.srv === 'xpand') this.skip(); const defaultOpts = basePromise.defaultOptions({ timezone: '+00:00' }); const defaultOptsCall = baseCallback.defaultOptions({ timezone: '+00:00' });