From 5f437a2107248313e1bb2fddd2daec69440086ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cpranavxc=E2=80=9D?= Date: Thu, 18 Jun 2020 18:57:58 +0530 Subject: [PATCH] fix: handling datatype in older mysql versions(< 5.7.8) fix #50 --- lib/index.js | 55 +++++++++++++--------- lib/utils.js | 128 +++++++++++++++++++++++++++++---------------------- 2 files changed, 106 insertions(+), 77 deletions(-) diff --git a/lib/index.js b/lib/index.js index 79e8eeb..d74a4a1 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,7 +3,7 @@ const Bluebird = require('bluebird'); const knexFactory = require('knex'); -const { resolve } = Bluebird; +const {resolve} = Bluebird; const util = require('util'); const { dateAsISO, @@ -18,6 +18,7 @@ const { isSqlite3, timestampTypeName, expiredCondition, + isDbSupportJSON, } = require('./utils'); const oneDay = 86400000; @@ -100,24 +101,29 @@ module.exports = function (connect) { .hasTable(self.tablename) .then((exists) => { if (!exists && self.createtable) { - return self.knex.schema.createTable(self.tablename, (table) => { - table.string(self.sidfieldname).primary(); - if (isMSSQL(self.knex)) { - table.text('sess').notNullable(); - } else { - table.json('sess').notNullable(); - } - if (isMySQL(self.knex) || isMSSQL(self.knex)) { - table - .dateTime('expired') - .notNullable() - .index(); - } else { - table - .timestamp('expired') - .notNullable() - .index(); - } + return new Promise((res) => { + isDbSupportJSON(self.knex).then((isSupport) => { + return self.knex.schema.createTable(self.tablename, (table) => { + table.string(self.sidfieldname).primary(); + if (isSupport) { + table.json('sess').notNullable(); + } else { + table.text('sess').notNullable(); + } + if (isMySQL(self.knex) || isMSSQL(self.knex)) { + table + .dateTime('expired') + .notNullable() + .index(); + } else { + table + .timestamp('expired') + .notNullable() + .index(); + } + res(); + }); + }); }); } return exists; @@ -170,7 +176,7 @@ module.exports = function (connect) { */ KnexStore.prototype.set = function (sid, sessObject, fn) { const self = this; - const { maxAge } = sessObject.cookie; + const {maxAge} = sessObject.cookie; const now = new Date().getTime(); const expired = maxAge ? now + maxAge : now + oneDay; const sess = JSON.stringify(sessObject); @@ -187,7 +193,8 @@ module.exports = function (connect) { ]) .then(() => [1])) .asCallback(fn)); - } if ( + } + if ( isPostgres(self.knex) && parseFloat(self.knex.client.version) >= 9.2 ) { @@ -199,7 +206,8 @@ module.exports = function (connect) { sess, ])) .asCallback(fn)); - } if (isMySQL(self.knex)) { + } + if (isMySQL(self.knex)) { // mysql/mariaDB optimized query return self.ready.then(() => resolve(self.knex .raw(getMysqlFastQuery(self.tablename, self.sidfieldname), [ @@ -208,7 +216,8 @@ module.exports = function (connect) { sess, ])) .asCallback(fn)); - } if (isMSSQL(self.knex)) { + } + if (isMSSQL(self.knex)) { // mssql optimized query return self.ready.then(() => resolve(self.knex .raw(getMssqlFastQuery(self.tablename, self.sidfieldname), [ diff --git a/lib/utils.js b/lib/utils.js index c40319d..a6f121a 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,4 +1,3 @@ - /* * Returns true if the specified knex instance is using sqlite3. * @return {bool} @@ -26,6 +25,26 @@ function isMSSQL(knex) { return ['mssql'].indexOf(knex.client.dialect) > -1; } +/* + * Returns true if the specified database supports JSON datatype. + * @return {bool} + * @api private + */ +async function isDbSupportJSON(knex) { + if (isMSSQL(knex)) return false; + if (!isMySQL(knex)) return true; + const data = await knex.raw('select version() as version'); + const { version } = data[0][0]; + const extractedVersions = version.split('.'); + // Only mysql version > 5.7.8 supports JSON datatype + return +extractedVersions[0] > 5 + || (extractedVersions[0] === '5' + && (+extractedVersions[1] > 7 + || (+extractedVersions[1] === '7' && +extractedVersions[2] >= 8) + ) + ); +} + /* * Returns true if the specified knex instance is using postgresql. * @return {bool} @@ -78,42 +97,42 @@ function getPostgresFastQuery(tablename, sidfieldname) { `with new_values (${ sidfieldname }, expired, sess) as (` - + ' values (?, ?::timestamp with time zone, ?::json)' - + '), ' - + 'upsert as ' - + '( ' - + ` update ${ - tablename - } cs set ` - + ` ${ - sidfieldname - } = nv.${ - sidfieldname - }, ` - + ' expired = nv.expired, ' - + ' sess = nv.sess ' - + ' from new_values nv ' - + ` where cs.${ - sidfieldname - } = nv.${ - sidfieldname - } ` - + ' returning cs.* ' - + ')' - + `insert into ${ - tablename - } (${ - sidfieldname - }, expired, sess) ` - + `select ${ - sidfieldname - }, expired, sess ` - + 'from new_values ' - + `where not exists (select 1 from upsert up where up.${ - sidfieldname - } = new_values.${ - sidfieldname - })` + + ' values (?, ?::timestamp with time zone, ?::json)' + + '), ' + + 'upsert as ' + + '( ' + + ` update ${ + tablename + } cs set ` + + ` ${ + sidfieldname + } = nv.${ + sidfieldname + }, ` + + ' expired = nv.expired, ' + + ' sess = nv.sess ' + + ' from new_values nv ' + + ` where cs.${ + sidfieldname + } = nv.${ + sidfieldname + } ` + + ' returning cs.* ' + + ')' + + `insert into ${ + tablename + } (${ + sidfieldname + }, expired, sess) ` + + `select ${ + sidfieldname + }, expired, sess ` + + 'from new_values ' + + `where not exists (select 1 from upsert up where up.${ + sidfieldname + } = new_values.${ + sidfieldname + })` ); } @@ -157,23 +176,23 @@ function getMssqlFastQuery(tablename, sidfieldname) { `merge ${ tablename } as T ` - + `using (values (?, ?, ?)) as S (${ - sidfieldname - }, expired, sess) ` - + `on (T.${ - sidfieldname - } = S.${ - sidfieldname - }) ` - + 'when matched then ' - + 'update set expired = S.expired, sess = S.sess ' - + 'when not matched by target then ' - + `insert (${ - sidfieldname - }, expired, sess) values (S.${ - sidfieldname - }, S.expired, S.sess) ` - + 'output inserted.*;' + + `using (values (?, ?, ?)) as S (${ + sidfieldname + }, expired, sess) ` + + `on (T.${ + sidfieldname + } = S.${ + sidfieldname + }) ` + + 'when matched then ' + + 'update set expired = S.expired, sess = S.sess ' + + 'when not matched by target then ' + + `insert (${ + sidfieldname + }, expired, sess) values (S.${ + sidfieldname + }, S.expired, S.sess) ` + + 'output inserted.*;' ); } @@ -221,4 +240,5 @@ module.exports = { isSqlite3, timestampTypeName, expiredCondition, + isDbSupportJSON, };