feat: add mysql8 support, update to mysql2 client and drop nodejs v14 and mysql5.7 support

BREAKING CHANGE
drop mysql5.7 support

Signed-off-by: Samarpan Bhattacharya <this.is.samy@gmail.com>
This commit is contained in:
Samarpan Bhattacharya 2023-06-18 23:34:31 +05:30
parent ab28f74fb4
commit 07173b6bde
12 changed files with 269 additions and 170 deletions

View File

@ -11,12 +11,11 @@ env:
jobs:
build:
# ubuntu support dropped due to https://github.com/ankane/setup-mysql/commit/70636bf8d2c54521a1b871af766b58d76b468d94
runs-on: macos-12
runs-on: ubuntu-20.04
strategy:
matrix:
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
node-version: [14, 16]
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
@ -25,7 +24,7 @@ jobs:
node-version: ${{ matrix.node-version }}
- uses: ankane/setup-mysql@v1
with:
mysql-version: 5.7
mysql-version: 8.0
- run: |
sudo mysql -e "CREATE USER '${{ secrets.MYSQL_USER }}'@'localhost' IDENTIFIED BY '${{ secrets.MYSQL_PASSWORD }}'"
sudo mysql -u root -e "GRANT ALL PRIVILEGES ON *.* TO '${{ secrets.MYSQL_USER }}'@'localhost'"
@ -34,6 +33,7 @@ jobs:
- run: npm install
- run: npm test
env:
MYSQL_HOST: '127.0.0.1'
MYSQL_USER: ${{ secrets.MYSQL_USER }}
MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }}
CI: true
@ -42,11 +42,11 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v2
- name: Use Node.js 14
uses: actions/setup-node@v2
- uses: actions/checkout@v3
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 14
node-version: 18
- name: Bootstrap project
run: |
npm ci --ignore-scripts
@ -58,13 +58,13 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Use Node.js 14
uses: actions/setup-node@v2
- name: Use Node.js 18
uses: actions/setup-node@v3
with:
node-version: 14
node-version: 18
- name: Bootstrap project
run: |
npm ci --ignore-scripts

View File

@ -12,7 +12,7 @@ In your application root directory, enter this command to install the connector:
npm install loopback-connector-mysql --save
```
**Note**: The MySQL connector requires MySQL 5.0+.
**Note**: Since `loopback-connector-mysql` v7.x.x, this MySQL connector has dropped support for MySQL 5.7 and requires MySQL 8.0+.
This installs the module from npm and adds it as a dependency to the
application's `package.json` file.

View File

@ -893,7 +893,11 @@ function mixinMigration(MySQL, mysql) {
case 'int':
case 'integer':
case 'bigint':
columnType = integerOptions(p, columnType);
// As of MySQL 8.0.17, the display width attribute is deprecated for integer data types;
// you should expect support for it to be removed in a future version of MySQL.
// As of MySQL 8.0.17, the UNSIGNED attribute is deprecated for columns of type FLOAT, DOUBLE, and DECIMAL (and any synonyms);
// you should expect support for it to be removed in a future version of MySQL.
columnType = unsigned(p, columnType);
break;
case 'decimal':
@ -906,7 +910,6 @@ function mixinMigration(MySQL, mysql) {
columnType = floatingPointOptions(p, columnType);
break;
}
columnType = unsigned(p, columnType);
return columnType;
}
@ -944,55 +947,6 @@ function mixinMigration(MySQL, mysql) {
return columnType;
}
function integerOptions(p, columnType) {
let tmp = 0;
if (p.display || p.limit) {
tmp = Number(p.display || p.limit);
}
if (tmp > 0) {
columnType += '(' + tmp + ')';
} else if (p.unsigned) {
switch (columnType.toLowerCase()) {
default:
case 'int':
columnType += '(10)';
break;
case 'mediumint':
columnType += '(8)';
break;
case 'smallint':
columnType += '(5)';
break;
case 'tinyint':
columnType += '(3)';
break;
case 'bigint':
columnType += '(20)';
break;
}
} else {
switch (columnType.toLowerCase()) {
default:
case 'int':
columnType += '(11)';
break;
case 'mediumint':
columnType += '(9)';
break;
case 'smallint':
columnType += '(6)';
break;
case 'tinyint':
columnType += '(4)';
break;
case 'bigint':
columnType += '(20)';
break;
}
}
return columnType;
}
function dateOptionsByType(p, columnType) {
switch (columnType.toLowerCase()) {
default:

View File

@ -9,7 +9,7 @@ const g = require('strong-globalize')();
/*!
* Module dependencies
*/
const mysql = require('mysql');
const mysql = require('mysql2');
const SqlConnector = require('loopback-connector').SqlConnector;
const ParameterizedSQL = SqlConnector.ParameterizedSQL;
@ -353,6 +353,11 @@ MySQL.prototype.toColumnValue = function(prop, val) {
if (val === null) {
if (this.isNullable(prop)) {
return val;
} else if (prop.type === Date) {
// MySQL has disallowed comparison of date types with strings.
// https://bugs.mysql.com/bug.php?id=95466
// https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-16.html
return new Date();
} else {
try {
const castNull = prop.type(val);
@ -569,19 +574,28 @@ MySQL.prototype.buildExpression = function(columnName, operator, operatorValue,
let clause;
switch (operator) {
case 'regexp':
clause = columnName + ' REGEXP ?';
// By default, MySQL regexp is not case sensitive. (https://dev.mysql.com/doc/refman/5.7/en/regexp.html)
// To allow case sensitive regexp query, it has to be binded to a `BINARY` type.
// If ignore case is not specified, search it as case sensitive.
if (!operatorValue.ignoreCase) {
clause = columnName + ' REGEXP BINARY ?';
// https://dev.mysql.com/doc/refman/8.0/en/regexp.html#function_regexp-like
// REGEXP_LIKE(expr, pat[, match_type]) - match_type parameter now support c,i and m flags of RegExp
let matchType = '';
if (operatorValue.ignoreCase === false) {
matchType += 'c';
} else if (operatorValue.ignoreCase === true) {
matchType += 'i';
}
if (operatorValue.global)
g.warn('{{MySQL}} {{regex}} syntax does not respect the {{`g`}} flag');
if (operatorValue.multiline) {
matchType += 'm';
}
if (operatorValue.multiline)
g.warn('{{MySQL}} {{regex}} syntax does not respect the {{`m`}} flag');
if (operatorValue.global) {
g.warn('{{MySQL}} {{regex}} syntax does not respect the {{`g`}} flag');
}
if (!!matchType) {
clause = `REGEXP_LIKE(${columnName}, ?, '${matchType}')`;
} else {
clause = `REGEXP_LIKE(${columnName}, ?)`;
}
return new ParameterizedSQL(clause,
[operatorValue.source]);

210
package-lock.json generated
View File

@ -13,7 +13,7 @@
"debug": "^4.1.1",
"lodash": "^4.17.11",
"loopback-connector": "^5.2.0",
"mysql": "^2.11.1",
"mysql2": "^3.3.5",
"strong-globalize": "^6.0.4"
},
"devDependencies": {
@ -373,14 +373,6 @@
"node": ">=0.10"
}
},
"node_modules/bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
"engines": {
"node": "*"
}
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -694,6 +686,14 @@
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -1152,6 +1152,14 @@
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
"node_modules/generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
"dependencies": {
"is-property": "^1.0.2"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -1312,6 +1320,17 @@
"node": ">=8.12.0"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ignore": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
@ -1460,6 +1479,11 @@
"node": ">=8"
}
},
"node_modules/is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
},
"node_modules/is-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
@ -1589,6 +1613,11 @@
"node": ">=10"
}
},
"node_modules/long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
},
"node_modules/loopback-connector": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/loopback-connector/-/loopback-connector-5.2.0.tgz",
@ -1893,24 +1922,50 @@
"safe-buffer": "^5.1.2"
}
},
"node_modules/mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"node_modules/mysql2": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.3.5.tgz",
"integrity": "sha512-ZTQGAzxGeaX1PyeSiZFCgQ34uiXguaEpn3aTFN9Enm9JDnbwWo+4/CJnDdQZ3n0NaMeysi8vwtW/jNUb9VqVDw==",
"dependencies": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
"denque": "^2.1.0",
"generate-function": "^2.3.1",
"iconv-lite": "^0.6.3",
"long": "^5.2.1",
"lru-cache": "^8.0.0",
"named-placeholders": "^1.1.3",
"seq-queue": "^0.0.5",
"sqlstring": "^2.3.2"
},
"engines": {
"node": ">= 0.6"
"node": ">= 8.0"
}
},
"node_modules/mysql/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"node_modules/mysql2/node_modules/lru-cache": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
"engines": {
"node": ">=16.14"
}
},
"node_modules/named-placeholders": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
"integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
"dependencies": {
"lru-cache": "^7.14.1"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/named-placeholders/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"engines": {
"node": ">=12"
}
},
"node_modules/nanoid": {
"version": "3.1.20",
@ -2386,6 +2441,11 @@
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -2412,6 +2472,11 @@
"upper-case-first": "^2.0.2"
}
},
"node_modules/seq-queue": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
},
"node_modules/serialize-javascript": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
@ -2573,9 +2638,9 @@
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"node_modules/sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
"engines": {
"node": ">= 0.6"
}
@ -3280,11 +3345,6 @@
"resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz",
"integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4="
},
"bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@ -3537,6 +3597,11 @@
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
"denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@ -3887,6 +3952,14 @@
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
"generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
"requires": {
"is-property": "^1.0.2"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@ -3999,6 +4072,14 @@
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="
},
"iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
"ignore": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
@ -4105,6 +4186,11 @@
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"dev": true
},
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
},
"is-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
@ -4213,6 +4299,11 @@
"chalk": "^4.0.0"
}
},
"long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
},
"loopback-connector": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/loopback-connector/-/loopback-connector-5.2.0.tgz",
@ -4448,21 +4539,40 @@
"safe-buffer": "^5.1.2"
}
},
"mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"mysql2": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.3.5.tgz",
"integrity": "sha512-ZTQGAzxGeaX1PyeSiZFCgQ34uiXguaEpn3aTFN9Enm9JDnbwWo+4/CJnDdQZ3n0NaMeysi8vwtW/jNUb9VqVDw==",
"requires": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
"denque": "^2.1.0",
"generate-function": "^2.3.1",
"iconv-lite": "^0.6.3",
"long": "^5.2.1",
"lru-cache": "^8.0.0",
"named-placeholders": "^1.1.3",
"seq-queue": "^0.0.5",
"sqlstring": "^2.3.2"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"lru-cache": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA=="
}
}
},
"named-placeholders": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
"integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
"requires": {
"lru-cache": "^7.14.1"
},
"dependencies": {
"lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="
}
}
},
@ -4817,6 +4927,11 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -4837,6 +4952,11 @@
"upper-case-first": "^2.0.2"
}
},
"seq-queue": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
},
"serialize-javascript": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz",
@ -4978,9 +5098,9 @@
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="
},
"stable": {
"version": "0.1.8",

View File

@ -3,7 +3,7 @@
"version": "6.2.0",
"description": "MySQL connector for loopback-datasource-juggler",
"engines": {
"node": "14 || 16"
"node": ">=16"
},
"main": "index.js",
"scripts": {
@ -24,7 +24,7 @@
"debug": "^4.1.1",
"lodash": "^4.17.11",
"loopback-connector": "^5.2.0",
"mysql": "^2.11.1",
"mysql2": "^3.3.5",
"strong-globalize": "^6.0.4"
},
"devDependencies": {

View File

@ -48,9 +48,7 @@ printf "\n${RED}>> Finding old builds and cleaning up${PLAIN} ${GREEN}...${PLAIN
docker rm -f $MYSQL_CONTAINER > /dev/null 2>&1
printf "\n${CYAN}Clean up complete.${PLAIN}\n"
## Pin mysql docker image to version as `mysql` node.js driver does not support v8 yet
## See https://github.com/mysqljs/mysql/issues/2002
DOCKER_IMAGE=mysql:5.7.22
DOCKER_IMAGE=mysql:8.0
## pull latest mysql image
printf "\n${RED}>> Pulling ${DOCKER_IMAGE} image${PLAIN} ${GREEN}...${PLAIN}"

View File

@ -45,10 +45,10 @@ describe('connections', function() {
});
it('should use utf8 charset', function(done) {
const test_set = /utf8/;
const test_collo = /utf8_general_ci/;
const test_set_str = 'utf8';
const test_set_collo = 'utf8_general_ci';
const test_set = /utf8mb4/;
const test_collo = /utf8mb4_0900_ai_ci/;
const test_set_str = 'utf8mb4';
const test_set_collo = 'utf8mb4_0900_ai_ci';
charsetTest(test_set, test_collo, test_set_str, test_set_collo, done);
});

View File

@ -23,7 +23,8 @@ describe('MySQL datetime handling', function() {
// Modifying the connection timezones mid-flight is a pain,
// but closing the existing connection requires more effort.
function setConnectionTimezones(tz) {
db.connector.client._allConnections.forEach(function(con) {
// _allConnections is a Queue in mysql2 library
db.connector.client._allConnections.toArray().forEach(function(con) {
con.config.timezone = tz;
});
}

View File

@ -27,7 +27,7 @@ describe('migrations', function() {
should.not.exist(err);
should.exist(result);
result[0].Key.should.equal('PRI');
result[0].Type.should.equal('bigint(20)');
result[0].Type.should.equal('bigint');
done();
});
});
@ -38,7 +38,7 @@ describe('migrations', function() {
fields.should.be.eql({
id: {
Field: 'id',
Type: 'int(11)',
Type: 'int',
Null: 'NO',
Key: 'PRI',
Default: null,
@ -73,7 +73,7 @@ describe('migrations', function() {
Extra: ''},
pendingPeriod: {
Field: 'pendingPeriod',
Type: 'int(11)',
Type: 'int',
Null: 'YES',
Key: '',
Default: null,
@ -197,25 +197,25 @@ describe('migrations', function() {
getFields('NumberData', function(err, fields) {
fields.should.be.eql({
id: {Field: 'id',
Type: 'int(11)',
Type: 'int',
Null: 'NO',
Key: 'PRI',
Default: null,
Extra: 'auto_increment'},
number: {Field: 'number',
Type: 'decimal(10,3) unsigned',
Type: 'decimal(10,3)',
Null: 'NO',
Key: 'MUL',
Default: null,
Extra: ''},
tinyInt: {Field: 'tinyInt',
Type: 'tinyint(2)',
Type: 'tinyint',
Null: 'YES',
Key: '',
Default: null,
Extra: ''},
mediumInt: {Field: 'mediumInt',
Type: 'mediumint(8) unsigned',
Type: 'mediumint unsigned',
Null: 'NO',
Key: '',
Default: null,
@ -235,7 +235,7 @@ describe('migrations', function() {
getFields('DateData', function(err, fields) {
fields.should.be.eql({
id: {Field: 'id',
Type: 'int(11)',
Type: 'int',
Null: 'NO',
Key: 'PRI',
Default: null,
@ -290,8 +290,8 @@ describe('migrations', function() {
// add new column
assert.ok(fields.newProperty, 'New column was not added');
if (fields.newProperty) {
assert.equal(fields.newProperty.Type, 'bigint(20) unsigned',
'New column type is not bigint(20) unsigned');
assert.equal(fields.newProperty.Type, 'bigint unsigned',
'New column type is not bigint unsigned');
}
// drop column - will not happen.
// assert.ok(!fields.pendingPeriod,
@ -400,7 +400,7 @@ describe('migrations', function() {
getFields('DefaultData', function(err, fields) {
fields.should.be.eql({
id: {Field: 'id',
Type: 'int(11)',
Type: 'int',
Null: 'NO',
Key: 'PRI',
Default: null,
@ -410,13 +410,13 @@ describe('migrations', function() {
Null: 'YES',
Key: '',
Default: 'CURRENT_TIMESTAMP',
Extra: ''},
Extra: 'DEFAULT_GENERATED'},
timestamp: {Field: 'timestamp',
Type: 'timestamp',
Null: 'YES',
Key: '',
Default: 'CURRENT_TIMESTAMP',
Extra: ''},
Extra: 'DEFAULT_GENERATED'},
isAdmin: {Field: 'isAdmin',
Type: 'tinyint(1)',
Null: 'YES',
@ -424,7 +424,7 @@ describe('migrations', function() {
Default: '0',
Extra: ''},
number: {Field: 'number',
Type: 'int(10) unsigned',
Type: 'int unsigned',
Null: 'NO',
Key: 'MUL',
Default: '256',
@ -502,7 +502,7 @@ describe('migrations', function() {
query('INSERT INTO `DateData` ' +
'(`dateTime`, `timestamp`) ' +
'VALUES("0000-00-00 00:00:00", "0000-00-00 00:00:00") ', function(err) {
const errMsg = 'ER_TRUNCATED_WRONG_VALUE: Incorrect datetime value: ' +
const errMsg = 'Incorrect datetime value: ' +
'\'0000-00-00 00:00:00\' for column \'dateTime\' at row 1';
assert(err);
assert.equal(err.message, errMsg);
@ -518,7 +518,7 @@ describe('migrations', function() {
query('INSERT INTO `DateData` ' +
'(`dateTime`, `timestamp`) ' +
'VALUES("1000-01-01 00:00:00", "0000-00-00 00:00:00") ', function(err) {
const errMsg = 'ER_TRUNCATED_WRONG_VALUE: Incorrect datetime value: ' +
const errMsg = 'Incorrect datetime value: ' +
'\'0000-00-00 00:00:00\' for column \'timestamp\' at row 1';
assert(err);
assert.equal(err.message, errMsg);

View File

@ -222,18 +222,24 @@ describe('MySQL connector', function() {
assert(isActual, 'isActual should return true after automigrate');
ds.discoverModelProperties('customer_test', function(err, props) {
assert.equal(props.length, 5);
// Mysql versions on different OS versions return results in different orders
props.sort(function(a, b) {
return a.columnName > b.columnName ? 1 : -1;
});
const names = props.map(function(p) {
return p.columnName;
});
assert.equal(props[0].nullable, 'N');
assert.equal(names[0], 'age');
assert.equal(names[1], 'customer_discount');
assert.equal(names[2], 'email');
assert.equal(names[3], 'id');
assert.equal(names[4], 'name');
assert.equal(props[0].nullable, 'Y');
assert.equal(props[1].nullable, 'Y');
assert.equal(props[2].nullable, 'N');
assert.equal(props[3].nullable, 'Y');
assert.equal(names[0], 'id');
assert.equal(names[1], 'name');
assert.equal(names[2], 'email');
assert.equal(names[3], 'age');
assert.equal(names[4], 'customer_discount');
assert.equal(props[3].nullable, 'N');
assert.equal(props[4].nullable, 'Y');
ds.connector.execute('SHOW INDEXES FROM customer_test', function(err, indexes) {
if (err) return done(err);
@ -250,16 +256,20 @@ describe('MySQL connector', function() {
ds.discoverModelProperties('customer_test', function(err, props) {
if (err) return done(err);
assert.equal(props.length, 7);
// Mysql versions on different OS versions return results in different orders
props.sort(function(a, b) {
return a.columnName > b.columnName ? 1 : -1;
});
const names = props.map(function(p) {
return p.columnName;
});
assert.equal(names[0], 'id');
assert.equal(names[1], 'email');
assert.equal(names[0], 'customer_address');
assert.equal(names[1], 'customer_code');
assert.equal(names[2], 'customer_discount');
assert.equal(names[3], 'firstName');
assert.equal(names[4], 'lastName');
assert.equal(names[5], 'customer_address');
assert.equal(names[6], 'customer_code');
assert.equal(names[3], 'email');
assert.equal(names[4], 'firstName');
assert.equal(names[5], 'id');
assert.equal(names[6], 'lastName');
ds.connector.execute('SHOW INDEXES FROM customer_test', function(err, updatedindexes) {
if (err) return done(err);
assert(updatedindexes);
@ -268,10 +278,11 @@ describe('MySQL connector', function() {
assert.equal(updatedindexes[1].Column_name, 'customer_code');
assert.equal(updatedindexes[2].Key_name, 'updated_name_index');
assert.equal(updatedindexes[3].Key_name, 'updated_name_index');
// Mysql supports only index sorting in ascending; DESC is ignored
assert.equal(updatedindexes[1].Collation, 'A');
assert.equal(updatedindexes[2].Collation, 'A');
assert.equal(updatedindexes[3].Collation, 'A');
// MySQL8 supports descending indexes:
// DESC in an index definition is no longer ignored but causes storage of key values in descending order.
assert.equal(updatedindexes[3].Collation, 'D');
assert.equal(updatedindexes[1].Non_unique, 0);
assert.equal(updatedindexes[2].Non_unique, 0);
assert.equal(updatedindexes[3].Non_unique, 0);
@ -761,7 +772,7 @@ describe('MySQL connector', function() {
// validate that the foreign key exists and points to the right column
assert(createTable);
assert(createTable.length.should.be.equal(1));
assert(/ON DELETE CASCADE ON UPDATE NO ACTION/.test(createTable[0]['Create Table']), 'Constraint must have correct trigger');
assert(/ON DELETE CASCADE/.test(createTable[0]['Create Table']), 'Constraint must have correct trigger');
ds.createModel(schema_v2.name, schema_v2.properties, schema_v2.options);
ds.isActual(function(err, isActual) {

View File

@ -103,18 +103,19 @@ describe('Discover models including other users', function() {
it('should return an array of all tables and views', function(done) {
db.discoverModelDefinitions({
all: true,
limit: 3,
}, function(err, models) {
if (err) {
console.error(err);
done(err);
} else {
let others = false;
assert.equal(3, models.length);
models.forEach(function(m) {
models.find(function(m) {
assert(m.owner);
if (m.owner !== 'STRONGLOOP') {
others = true;
return true;
} else {
return false;
}
});
assert(others, 'Should have tables/views owned by others');
@ -133,7 +134,7 @@ describe('Discover model properties', function() {
done(err);
} else {
models.forEach(function(m) {
assert(m.tableName === 'product');
assert(m.tableName.toLowerCase() === 'product');
});
done(null, models);
}
@ -150,7 +151,7 @@ describe('Discover model primary keys', function() {
done(err);
} else {
models.forEach(function(m) {
assert(m.tableName === 'product');
assert(m.tableName.toLowerCase() === 'product');
});
done(null, models);
}
@ -164,7 +165,7 @@ describe('Discover model primary keys', function() {
done(err);
} else {
models.forEach(function(m) {
assert(m.tableName === 'product');
assert(m.tableName.toLowerCase() === 'product');
});
done(null, models);
}
@ -206,7 +207,7 @@ describe('Discover model generated columns', function() {
db.discoverModelProperties('product', function(err, models) {
if (err) return done(err);
models.forEach(function(model) {
assert(model.tableName === 'product');
assert(model.tableName.toLowerCase() === 'product');
assert(!model.generated, 'STRONGLOOP.PRODUCT table should not have generated (identity) columns');
});
done();
@ -216,7 +217,7 @@ describe('Discover model generated columns', function() {
db.discoverModelProperties('testgen', function(err, models) {
if (err) return done(err);
models.forEach(function(model) {
assert(model.tableName === 'testgen');
assert(model.tableName.toLowerCase() === 'testgen');
if (model.columnName === 'ID') {
assert(model.generated, 'STRONGLOOP.TESTGEN.ID should be a generated (identity) column');
}