diff --git a/.eslintrc b/.eslintrc index b532eb41..c06c22b9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,7 +10,7 @@ "plugin:prettier/recommended" ], "parserOptions": { - "ecmaVersion": 6 + "ecmaVersion": 2020 }, "env": { "node": true, diff --git a/.travis.yml b/.travis.yml index a925866f..c471a27a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,27 +41,25 @@ matrix: allow_failures: - node_js: "12" env: DB=build SKIP_LEAK=1 - - node_js: "12" - env: SKYSQL=true SKIP_LEAK=1 include: - node_js: "12" env: DB=build SKIP_LEAK=1 - node_js: "12" env: SKYSQL=true SKIP_LEAK=1 - node_js: "10" - env: DB=mariadb:10.4 + env: DB=mariadb:10.5 - node_js: "12" - env: DB=mariadb:10.4 SKIP_LEAK=1 ADDITIONAL_CONF=--version=10.4.199 + env: DB=mariadb:10.5 SKIP_LEAK=1 ADDITIONAL_CONF=--version=10.5.199 - node_js: "14" - env: DB=mariadb:10.4 SKIP_LEAK=1 + env: DB=mariadb:10.5 SKIP_LEAK=1 - node_js: "12" - env: DB=mariadb:10.4 SKIP_LEAK=1 TEST_COLLATION=cp1251_general_ci + env: DB=mariadb:10.5 SKIP_LEAK=1 TEST_COLLATION=cp1251_general_ci - node_js: "12" - env: DB=mariadb:10.4 LINT=1 SKIP_LEAK=1 + env: DB=mariadb:10.5 LINT=1 SKIP_LEAK=1 - node_js: "12" - env: DB=mariadb:10.4 BENCH=1 SKIP_LEAK=1 + env: DB=mariadb:10.5 BENCH=1 SKIP_LEAK=1 - node_js: "12" - env: DB=mariadb:10.4 MAXSCALE_VERSION=2.2.9 TEST_PORT=4007 TEST_USER=bob TEXT_DATABASE=test2 SKIP_LEAK=1 + env: DB=mariadb:10.5 MAXSCALE_VERSION=2.5.4 MAXSCALE_TEST_DISABLE=true SKIP_LEAK=1 - node_js: "12" env: DB=mariadb:10.1 SKIP_LEAK=1 - node_js: "12" @@ -69,7 +67,7 @@ matrix: - node_js: "12" env: DB=mariadb:10.3 SKIP_LEAK=1 - node_js: "12" - env: DB=mysql:5.6 SKIP_LEAK=1 + env: DB=mariadb:10.4 SKIP_LEAK=1 - node_js: "12" env: DB=mysql:5.7 SKIP_LEAK=1 - node_js: "12" diff --git a/.travis/docker-compose.yml b/.travis/docker-compose.yml index 69129be8..e9664e14 100644 --- a/.travis/docker-compose.yml +++ b/.travis/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2' +version: '2.1' services: db: image: $DB @@ -7,11 +7,14 @@ services: - 3305:3306 volumes: - $SSLCERT:/etc/sslcert - - $ENTRYPOINT:/pam + - $ENTRYPOINT:/docker-entrypoint-initdb.d environment: MYSQL_DATABASE: testn MYSQL_ALLOW_EMPTY_PASSWORD: 1 - MYSQL_ROOT_PASSWORD: - + healthcheck: + test: ["CMD", "mysql", "--protocol=tcp", "-ubob", "-h127.0.0.1"] + timeout: 50s + retries: 10 + interval: 5s diff --git a/.travis/entrypoint/dbinit.sql b/.travis/entrypoint/dbinit.sql deleted file mode 100644 index 665bca2a..00000000 --- a/.travis/entrypoint/dbinit.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE USER 'bob'@'%'; -GRANT ALL ON *.* TO 'bob'@'%' with grant option; - -CREATE USER 'boby'@'%' identified by 'heyPassw0@rd'; -GRANT ALL ON *.* TO 'boby'@'%' with grant option; - -INSTALL PLUGIN pam SONAME 'auth_pam'; - -FLUSH PRIVILEGES; - -CREATE DATABASE test2; \ No newline at end of file diff --git a/.travis/maxscale-compose.yml b/.travis/maxscale-compose.yml index 274e8acc..842f77da 100644 --- a/.travis/maxscale-compose.yml +++ b/.travis/maxscale-compose.yml @@ -1,20 +1,8 @@ version: '2.1' services: - maxscale: - depends_on: - - db - ports: - - 4006:4006 - - 4007:4007 - - 4008:4008 - build: - context: . - dockerfile: maxscale/Dockerfile - args: - MAXSCALE_VERSION: $MAXSCALE_VERSION db: image: $DB - command: --max-connections=500 --max-allowed-packet=40m --innodb-log-file-size=400m --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --ssl-ca=/etc/sslcert/ca.crt --ssl-cert=/etc/sslcert/server.crt --ssl-key=/etc/sslcert/server.key --bind-address=0.0.0.0 + command: --innodb-log-file-size=400m --max-allowed-packet=40m --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --ssl-ca=/etc/sslcert/ca.crt --ssl-cert=/etc/sslcert/server.crt --ssl-key=/etc/sslcert/server.key --bind-address=0.0.0.0 $ADDITIONAL_CONF ports: - 3305:3306 volumes: @@ -23,3 +11,26 @@ services: environment: MYSQL_DATABASE: testn MYSQL_ALLOW_EMPTY_PASSWORD: 1 + healthcheck: + test: ["CMD", "mysql", "--protocol=tcp", "-ubob", "-h127.0.0.1"] + timeout: 50s + retries: 10 + interval: 5s + + maxscale: + depends_on: + db: + condition: service_healthy + links: + - "db:database" + ports: + - 4006:4006 + - 4008:4008 + - 4009:4009 + volumes: + - $SSLCERT:/etc/sslcert + build: + context: . + dockerfile: maxscale/Dockerfile + args: + MAXSCALE_VERSION: $MAXSCALE_VERSION diff --git a/.travis/maxscale/Dockerfile b/.travis/maxscale/Dockerfile index 61e41817..4c6009a2 100644 --- a/.travis/maxscale/Dockerfile +++ b/.travis/maxscale/Dockerfile @@ -1,12 +1,11 @@ FROM centos:7 ARG MAXSCALE_VERSION -ENV MAXSCALE_VERSION ${MAXSCALE_VERSION:-2.2.9} +ENV MAXSCALE_VERSION ${MAXSCALE_VERSION:-2.5.4} COPY maxscale/mariadb.repo /etc/yum.repos.d/ - RUN rpm --import https://yum.mariadb.org/RPM-GPG-KEY-MariaDB \ - && yum -y install https://downloads.mariadb.com/MaxScale/${MAXSCALE_VERSION}/centos/7/x86_64/maxscale-${MAXSCALE_VERSION}-1.centos.7.x86_64.rpm \ + && yum -y install https://downloads.mariadb.com/MaxScale/${MAXSCALE_VERSION}/centos/7/x86_64/maxscale-${MAXSCALE_VERSION}-1.rhel.7.x86_64.rpm \ && yum -y update RUN yum -y install maxscale-${MAXSCALE_VERSION} MariaDB-client \ @@ -14,8 +13,8 @@ RUN yum -y install maxscale-${MAXSCALE_VERSION} MariaDB-client \ && rm -rf /tmp/* COPY maxscale/docker-entrypoint.sh / -RUN chmod 777 /etc/maxscale.cnf COPY maxscale/maxscale.cnf /etc/ +RUN chmod 777 /etc/maxscale.cnf RUN chmod 777 /docker-entrypoint.sh diff --git a/.travis/maxscale/maxscale.cnf b/.travis/maxscale/maxscale.cnf index 59788bd1..46254ac8 100644 --- a/.travis/maxscale/maxscale.cnf +++ b/.travis/maxscale/maxscale.cnf @@ -1,45 +1,59 @@ -# MaxScale documentation on GitHub: -# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Documentation-Contents.md +# MaxScale documentation: +# https://mariadb.com/kb/en/mariadb-maxscale-24/ # Global parameters # # Complete list of configuration options: -# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Getting-Started/Configuration-Guide.md - +# https://mariadb.com/kb/en/mariadb-maxscale-24-mariadb-maxscale-configuration-guide/ [maxscale] -threads=2 -log_messages=1 -log_trace=1 -log_debug=1 +threads=auto # Server definitions # # Set the address of the server to the network -# address of a MySQL server. +# address of a MariaDB server. # +[server2] +type=server +address=database +port=3306 +protocol=MariaDBBackend +ssl=true +ssl_ca_cert=/etc/sslcert/server.crt +ssl_cert=/etc/sslcert/client.crt +ssl_key=/etc/sslcert/client.key + + [server1] type=server address=db port=3306 protocol=MariaDBBackend -authenticator_options=skip_authentication=true -router_options=master + # Monitor for the servers # # This will keep MaxScale aware of the state of the servers. -# MySQL Monitor documentation: -# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Monitors/MySQL-Monitor.md +# MariaDB Monitor documentation: +# https://mariadb.com/kb/en/mariadb-maxscale-24-mariadb-monitor/ -[MySQLMonitor] +[MariaDB-Monitor] type=monitor module=mariadbmon servers=server1 user=boby -passwd=heyPassw0@rd -monitor_interval=10000 +password=heyPassw0@rd +monitor_interval=2000 + +[MariaDB-Monitor2] +type=monitor +module=mariadbmon +servers=server2 +user=boby +password=heyPassw0@rd +monitor_interval=2000 # Service definitions # @@ -48,78 +62,61 @@ monitor_interval=10000 # # ReadConnRoute documentation: -# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Routers/ReadConnRoute.md +# https://mariadb.com/kb/en/mariadb-maxscale-24-readconnroute/ -[Read-OnlyService] -enable_root_user=1 -version_string=10.4.99-MariaDB-maxScale +[Read-Only-Service] type=service -router=readconnroute -servers=server1 +router=readwritesplit +version_string=10.5.99-MariaDB-maxScale +servers=server2 user=boby -passwd=heyPassw0@rd +password=heyPassw0@rd router_options=slave -localhost_match_wildcard_host=1 -[Read-WriteService] -enable_root_user=1 -version_string=10.4.99-MariaDB-maxScale +# ReadWriteSplit documentation: +# https://mariadb.com/kb/en/mariadb-maxscale-24-readwritesplit/ + +[Read-Write-Service] type=service router=readwritesplit servers=server1 +version_string=10.5.99-MariaDB-maxScale user=boby -passwd=heyPassw0@rd -localhost_match_wildcard_host=1 +password=heyPassw0@rd -[WriteService] +[Read-Write-Service2] type=service -router=readconnroute -servers=server1 +router=readwritesplit +version_string=10.5.99-MariaDB-maxScale +servers=server2 user=boby -passwd=heyPassw0@rd -router_options=master -localhost_match_wildcard_host=1 -version_string=10.4.99-MariaDB-maxScale - - -# This service enables the use of the MaxAdmin interface -# MaxScale administration guide: -# https://github.com/mariadb-corporation/MaxScale/blob/2.1/Documentation/Reference/MaxAdmin.mda - -[MaxAdminService] -enable_root_user=1 -version_string=10.4.99-MariaDB-maxScale -type=service -router=cli +password=heyPassw0@rd # Listener definitions for the services # # These listeners represent the ports the # services will listen on. # -[WriteListener] -type=listener -service=WriteService -protocol=MariaDBClient -port=4007 -#socket=/var/lib/maxscale/writeconn.sock -[Read-OnlyListener] +[Read-Only-Listener] type=listener -service=Read-OnlyService +service=Read-Only-Service protocol=MariaDBClient port=4008 -#socket=/var/lib/maxscale/readconn.sock -[Read-WriteListener] +[Read-Write-Listener] type=listener -service=Read-WriteService +service=Read-Write-Service protocol=MariaDBClient port=4006 -#socket=/var/lib/maxscale/rwsplit.sock -[MaxAdminListener] + +[Read-Write-Listener2] type=listener -service=MaxAdminService -protocol=maxscaled -socket=/tmp/maxadmin.sock +service=Read-Write-Service2 +protocol=MariaDBClient +port=4009 +ssl=true +ssl_ca_cert=/etc/sslcert/ca.crt +ssl_cert=/etc/sslcert/server.crt +ssl_key=/etc/sslcert/server.key diff --git a/.travis/script.sh b/.travis/script.sh index 9e8083bb..e4082f28 100644 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -9,52 +9,61 @@ set -e if [ -n "$SKYSQL" ] ; then - if [ -z "$SKYSQL_TEST_HOST" ] ; then + if [ -z "$SKYSQL_HOST" ] ; then echo "No SkySQL configuration found !" - exit 1 + exit 0 + else + export TEST_USER=$SKYSQL_USER + export TEST_HOST=$SKYSQL_HOST + export TEST_PASSWORD=$SKYSQL_PASSWORD + export TEST_PORT=$SKYSQL_PORT + export TEST_SSL_CA=$SKYSQL_SSL_CA + export TEST_BULK=false fi - export TEST_USER=$SKYSQL_TEST_USER - export TEST_HOST=$SKYSQL_TEST_HOST - export TEST_PASSWORD=$SKYSQL_TEST_PASSWORD - export TEST_PORT=$SKYSQL_TEST_PORT - export TEST_SSL_CA=$SKYSQL_TEST_SSL_CA - export TEST_BULK=false - else + export TEST_USER=boby + export TEST_PASSWORD=heyPassw0@rd + export TEST_HOST=mariadb.example.com + export COMPOSE_FILE=.travis/docker-compose.yml + export ENTRYPOINT=$PROJ_PATH/.travis/sql if [ "$DB" = "build" ] ; then .travis/build/build.sh docker build -t build:latest --label build .travis/build/ fi - export ENTRYPOINT=$PROJ_PATH/.travis/entrypoint if [ -n "$MAXSCALE_VERSION" ] ; then ################################################################################################################### # launch Maxscale with one server ################################################################################################################### export COMPOSE_FILE=.travis/maxscale-compose.yml - export ENTRYPOINT=$PROJ_PATH/.travis/sql + docker-compose -f ${COMPOSE_FILE} build - docker-compose -f ${COMPOSE_FILE} up -d - else - docker-compose -f .travis/docker-compose.yml up -d + export TEST_PORT=4006 + export TEST_SSL_PORT=4009 fi + docker-compose -f ${COMPOSE_FILE} up -d + if [ -z "$SKIP_LEAK" ] ; then npm install node-memwatch; fi node .travis/wait-for-docker-up.js - - if [ -z "$MAXSCALE_VERSION" ] ; then - docker-compose -f .travis/docker-compose.yml exec -u root db bash /pam/pam.sh - sleep 1 - docker-compose -f .travis/docker-compose.yml stop db - sleep 1 - docker-compose -f .travis/docker-compose.yml up -d - docker-compose -f .travis/docker-compose.yml logs db - node --version - node .travis/wait-for-docker-up.js + docker-compose -f ${COMPOSE_FILE} logs + if [ -n "$MAXSCALE_VERSION" ] ; then + docker-compose -f ${COMPOSE_FILE} exec maxscale tail -n 500 /var/log/maxscale/maxscale.log fi + +# if [ -z "$MAXSCALE_VERSION" ] ; then +# docker-compose -f .travis/docker-compose.yml exec -u root db bash /pam/pam.sh +# sleep 1 +# docker-compose -f .travis/docker-compose.yml stop db +# sleep 1 +# docker-compose -f .travis/docker-compose.yml up -d +# docker-compose -f .travis/docker-compose.yml logs db +# node --version +# node .travis/wait-for-docker-up.js +# fi fi if [ -n "$LINT" ] ; then npm run test:lint; fi diff --git a/.travis/sql/dbinit.sql b/.travis/sql/dbinit.sql index 81084176..9b671f60 100644 --- a/.travis/sql/dbinit.sql +++ b/.travis/sql/dbinit.sql @@ -1,9 +1,15 @@ +CREATE USER 'bob'@'localhost'; +GRANT ALL ON *.* TO 'bob'@'localhost' with grant option; + CREATE USER 'bob'@'%'; GRANT ALL ON *.* TO 'bob'@'%' with grant option; CREATE USER 'boby'@'%' identified by 'heyPassw0@rd'; GRANT ALL ON *.* TO 'boby'@'%' with grant option; +CREATE USER 'boby'@'localhost' identified by 'heyPassw0@rd'; +GRANT ALL ON *.* TO 'boby'@'localhost' with grant option; + FLUSH PRIVILEGES; CREATE DATABASE test2; \ No newline at end of file diff --git a/.travis/entrypoint/pam.sh b/.travis/sql/pam.sh similarity index 100% rename from .travis/entrypoint/pam.sh rename to .travis/sql/pam.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fec257b..40522caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # Change Log +## [2.5.0](https://github.com/mariadb-corporation/mariadb-connector-nodejs/tree/2.5.0) (15 Oct 2020) +[Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-nodejs/compare/2.4.2...2.5.0) + +* CONJS-148 - permit setting socket keep alive (option `keepAliveDelay`) +* CONJS-145 - batch rewrite error when packet reach maxAllowedPacket +* CONJS-146 - Using callback API, batch, avoid return error if connection not established +* CONJS-144 - TypeScript type ssl wrong definitions +* CONJS-143 - Array parameter escaping differ from mysql/mysql2 +* CONJS-133 - Support ES2020 BigInt object (option `supportBigInt`) +* CONJS-77 - Support MySQL caching_sha256_password authentication +* CONJS-76 - Support MySQL sha256_password authentication + + +New Options + +|option|description|type|default| +|---:|---|:---:|:---:| +| **`arrayParenthesis`** | Indicate if array are included in parenthesis. This option permit compatibility with version < 2.5|*boolean* | false | +| **`rsaPublicKey`** | Indicate path/content to MySQL server RSA public key. use requires Node.js v11.6+ |*string* | | +| **`cachingRsaPublicKey`** | Indicate path/content to MySQL server caching RSA public key. use requires Node.js v11.6+ |*string* | | +| **`allowPublicKeyRetrieval`** | Indicate that if `rsaPublicKey` or `cachingRsaPublicKey` public key are not provided, if client can ask server to send public key. |*boolean* | false | +| **`supportBigInt`** | Whether resultset should return javascript ES2020 [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) for [BIGINT](https://mariadb.com/kb/en/bigint/) data type. This ensures having expected value even for value > 2^53 (see [safe](documentation/connection-options.md#support-for-big-integer) range). |*boolean* | false | +| **`keepAliveDelay`** | permit to enable socket keep alive, setting delay. 0 means not enabled. Keep in mind that this don't reset server [@@wait_timeout](https://mariadb.com/kb/en/library/server-system-variables/#wait_timeout) (use pool option idleTimeout for that). in ms |*int* | | + +CONJS-143 is a breaking change. Queries that have a IN parameter with array parameters format change. +previous format did not accept parenthesis : +``` +conn.query('SELECT * FROM arrayParam WHERE id = ? AND val IN ?', [1, ['b', 'c']]); +``` + +now, format is +``` +conn.query('SELECT * FROM arrayParam WHERE id = ? AND val IN (?)', [1, ['b', 'c']]); +``` +same than mysql/mysql2 drivers. +previous behaviour can be reverted setting option `arrayParenthesis` to true. + + + ## [2.4.2](https://github.com/mariadb-corporation/mariadb-connector-nodejs/tree/2.4.2) (23 Jul 2020) [Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-nodejs/compare/2.4.1...2.4.2) diff --git a/appveyor-download.bat b/appveyor-download.bat index fc12a609..5cc2dbbb 100644 --- a/appveyor-download.bat +++ b/appveyor-download.bat @@ -2,12 +2,11 @@ set archive=http://ftp.hosteurope.de/mirror/archive.mariadb.org//mariadb-%DB%/winx64-packages/mariadb-%DB%-winx64.msi set last=http://mirror.i3d.net/pub/mariadb//mariadb-%DB%/winx64-packages/mariadb-%DB%-winx64.msi -curl -fsS -o server.msi %archive% +curl -fLsS -o server.msi %archive% if %ERRORLEVEL% == 0 goto end - -curl -fsS -o server.msi %last% +curl -fLsS -o server.msi %last% if %ERRORLEVEL% == 0 goto end echo Failure Reason Given is %errorlevel% diff --git a/appveyor.yml b/appveyor.yml index 7a9fb64c..874a27c0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,43 +12,43 @@ environment: TEST_LOG_PACKETS: true matrix: - - DB: '10.4.13' + - DB: '10.5.6' MEM: "21" nodejs_version: "10" - - DB: '10.4.13' + - DB: '10.5.6' MEM: "21" nodejs_version: "12" SKIP_LEAK: "1" - - DB: '10.4.13' + - DB: '10.5.6' nodejs_version: "12" MEM: "21" TEST_ZIP: 1 SKIP_LEAK: "1" - - DB: '10.4.13' + - DB: '10.5.6' nodejs_version: "12" MEM: "21" BENCH: "1" SKIP_LEAK: "1" - - DB: '10.5.4' + - DB: '10.4.15' MEM: "21" nodejs_version: "12" SKIP_LEAK: "1" - - DB: '10.3.23' + - DB: '10.3.24' MEM: "21" nodejs_version: "12" SKIP_LEAK: "1" - - DB: '10.2.32' + - DB: '10.2.33' MEM: "21" nodejs_version: "12" SKIP_LEAK: "1" - - DB: '10.1.45' + - DB: '10.1.46' MEM: "21" nodejs_version: "12" SKIP_LEAK: "1" diff --git a/documentation/connection-options.md b/documentation/connection-options.md index 44a14f4d..c7035446 100644 --- a/documentation/connection-options.md +++ b/documentation/connection-options.md @@ -355,7 +355,13 @@ mariadb.createConnection({ | **permitConnectionWhenExpired** | Permit a user with expired password to connect. Only possible operation in this case will be to change password ('SET PASSWORD=PASSWORD('XXX')')|*boolean* |false| | **forceVersionCheck** | Force server version check by explicitly using SELECT VERSION(), not relying on server initial packet.
Since version 2.2.0 |*boolean* |false| | **checkDuplicate** | Indicate to throw an exception if result-set will not contain some data due to having duplicate identifier.
JSON cannot have multiple identical key, so query like `SELECT 1 as i, 2 as i` cannot result in { i:1, i:2 }, 'i:1' would be skipped.
When `checkDuplicate` is enable (default) driver will throw an error if some data are skipped. Duplication error can be avoided by multiple ways, like using unique aliases or using options [`rowsAsArray`](https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/promise-api.md#rowsasarray) / [`nestTables`](https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/promise-api.md#nestTables) for example
Since version 2.3.0|*boolean* | true | - +| **arrayParenthesis** | Indicate if array are included in parenthesis. This option permit compatibility with version < 2.5|*boolean* | false | +| **autoJsonMap** | indicate if JSON fields for MariaDB server 10.5.2+ results in JSON format (or String if disabled)|*boolean* | true | +| **keepAliveDelay** | permit to enable socket keep alive, setting delay. 0 means not enabled. Keep in mind that this don't reset server [@@wait_timeout](https://mariadb.com/kb/en/library/server-system-variables/#wait_timeout) (use pool option idleTimeout for that). in ms |*int* | | +| **rsaPublicKey** | Indicate path/content to MySQL server RSA public key. use requires Node.js v11.6+ |*string* | | +| **cachingRsaPublicKey** | Indicate path/content to MySQL server caching RSA public key. use requires Node.js v11.6+ |*string* | | +| **allowPublicKeyRetrieval** | Indicate that if `rsaPublicKey` or `cachingRsaPublicKey` public key are not provided, if client can ask server to send public key. |*boolean* | false | +| **supportBigInt** | Whether resultset should return javascript ES2020 [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) for [BIGINT](https://mariadb.com/kb/en/bigint/) data type. This ensures having expected value even for value > 2^53 (see [safe](documentation/connection-options.md#support-for-big-integer) range). |*boolean* | false | ## F.A.Q. diff --git a/documentation/promise-api.md b/documentation/promise-api.md index a6a573f0..27dce51a 100644 --- a/documentation/promise-api.md +++ b/documentation/promise-api.md @@ -460,6 +460,7 @@ connection.query('select * from animals') * [`nestTables`](#nestTables) * [`dateStrings`](#dateStrings) * [`supportBigNumbers`](#supportBigNumbers) +* [`supportBigint`](#supportBigint) * [`bigNumberStrings`](#bigNumberStrings) Those options can be set on the query level, but are usually set at the connection level, and will then apply to all queries. @@ -598,6 +599,16 @@ Whether you want the Connector to retrieve date values as strings, rather than ` Whether the query should return integers as [`Long`](https://www.npmjs.com/package/long) objects when they are not in the [safe](documentation/connection-options.md#support-for-big-integer) range. +#### `supportBigInt` + +*boolean, default: false* + +Whether the query should return javascript ES2020 [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript +/Reference/Global_Objects/BigInt) +for [BIGINT](https://mariadb.com/kb/en/bigint/) data type. +This ensures having expected value even for value > 2^53 (see [safe](documentation/connection-options.md#support-for-big-integer) range). + + #### `bigNumberStrings` *boolean, default: false* diff --git a/lib/cmd/batch-bulk.js b/lib/cmd/batch-bulk.js index a704b2cb..4e1a669e 100644 --- a/lib/cmd/batch-bulk.js +++ b/lib/cmd/batch-bulk.js @@ -12,7 +12,6 @@ const BulkPacket = require('../io/bulk-packet'); class BatchBulk extends CommonBinary { constructor(resolve, reject, options, connOpts, sql, values) { super(resolve, reject, options, connOpts, sql, values); - this.sending = true; this.onPacketReceive = this.readPrepareResultPacket; } @@ -24,6 +23,7 @@ class BatchBulk extends CommonBinary { * @param info connection information */ start(out, opts, info) { + this.sending = true; this.info = info; this.values = this.initialValues; @@ -65,9 +65,14 @@ class BatchBulk extends CommonBinary { out.writeInt8(0x16); out.writeString(questionMarkSql); out.flushBuffer(true); - out.startPacket(this); - this.valueIdx = 0; - this.sendQueries(); + + if (this.opts.pipelining) { + out.startPacket(this); + this.valueIdx = 0; + this.sendQueries(); + } else { + this.out = out; + } } sendQueries() { @@ -130,6 +135,16 @@ class BatchBulk extends CommonBinary { success(val) { this.packet.waitingResponseNo--; + if (!this.opts.pipelining && this.packet.statementId === -1) { + this.packet.statementId = this.statementId; + this.out.startPacket(this); + this.valueIdx = 0; + this.sendQueries(); + this._responseIndex++; + this.onPacketReceive = this.readResponsePacket; + return; + } + if (!this.sending && this.packet.waitingResponseNo === 0) { //send COM_STMT_CLOSE packet if (!this.firstError || !this.firstError.fatal) { diff --git a/lib/cmd/batch-rewrite.js b/lib/cmd/batch-rewrite.js index 10cca146..51743824 100644 --- a/lib/cmd/batch-rewrite.js +++ b/lib/cmd/batch-rewrite.js @@ -13,7 +13,6 @@ const QUOTE = 0x27; class BatchRewrite extends CommonText { constructor(resolve, reject, options, connOpts, sql, values) { super(resolve, reject, options, connOpts, sql, values); - this.sending = true; } /** @@ -24,6 +23,7 @@ class BatchRewrite extends CommonText { * @param info connection information */ start(out, opts, info) { + this.sending = true; this.info = info; if (this.opts.timeout) { const err = Errors.createError( @@ -251,7 +251,7 @@ class BatchRewrite extends CommonText { this.emit('send_end'); this.throwNewError( 'Parameter at position ' + - (val.length + 1) + + val.length + ' is not set for values ' + r + '\n' + diff --git a/lib/cmd/change-user.js b/lib/cmd/change-user.js index bee90b77..da560214 100644 --- a/lib/cmd/change-user.js +++ b/lib/cmd/change-user.js @@ -24,7 +24,7 @@ class ChangeUser extends Handshake { switch (info.defaultPluginName) { case 'mysql_native_password': case '': - authToken = NativePasswordAuth.encryptPassword(pwd, info.seed); + authToken = NativePasswordAuth.encryptPassword(pwd, info.seed, 'sha1'); break; case 'client_ed25519': authToken = Ed25519PasswordAuth.encryptPassword(pwd, info.seed); @@ -39,7 +39,7 @@ class ChangeUser extends Handshake { out.writeString(this.opts.user || ''); out.writeInt8(0); - if (info.serverCapabilities.low & Capabilities.SECURE_CONNECTION) { + if (info.serverCapabilities & Capabilities.SECURE_CONNECTION) { out.writeInt8(authToken.length); out.writeBuffer(authToken, 0, authToken.length); } else { @@ -47,7 +47,7 @@ class ChangeUser extends Handshake { out.writeInt8(0); } - if (info.clientCapabilities.low & Capabilities.CONNECT_WITH_DB) { + if (info.clientCapabilities & Capabilities.CONNECT_WITH_DB) { out.writeString(this.opts.database); out.writeInt8(0); info.database = this.opts.database; @@ -55,12 +55,12 @@ class ChangeUser extends Handshake { out.writeInt16(this.opts.collation.index); - if (info.clientCapabilities.low & Capabilities.PLUGIN_AUTH) { + if (info.clientCapabilities & Capabilities.PLUGIN_AUTH) { out.writeString(info.defaultPluginName); out.writeInt8(0); } - if (this.opts.connectAttributes && info.serverCapabilities.low & Capabilities.CONNECT_ATTRS) { + if (this.opts.connectAttributes && info.serverCapabilities & Capabilities.CONNECT_ATTRS) { out.writeInt8(0xfc); let initPos = out.pos; //save position, assuming connection attributes length will be less than 2 bytes length out.writeInt16(0); diff --git a/lib/cmd/column-definition.js b/lib/cmd/column-definition.js index 1af14235..e208a678 100644 --- a/lib/cmd/column-definition.js +++ b/lib/cmd/column-definition.js @@ -11,7 +11,7 @@ const Capabilities = require('../const/capabilities'); class ColumnDef { constructor(packet, info) { this._parse = new StringParser(packet); - if (info.serverCapabilities.high & Capabilities.MARIADB_CLIENT_EXTENDED_TYPE_INFO) { + if (info.serverCapabilities & Capabilities.MARIADB_CLIENT_EXTENDED_TYPE_INFO) { const subPacket = packet.subPacketLengthEncoded(); while (subPacket.remaining()) { switch (subPacket.readUInt8()) { diff --git a/lib/cmd/command.js b/lib/cmd/command.js index 78901ccb..bb86d249 100644 --- a/lib/cmd/command.js +++ b/lib/cmd/command.js @@ -114,7 +114,9 @@ class Command extends EventEmitter { packet.skip(1); //skip header const affectedRows = packet.readUnsignedLength(); - const insertId = packet.readSignedLength(); + const insertId = opts.supportBigInt + ? packet.readSignedLengthBigInt() + : packet.readSignedLength(); info.status = packet.readUInt16(); diff --git a/lib/cmd/common-binary-cmd.js b/lib/cmd/common-binary-cmd.js index 9b95683e..a7348576 100644 --- a/lib/cmd/common-binary-cmd.js +++ b/lib/cmd/common-binary-cmd.js @@ -25,6 +25,7 @@ class CommonBinary extends ResultSet { flushed = out.writeInt8(0x00); flushed = out.writeInt8(value ? 0x01 : 0x00) || flushed; break; + case 'bigint': case 'number': flushed = out.writeInt8(0x00); flushed = out.writeLengthStringAscii('' + value) || flushed; diff --git a/lib/cmd/common-text-cmd.js b/lib/cmd/common-text-cmd.js index ca49f6c6..e673c56d 100644 --- a/lib/cmd/common-text-cmd.js +++ b/lib/cmd/common-text-cmd.js @@ -28,6 +28,7 @@ class CommonText extends ResultSet { case 'boolean': out.writeStringAscii(value ? 'true' : 'false'); break; + case 'bigint': case 'number': out.writeStringAscii('' + value); break; @@ -45,12 +46,16 @@ class CommonText extends ResultSet { } else if (Long.isLong(value)) { out.writeStringAscii(value.toString()); } else if (Array.isArray(value)) { - out.writeStringAscii('('); + if (opts.arrayParenthesis) { + out.writeStringAscii('('); + } for (let i = 0; i < value.length; i++) { if (i !== 0) out.writeStringAscii(','); this.writeParam(out, value[i], opts, info); } - out.writeStringAscii(')'); + if (opts.arrayParenthesis) { + out.writeStringAscii(')'); + } } else { if ( value.type != null && @@ -267,12 +272,12 @@ class CommonText extends ResultSet { column.int = () => packet.readIntLengthEncoded(); column.long = () => packet.readLongLengthEncoded( + opts.supportBigInt, opts.supportBigNumbers, opts.bigNumberStrings, (column.flags & FieldDetail.UNSIGNED) > 0 ); - column.decimal = () => - packet.readDecimalLengthEncoded(opts.supportBigNumbers, opts.bigNumberStrings); + column.decimal = () => packet.readDecimalLengthEncoded(opts.bigNumberStrings); column.date = () => packet.readDateTime(opts); column.geometry = () => { return column.readGeometry(); @@ -310,13 +315,14 @@ class CommonText extends ResultSet { return packet.readFloatLengthCoded(); case FieldType.LONGLONG: return packet.readLongLengthEncoded( + opts.supportBigInt, opts.supportBigNumbers, opts.bigNumberStrings, (column.flags & FieldDetail.UNSIGNED) > 0 ); case FieldType.DECIMAL: case FieldType.NEWDECIMAL: - return packet.readDecimalLengthEncoded(opts.supportBigNumbers, opts.bigNumberStrings); + return packet.readDecimalLengthEncoded(opts.bigNumberStrings); case FieldType.DATE: if (opts.dateStrings) { return packet.readAsciiStringLengthEncoded(); diff --git a/lib/cmd/handshake/auth/caching-sha2-password-auth.js b/lib/cmd/handshake/auth/caching-sha2-password-auth.js new file mode 100644 index 00000000..598296d7 --- /dev/null +++ b/lib/cmd/handshake/auth/caching-sha2-password-auth.js @@ -0,0 +1,167 @@ +const PluginAuth = require('./plugin-auth'); +const fs = require('fs'); +const crypto = require('crypto'); +const Errors = require('../../../misc/errors'); +const NativePasswordAuth = require('./native-password-auth'); +const Sha256PasswordAuth = require('./sha256-password-auth'); + +const State = { + INIT: 'INIT', + FAST_AUTH_RESULT: 'FAST_AUTH_RESULT', + REQUEST_SERVER_KEY: 'REQUEST_SERVER_KEY', + SEND_AUTH: 'SEND_AUTH' +}; + +/** + * Use caching Sha2 password authentication + */ +class CachingSha2PasswordAuth extends PluginAuth { + constructor(packSeq, compressPackSeq, pluginData, resolve, reject, multiAuthResolver) { + super(resolve, reject, multiAuthResolver); + this.pluginData = pluginData; + this.sequenceNo = packSeq; + this.counter = 0; + this.state = State.INIT; + } + + start(out, opts, info) { + this.exchange(this.pluginData, out, opts, info); + this.onPacketReceive = this.response; + } + + exchange(buffer, out, opts, info) { + switch (this.state) { + case State.INIT: + const truncatedSeed = this.pluginData.slice(0, this.pluginData.length - 1); + const encPwd = NativePasswordAuth.encryptPassword(opts.password, truncatedSeed, 'sha256'); + out.startPacket(this); + if (encPwd.length > 0) { + out.writeBuffer(encPwd, 0, encPwd.length); + out.flushBuffer(true); + } else { + out.writeEmptyPacket(true); + } + this.state = State.FAST_AUTH_RESULT; + return; + + case State.FAST_AUTH_RESULT: + // length encoded numeric : 0x01 0x03/0x04 + const fastAuthResult = buffer[1]; + switch (fastAuthResult) { + case 0x03: + // success authentication + this.emit('send_end'); + return this.successSend(packet, out, opts, info); + + case 0x04: + if (opts.ssl) { + // using SSL, so sending password in clear + out.startPacket(this); + out.writeString(opts.password); + out.writeInt8(0); + out.flushBuffer(true); + return; + } + + // retrieve public key from configuration or from server + if (opts.cachingRsaPublicKey) { + try { + let key = opts.cachingRsaPublicKey; + if (!key.includes('-----BEGIN')) { + // rsaPublicKey contain path + key = fs.readFileSync(key, 'utf8'); + } + this.publicKey = Sha256PasswordAuth.retreivePublicKey(key); + } catch (err) { + return this.throwError(err, info); + } + // send Sha256Password Packet + Sha256PasswordAuth.sendSha256PwdPacket( + this, + this.pluginData, + this.publicKey, + opts.password, + out + ); + } else { + if (!opts.allowPublicKeyRetrieval) { + return this.throwError( + Errors.createError( + 'RSA public key is not available client side. Either set option `cachingRsaPublicKey` to indicate' + + ' public key path, or allow public key retrieval with option `allowPublicKeyRetrieval`', + true, + info, + '08S01', + Errors.ER_CANNOT_RETRIEVE_RSA_KEY + ), + info + ); + } + this.state = State.REQUEST_SERVER_KEY; + // ask caching public Key Retrieval + out.startPacket(this); + out.writeInt8(0x02); + out.flushBuffer(true); + } + return; + } + + case State.REQUEST_SERVER_KEY: + this.publicKey = Sha256PasswordAuth.retreivePublicKey(buffer.toString('utf8', 1)); + this.state = State.SEND_AUTH; + Sha256PasswordAuth.sendSha256PwdPacket( + this, + this.pluginData, + this.publicKey, + opts.password, + out + ); + } + } + + static retreivePublicKey(key) { + return key.replace('(-+BEGIN PUBLIC KEY-+\\r?\\n|\\n?-+END PUBLIC KEY-+\\r?\\n?)', ''); + } + + static sendSha256PwdPacket(cmd, pluginData, publicKey, password, out) { + const truncatedSeed = pluginData.slice(0, pluginData.length - 1); + out.startPacket(cmd); + const enc = Sha256PasswordAuth.encrypt(truncatedSeed, password, publicKey); + out.writeBuffer(enc, 0, enc.length); + out.flushBuffer(cmd); + } + + // encrypt password with public key + static encrypt(seed, password, publicKey) { + const nullFinishedPwd = Buffer.from(password + '\0'); + const xorBytes = Buffer.allocUnsafe(nullFinishedPwd.length); + const seedLength = seed.length; + for (let i = 0; i < xorBytes.length; i++) { + xorBytes[i] = nullFinishedPwd[i] ^ seed[i % seedLength]; + } + return crypto.publicEncrypt( + { key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }, + xorBytes + ); + } + + response(packet, out, opts, info) { + const marker = packet.peek(); + switch (marker) { + //********************************************************************************************************* + //* OK_Packet and Err_Packet ending packet + //********************************************************************************************************* + case 0x00: + case 0xff: + this.emit('send_end'); + return this.successSend(packet, out, opts, info); + + default: + let promptData = packet.readBufferRemaining(); + this.exchange(promptData, out, opts, info); + this.onPacketReceive = this.response; + } + } +} + +module.exports = CachingSha2PasswordAuth; diff --git a/lib/cmd/handshake/auth/native-password-auth.js b/lib/cmd/handshake/auth/native-password-auth.js index 8c8ec1df..464148ce 100644 --- a/lib/cmd/handshake/auth/native-password-auth.js +++ b/lib/cmd/handshake/auth/native-password-auth.js @@ -17,24 +17,28 @@ class NativePasswordAuth extends PluginAuth { start(out, opts, info) { //seed is ended with a null byte value. const data = this.pluginData.slice(0, 20); - let authToken = NativePasswordAuth.encryptPassword(opts.password, data); + let authToken = NativePasswordAuth.encryptPassword(opts.password, data, 'sha1'); out.startPacket(this); - out.writeBuffer(authToken, 0, authToken.length); - out.flushBuffer(true); + if (authToken.length > 0) { + out.writeBuffer(authToken, 0, authToken.length); + out.flushBuffer(true); + } else { + out.writeEmptyPacket(true); + } this.emit('send_end'); this.onPacketReceive = this.successSend; } - static encryptPassword(password, seed) { + static encryptPassword(password, seed, algorithm) { if (!password) return Buffer.alloc(0); - let hash = Crypto.createHash('sha1'); + let hash = Crypto.createHash(algorithm); let stage1 = hash.update(password, 'utf8').digest(); - hash = Crypto.createHash('sha1'); + hash = Crypto.createHash(algorithm); let stage2 = hash.update(stage1).digest(); - hash = Crypto.createHash('sha1'); + hash = Crypto.createHash(algorithm); hash.update(seed); hash.update(stage2); diff --git a/lib/cmd/handshake/auth/sha256-password-auth.js b/lib/cmd/handshake/auth/sha256-password-auth.js new file mode 100644 index 00000000..52291495 --- /dev/null +++ b/lib/cmd/handshake/auth/sha256-password-auth.js @@ -0,0 +1,142 @@ +const PluginAuth = require('./plugin-auth'); +const fs = require('fs'); +const crypto = require('crypto'); +const Errors = require('../../../misc/errors'); + +/** + * Use Sha256 authentication + */ +class Sha256PasswordAuth extends PluginAuth { + constructor(packSeq, compressPackSeq, pluginData, resolve, reject, multiAuthResolver) { + super(resolve, reject, multiAuthResolver); + this.pluginData = pluginData; + this.sequenceNo = packSeq; + this.counter = 0; + this.initialState = true; + } + + start(out, opts, info) { + this.exchange(this.pluginData, out, opts, info); + this.onPacketReceive = this.response; + } + + exchange(buffer, out, opts, info) { + if (this.initialState) { + if (!opts.password) { + out.startPacket(this); + out.writeEmptyPacket(true); + return; + } else if (opts.ssl) { + // using SSL, so sending password in clear + out.startPacket(this); + if (opts.password) { + out.writeString(opts.password); + } + out.writeInt8(0); + out.flushBuffer(true); + return; + } else { + // retrieve public key from configuration or from server + if (opts.rsaPublicKey) { + try { + let key = opts.rsaPublicKey; + if (!key.includes('-----BEGIN')) { + // rsaPublicKey contain path + key = fs.readFileSync(key, 'utf8'); + } + this.publicKey = Sha256PasswordAuth.retreivePublicKey(key); + } catch (err) { + return this.throwError(err, info); + } + } else { + if (!opts.allowPublicKeyRetrieval) { + return this.throwError( + Errors.createError( + 'RSA public key is not available client side. Either set option `rsaPublicKey` to indicate' + + ' public key path, or allow public key retrieval with option `allowPublicKeyRetrieval`', + + true, + info, + '08S01', + Errors.ER_CANNOT_RETRIEVE_RSA_KEY + ), + info + ); + } + this.initialState = false; + + // ask public Key Retrieval + out.startPacket(this); + out.writeInt8(0x01); + out.flushBuffer(true); + return; + } + } + + // send Sha256Password Packet + Sha256PasswordAuth.sendSha256PwdPacket( + this, + this.pluginData, + this.publicKey, + opts.password, + out + ); + } else { + // has request public key + this.publicKey = Sha256PasswordAuth.retreivePublicKey(buffer.toString('utf8', 1)); + Sha256PasswordAuth.sendSha256PwdPacket( + this, + this.pluginData, + this.publicKey, + opts.password, + out + ); + } + } + + static retreivePublicKey(key) { + return key.replace('(-+BEGIN PUBLIC KEY-+\\r?\\n|\\n?-+END PUBLIC KEY-+\\r?\\n?)', ''); + } + + static sendSha256PwdPacket(cmd, pluginData, publicKey, password, out) { + const truncatedSeed = pluginData.slice(0, pluginData.length - 1); + out.startPacket(cmd); + const enc = Sha256PasswordAuth.encrypt(truncatedSeed, password, publicKey); + out.writeBuffer(enc, 0, enc.length); + out.flushBuffer(cmd); + } + + // encrypt password with public key + static encrypt(seed, password, publicKey) { + const nullFinishedPwd = Buffer.from(password + '\0'); + const xorBytes = Buffer.allocUnsafe(nullFinishedPwd.length); + const seedLength = seed.length; + for (let i = 0; i < xorBytes.length; i++) { + xorBytes[i] = nullFinishedPwd[i] ^ seed[i % seedLength]; + } + return crypto.publicEncrypt( + { key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }, + xorBytes + ); + } + + response(packet, out, opts, info) { + const marker = packet.peek(); + switch (marker) { + //********************************************************************************************************* + //* OK_Packet and Err_Packet ending packet + //********************************************************************************************************* + case 0x00: + case 0xff: + this.emit('send_end'); + return this.successSend(packet, out, opts, info); + + default: + let promptData = packet.readBufferRemaining(); + this.exchange(promptData, out, opts, info); + this.onPacketReceive = this.response; + } + } +} + +module.exports = Sha256PasswordAuth; diff --git a/lib/cmd/handshake/client-capabilities.js b/lib/cmd/handshake/client-capabilities.js index 9c0692b9..336a13c7 100644 --- a/lib/cmd/handshake/client-capabilities.js +++ b/lib/cmd/handshake/client-capabilities.js @@ -1,7 +1,6 @@ 'use strict'; const Capabilities = require('../../const/capabilities'); -const Long = require('long'); /** * Initialize client capabilities according to options and server capabilities @@ -10,7 +9,7 @@ const Long = require('long'); * @param info information */ module.exports.init = function (opts, info) { - let capabilitiesLow = + let capabilities = Capabilities.IGNORE_SPACE | Capabilities.PROTOCOL_41 | Capabilities.TRANSACTIONS | @@ -21,53 +20,52 @@ module.exports.init = function (opts, info) { Capabilities.PLUGIN_AUTH | Capabilities.PLUGIN_AUTH_LENENC_CLIENT_DATA; - let capabilitiesHigh = 0; - if ((info.serverCapabilities.low & Capabilities.MYSQL) === 0) { - capabilitiesHigh |= Capabilities.MARIADB_CLIENT_EXTENDED_TYPE_INFO; + if ((info.serverCapabilities & Capabilities.MYSQL) === 0n) { + capabilities |= Capabilities.MARIADB_CLIENT_EXTENDED_TYPE_INFO; } if (opts.connectAttributes) { - capabilitiesLow |= Capabilities.CONNECT_ATTRS; + capabilities |= Capabilities.CONNECT_ATTRS; } if (opts.foundRows) { - capabilitiesLow |= Capabilities.FOUND_ROWS; + capabilities |= Capabilities.FOUND_ROWS; } if (opts.permitLocalInfile) { - capabilitiesLow |= Capabilities.LOCAL_FILES; + capabilities |= Capabilities.LOCAL_FILES; } if (opts.multipleStatements) { - capabilitiesLow |= Capabilities.MULTI_STATEMENTS; + capabilities |= Capabilities.MULTI_STATEMENTS; } - info.eofDeprecated = (info.serverCapabilities.low & Capabilities.DEPRECATE_EOF) > 0; + info.eofDeprecated = (info.serverCapabilities & Capabilities.DEPRECATE_EOF) > 0; if (info.eofDeprecated) { - capabilitiesLow |= Capabilities.DEPRECATE_EOF; + capabilities |= Capabilities.DEPRECATE_EOF; } if (opts.database) { - capabilitiesLow |= Capabilities.CONNECT_WITH_DB; + capabilities |= Capabilities.CONNECT_WITH_DB; } // use compression only if requested by client and supported by server if (opts.compress) { - if (info.serverCapabilities.low & Capabilities.COMPRESS) { - capabilitiesLow |= Capabilities.COMPRESS; + if (info.serverCapabilities & Capabilities.COMPRESS) { + capabilities |= Capabilities.COMPRESS; } else { opts.compress = false; } } if (opts.bulk) { - if (info.serverCapabilities.high & Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) { - capabilitiesHigh |= Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS; + if (info.serverCapabilities & Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) { + capabilities |= Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS; } } if (opts.permitConnectionWhenExpired) { - capabilitiesLow |= Capabilities.CAN_HANDLE_EXPIRED_PASSWORDS; + capabilities |= Capabilities.CAN_HANDLE_EXPIRED_PASSWORDS; } - info.clientCapabilities = new Long(capabilitiesLow, capabilitiesHigh, true); + info.clientCapabilities = capabilities; }; diff --git a/lib/cmd/handshake/client-handshake-response.js b/lib/cmd/handshake/client-handshake-response.js index 57c27df4..35571f1b 100644 --- a/lib/cmd/handshake/client-handshake-response.js +++ b/lib/cmd/handshake/client-handshake-response.js @@ -35,28 +35,27 @@ module.exports.send = function send(cmd, out, opts, pluginName, info) { break; default: - authToken = NativePasswordAuth.encryptPassword(pwd, info.seed); + authToken = NativePasswordAuth.encryptPassword(pwd, info.seed, 'sha1'); authPlugin = 'mysql_native_password'; break; } - - out.writeInt32(info.clientCapabilities.low); + out.writeInt32(Number(info.clientCapabilities & 0xffffffffn)); out.writeInt32(1024 * 1024 * 1024); // max packet size out.writeInt8(opts.collation.index); for (let i = 0; i < 19; i++) { out.writeInt8(0); } - out.writeInt32(info.clientCapabilities.high); + out.writeInt32(Number(info.clientCapabilities >> 32n)); //null encoded user out.writeString(opts.user || ''); out.writeInt8(0); - if (info.serverCapabilities.low & Capabilities.PLUGIN_AUTH_LENENC_CLIENT_DATA) { + if (info.serverCapabilities & Capabilities.PLUGIN_AUTH_LENENC_CLIENT_DATA) { out.writeLengthCoded(authToken.length); out.writeBuffer(authToken, 0, authToken.length); - } else if (info.serverCapabilities.low & Capabilities.SECURE_CONNECTION) { + } else if (info.serverCapabilities & Capabilities.SECURE_CONNECTION) { out.writeInt8(authToken.length); out.writeBuffer(authToken, 0, authToken.length); } else { @@ -64,18 +63,18 @@ module.exports.send = function send(cmd, out, opts, pluginName, info) { out.writeInt8(0); } - if (info.clientCapabilities.low & Capabilities.CONNECT_WITH_DB) { + if (info.clientCapabilities & Capabilities.CONNECT_WITH_DB) { out.writeString(opts.database); out.writeInt8(0); info.database = opts.database; } - if (info.clientCapabilities.low & Capabilities.PLUGIN_AUTH) { - out.writeString(pluginName); + if (info.clientCapabilities & Capabilities.PLUGIN_AUTH) { + out.writeString(authPlugin); out.writeInt8(0); } - if (opts.connectAttributes && info.serverCapabilities.low & Capabilities.CONNECT_ATTRS) { + if (opts.connectAttributes && info.serverCapabilities & Capabilities.CONNECT_ATTRS) { out.writeInt8(0xfc); let initPos = out.pos; //save position, assuming connection attributes length will be less than 2 bytes length out.writeInt16(0); diff --git a/lib/cmd/handshake/handshake.js b/lib/cmd/handshake/handshake.js index 07d5af62..29c7f325 100644 --- a/lib/cmd/handshake/handshake.js +++ b/lib/cmd/handshake/handshake.js @@ -7,6 +7,7 @@ const SslRequest = require('./ssl-request'); const ClientCapabilities = require('./client-capabilities'); const Errors = require('../../misc/errors'); const Capabilities = require('../../const/capabilities'); +const process = require('process'); /** * Handle handshake. @@ -25,7 +26,7 @@ class Handshake extends Command { ensureOptionCompatibility(opts, info) { if ( opts.multipleStatements && - (info.serverCapabilities.low & Capabilities.MULTI_STATEMENTS) === 0 + (info.serverCapabilities & Capabilities.MULTI_STATEMENTS) === 0 ) { return this.throwNewError( "Option `multipleStatements` enable, but server doesn'permits multi-statment", @@ -36,7 +37,7 @@ class Handshake extends Command { ); } - if (opts.permitLocalInfile && (info.serverCapabilities.low & Capabilities.LOCAL_FILES) === 0) { + if (opts.permitLocalInfile && (info.serverCapabilities & Capabilities.LOCAL_FILES) === 0) { return this.throwNewError( "Option `permitLocalInfile` enable, but server doesn'permits using local file", true, @@ -60,8 +61,8 @@ class Handshake extends Command { ClientCapabilities.init(opts, info); if (opts.ssl) { - if (info.serverCapabilities.low & Capabilities.SSL) { - info.clientCapabilities.low |= Capabilities.SSL; + if (info.serverCapabilities & Capabilities.SSL) { + info.clientCapabilities |= Capabilities.SSL; SslRequest.send(this, out, info, opts); this._createSecureContext( function () { @@ -152,7 +153,7 @@ class Handshake extends Command { */ dispatchAuthSwitchRequest(packet, out, opts, info) { let pluginName, pluginData; - if (info.clientCapabilities.low & Capabilities.PLUGIN_AUTH) { + if (info.clientCapabilities & Capabilities.PLUGIN_AUTH) { packet.skip(1); //header if (packet.remaining()) { //AuthSwitchRequest packet. @@ -168,20 +169,25 @@ class Handshake extends Command { pluginData = packet.readBufferRemaining(); } - const plugin = Handshake.pluginHandler( - pluginName, - this.plugin.sequenceNo, - this.plugin.compressSequenceNo, - pluginData, - info, - opts, - out, - this.resolve, - this.reject, - this.handshakeResult.bind(this) - ); + try { + this.plugin = Handshake.pluginHandler( + pluginName, + this.plugin.sequenceNo, + this.plugin.compressSequenceNo, + pluginData, + info, + opts, + out, + this.resolve, + this.reject, + this.handshakeResult.bind(this) + ); + } catch (err) { + this.reject(err); + return; + } - if (!plugin) { + if (!this.plugin) { this.reject( Errors.createError( "Client does not support authentication protocol '" + @@ -194,7 +200,6 @@ class Handshake extends Command { ) ); } else { - this.plugin = plugin; this._addCommand(this.plugin, false); } } @@ -229,6 +234,32 @@ class Handshake extends Command { pluginAuth = require('./auth/pam-password-auth.js'); break; + case 'sha256_password': + if (!Handshake.ensureNodeVersion(11, 6, 0)) { + throw Errors.createError( + 'sha256_password authentication plugin require node 11.6+', + true, + info, + '08004', + Errors.ER_MINIMUM_NODE_VERSION_REQUIRED + ); + } + pluginAuth = require('./auth/sha256-password-auth.js'); + break; + + case 'caching_sha2_password': + if (!Handshake.ensureNodeVersion(11, 6, 0)) { + throw Errors.createError( + 'caching_sha2_password authentication plugin require node 11.6+', + true, + info, + '08004', + Errors.ER_MINIMUM_NODE_VERSION_REQUIRED + ); + } + pluginAuth = require('./auth/caching-sha2-password-auth.js'); + break; + //TODO "auth_gssapi_client" default: @@ -243,6 +274,15 @@ class Handshake extends Command { multiAuthResolver ); } + + static ensureNodeVersion(major, minor, patch) { + const ver = process.versions.node.split('.'); + return ( + ver[0] > major || + (ver[0] === major && ver[1] > minor) || + (ver[0] === major && ver[1] === minor && ver[2] >= patch) + ); + } } module.exports = Handshake; diff --git a/lib/cmd/handshake/initial-handshake.js b/lib/cmd/handshake/initial-handshake.js index aecd3081..f8edd0d9 100644 --- a/lib/cmd/handshake/initial-handshake.js +++ b/lib/cmd/handshake/initial-handshake.js @@ -2,7 +2,6 @@ const Capabilities = require('../../const/capabilities'); const ConnectionInformation = require('../../misc/connection-information'); -const Long = require('long'); /** * Parser server initial handshake. @@ -19,34 +18,33 @@ class InitialHandshake { let seed1 = packet.readBuffer(8); packet.skip(1); //reserved byte - let serverCapabilitiesLow = packet.readUInt16(); + let serverCapabilities = BigInt(packet.readUInt16()); //skip characterSet packet.skip(1); info.status = packet.readUInt16(); - serverCapabilitiesLow += packet.readUInt16() << 16; + serverCapabilities += BigInt(packet.readUInt16()) << 16n; let saltLength = 0; - if (serverCapabilitiesLow & Capabilities.PLUGIN_AUTH) { + if (serverCapabilities & Capabilities.PLUGIN_AUTH) { saltLength = Math.max(12, packet.readUInt8() - 9); } else { packet.skip(1); } - let serverCapabilitiesHigh = 0; - if (serverCapabilitiesLow & Capabilities.MYSQL) { + if (serverCapabilities & Capabilities.MYSQL) { packet.skip(10); } else { packet.skip(6); - serverCapabilitiesHigh = packet.readUInt32(); + serverCapabilities += BigInt(packet.readUInt32()) << 32n; } - if (serverCapabilitiesLow & Capabilities.SECURE_CONNECTION) { + if (serverCapabilities & Capabilities.SECURE_CONNECTION) { let seed2 = packet.readBuffer(saltLength); info.seed = Buffer.concat([seed1, seed2]); } else { info.seed = seed1; } packet.skip(1); - info.serverCapabilities = new Long(serverCapabilitiesLow, serverCapabilitiesHigh, true); + info.serverCapabilities = serverCapabilities; /** * check for MariaDB 10.x replication hack , remove fake prefix if needed @@ -61,10 +59,10 @@ class InitialHandshake { //Support for MDEV-7780 faking server version info.serverVersion.mariaDb = info.serverVersion.raw.includes('MariaDB') || - (serverCapabilitiesLow & Capabilities.MYSQL) === 0; + (serverCapabilities & Capabilities.MYSQL) === 0n; } - if (serverCapabilitiesLow & Capabilities.PLUGIN_AUTH) { + if (serverCapabilities & Capabilities.PLUGIN_AUTH) { this.pluginName = packet.readStringNullEnded(); } else { this.pluginName = ''; diff --git a/lib/cmd/handshake/ssl-request.js b/lib/cmd/handshake/ssl-request.js index a9756ee8..c6467d58 100644 --- a/lib/cmd/handshake/ssl-request.js +++ b/lib/cmd/handshake/ssl-request.js @@ -12,17 +12,17 @@ const Capabilities = require('../../const/capabilities'); */ module.exports.send = function sendSSLRequest(cmd, out, info, opts) { out.startPacket(cmd); - out.writeInt32(info.clientCapabilities.low); + out.writeInt32(Number(info.clientCapabilities & 0xffffffffn)); out.writeInt32(1024 * 1024 * 1024); // max packet size out.writeInt8(opts.collation.index); for (let i = 0; i < 19; i++) { out.writeInt8(0); } - if (info.serverCapabilities.low & Capabilities.MYSQL) { + if (info.serverCapabilities & Capabilities.MYSQL) { out.writeInt32(0); } else { - out.writeInt32(info.clientCapabilities.high); + out.writeInt32(Number(info.clientCapabilities >> 32n)); } out.flushBuffer(true); diff --git a/lib/cmd/resultset.js b/lib/cmd/resultset.js index e889d69c..a0ad8c6b 100644 --- a/lib/cmd/resultset.js +++ b/lib/cmd/resultset.js @@ -94,6 +94,9 @@ class ResultSet extends Command { this.opts = { timeout: cmdOpts.timeout, autoJsonMap: connOpts.autoJsonMap, + arrayParenthesis: connOpts.arrayParenthesis, + supportBigInt: + cmdOpts.supportBigInt != undefined ? cmdOpts.supportBigInt : connOpts.supportBigInt, checkDuplicate: cmdOpts.checkDuplicate != undefined ? cmdOpts.checkDuplicate : connOpts.checkDuplicate, typeCast: cmdOpts.typeCast != undefined ? cmdOpts.typeCast : connOpts.typeCast, @@ -101,6 +104,7 @@ class ResultSet extends Command { nestTables: cmdOpts.nestTables != undefined ? cmdOpts.nestTables : connOpts.nestTables, dateStrings: cmdOpts.dateStrings != undefined ? cmdOpts.dateStrings : connOpts.dateStrings, tz: cmdOpts.tz != undefined ? cmdOpts.tz : connOpts.tz, + pipelining: connOpts.pipelining, localTz: cmdOpts.localTz != undefined ? cmdOpts.localTz : connOpts.localTz, namedPlaceholders: cmdOpts.namedPlaceholders != undefined @@ -474,13 +478,20 @@ class ResultSet extends Command { sqlMsg += '}'; } else { sqlMsg += '['; - for (let i = 0; i < values.length; i++) { - if (i !== 0) sqlMsg += ','; - let param = values[i]; - sqlMsg = ResultSet.logParam(sqlMsg, param); + if (Array.isArray(values)) { + for (let i = 0; i < values.length; i++) { + if (i !== 0) sqlMsg += ','; + let param = values[i]; + sqlMsg = ResultSet.logParam(sqlMsg, param); + if (sqlMsg.length > this.opts.debugLen) { + sqlMsg = sqlMsg.substr(0, this.opts.debugLen) + '...'; + break; + } + } + } else { + sqlMsg = ResultSet.logParam(sqlMsg, values); if (sqlMsg.length > this.opts.debugLen) { sqlMsg = sqlMsg.substr(0, this.opts.debugLen) + '...'; - break; } } sqlMsg += ']'; diff --git a/lib/config/connection-options.js b/lib/config/connection-options.js index 1a7fb4ee..ec0067e4 100644 --- a/lib/config/connection-options.js +++ b/lib/config/connection-options.js @@ -62,6 +62,9 @@ class ConnectionOptions { this.debugLen = opts.debugLen || 256; this.foundRows = opts.foundRows === undefined || opts.foundRows; this.host = opts.host || 'localhost'; + this.rsaPublicKey = opts.rsaPublicKey; + this.cachingRsaPublicKey = opts.cachingRsaPublicKey; + this.allowPublicKeyRetrieval = opts.allowPublicKeyRetrieval || false; this.initSql = opts.initSql; this.forceVersionCheck = opts.forceVersionCheck || false; this.maxAllowedPacket = opts.maxAllowedPacket; @@ -71,6 +74,8 @@ class ConnectionOptions { this.nestTables = opts.nestTables; this.password = opts.password; this.autoJsonMap = opts.autoJsonMap === undefined ? true : opts.autoJsonMap; + this.arrayParenthesis = opts.arrayParenthesis || false; + this.keepAliveDelay = opts.keepAliveDelay === undefined ? 0 : opts.keepAliveDelay; this.permitSetMultiParamEntries = opts.permitSetMultiParamEntries || false; this.permitConnectionWhenExpired = opts.permitConnectionWhenExpired || false; this.pipelining = opts.pipelining; @@ -98,6 +103,7 @@ class ConnectionOptions { } } this.supportBigNumbers = opts.supportBigNumbers || false; + this.supportBigInt = opts.supportBigInt || false; this.timezone = opts.timezone || 'local'; if (this.timezone && this.timezone !== 'local' && this.timezone !== 'auto') { let tzName = this.timezone; @@ -163,17 +169,24 @@ class ConnectionOptions { static parseOptionDataType(opts) { if (opts.bigNumberStrings) opts.bigNumberStrings = opts.bigNumberStrings == 'true'; if (opts.bulk) opts.bulk = opts.bulk == 'true'; + if (opts.rsaPublicKey) opts.rsaPublicKey = opts.rsaPublicKey; + if (opts.cachingRsaPublicKey) opts.cachingRsaPublicKey = opts.cachingRsaPublicKey; if (opts.logPackets) opts.logPackets = opts.logPackets == 'true'; + if (opts.allowPublicKeyRetrieval) + opts.allowPublicKeyRetrieval = opts.allowPublicKeyRetrieval == 'true'; + if (opts.charsetNumber && !isNaN(Number.parseInt(opts.charsetNumber))) { opts.charsetNumber = Number.parseInt(opts.charsetNumber); } if (opts.compress) opts.compress = opts.compress == 'true'; if (opts.connectAttributes) opts.connectAttributes = JSON.parse(opts.connectAttributes); if (opts.connectTimeout) opts.connectTimeout = parseInt(opts.connectTimeout); + if (opts.keepAliveDelay) opts.keepAliveDelay = parseInt(opts.keepAliveDelay); if (opts.socketTimeout) opts.socketTimeout = parseInt(opts.socketTimeout); if (opts.dateStrings) opts.dateStrings = opts.dateStrings == 'true'; if (opts.debug) opts.debug = opts.debug == 'true'; if (opts.autoJsonMap) opts.autoJsonMap = opts.autoJsonMap == 'true'; + if (opts.arrayParenthesis) opts.arrayParenthesis = opts.arrayParenthesis == 'true'; if (opts.checkDuplicate) opts.checkDuplicate = opts.checkDuplicate == 'true'; if (opts.debugCompress) opts.debugCompress = opts.debugCompress == 'true'; @@ -192,6 +205,7 @@ class ConnectionOptions { if (opts.forceVersionCheck) opts.forceVersionCheck = opts.forceVersionCheck == 'true'; if (opts.rowsAsArray) opts.rowsAsArray = opts.rowsAsArray == 'true'; if (opts.supportBigNumbers) opts.supportBigNumbers = opts.supportBigNumbers == 'true'; + if (opts.supportBigInt) opts.supportBigInt = opts.supportBigInt == 'true'; if (opts.trace) opts.trace = opts.trace == 'true'; if (opts.ssl && (opts.ssl == 'true' || opts.ssl == 'false')) opts.ssl = opts.ssl == 'true'; return opts; diff --git a/lib/connection.js b/lib/connection.js index 6b455ddb..63856a10 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -659,11 +659,14 @@ function Connection(options) { * @return {boolean} indicating if must use rewrite or bulk */ const canUseBulk = (values) => { + // not using info.isMariaDB() directly in case of callback use, + // without connection beeing completly finished. let useBulk = - info.isMariaDB() && + info.serverVersion && + info.serverVersion.mariaDb && info.hasMinVersion(10, 2, 7) && opts.bulk && - (info.serverCapabilities.high & Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > 0; + (info.serverCapabilities & Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > 0n; if (useBulk) { //ensure that there is no stream object @@ -726,7 +729,7 @@ function Connection(options) { _authSucceed, _authFail, _createSecureContext.bind(this, _authFail), - _addCommand.bind(this), + _addCommandEnable.bind(this), _getSocket ); Error.captureStackTrace(handshake); @@ -934,6 +937,11 @@ function Connection(options) { _socketConnected = true; _socket.setTimeout(opts.socketTimeout, _socketTimeoutReached.bind(this, authFailHandler)); _socket.setNoDelay(true); + + // keep alive for socket. This won't reset server wait_timeout use pool option idleTimeout for that + if (opts.keepAliveDelay) { + _socket.setKeepAlive(true, opts.keepAliveDelay); + } } }.bind(this) ); @@ -952,7 +960,7 @@ function Connection(options) { //enable packet compression according to option if (opts.logPackets) info.enableLogPacket(); if (opts.compress) { - if (info.serverCapabilities.low & Capabilities.COMPRESS) { + if (info.serverCapabilities & Capabilities.COMPRESS) { _out.setStream(new CompressionOutputStream(_socket, opts, info)); _in = new CompressionInputStream(_in, _receiveQueue, opts, info); _socket.removeAllListeners('data'); @@ -964,17 +972,12 @@ function Connection(options) { } } - if (opts.pipelining) { - _addCommand = _addCommandEnablePipeline; - const commands = _receiveQueue.toArray(); - commands.forEach((cmd) => { - const listeners = cmd.listeners('end'); - cmd.removeListener('end', listeners[0]); - cmd.once('send_end', () => { - setImmediate(_nextSendCmd); - }); - }); - } + _addCommand = opts.pipelining ? _addCommandEnablePipeline : _addCommandEnable; + + const commands = _waitingAuthenticationQueue.toArray(); + commands.forEach((cmd) => { + _addCommand(cmd); + }); const errorInitialQueries = (err) => { if (!err.fatal) this.end().catch((err) => {}); @@ -1154,6 +1157,18 @@ function Connection(options) { _fatalError(err, true); }; + /** + * Add command to waiting queue until authentication. + * + * @param cmd command + * @returns {*} current command + * @private + */ + const _addCommandQueue = (cmd) => { + _waitingAuthenticationQueue.push(cmd); + return cmd; + }; + /** * Add command to command sending and receiving queue. * @@ -1235,9 +1250,10 @@ function Connection(options) { */ const _socketErrorHandler = function (authFailHandler, err) { if (_status === Status.CLOSING || _status === Status.CLOSED) return; - - _socket.writeBuf = () => {}; - _socket.flush = () => {}; + if (_socket) { + _socket.writeBuf = () => {}; + _socket.flush = () => {}; + } //socket has been ended without error if (!err) { @@ -1381,11 +1397,12 @@ function Connection(options) { const info = new ConnectionInformation(); const _sendQueue = new Queue(); const _receiveQueue = new Queue(); + const _waitingAuthenticationQueue = new Queue(); let _status = Status.NOT_CONNECTED; let _socketConnected = false; let _socket = null; let _timeout = null; - let _addCommand = _addCommandEnable; + let _addCommand = _addCommandQueue; const _fatalError = _fatalErrorHandler(this); let _out = new PacketOutputStream(opts, info); let _in = new PacketInputStream(_unexpectedPacket.bind(this), _receiveQueue, _out, opts, info); diff --git a/lib/const/capabilities.js b/lib/const/capabilities.js index 17c9f8f2..aedd8568 100644 --- a/lib/const/capabilities.js +++ b/lib/const/capabilities.js @@ -3,62 +3,62 @@ * see : https://mariadb.com/kb/en/library/1-connecting-connecting/#capabilities */ /* mysql/old mariadb server/client */ -module.exports.MYSQL = 1; +module.exports.MYSQL = 1n; /* Found instead of affected rows */ -module.exports.FOUND_ROWS = 2; +module.exports.FOUND_ROWS = 2n; /* get all column flags */ -module.exports.LONG_FLAG = 4; +module.exports.LONG_FLAG = 4n; /* one can specify db on connect */ -module.exports.CONNECT_WITH_DB = 8; +module.exports.CONNECT_WITH_DB = 8n; /* don't allow database.table.column */ -module.exports.NO_SCHEMA = 1 << 4; +module.exports.NO_SCHEMA = 1n << 4n; /* can use compression protocol */ -module.exports.COMPRESS = 1 << 5; +module.exports.COMPRESS = 1n << 5n; /* odbc client */ -module.exports.ODBC = 1 << 6; +module.exports.ODBC = 1n << 6n; /* can use LOAD DATA LOCAL */ -module.exports.LOCAL_FILES = 1 << 7; +module.exports.LOCAL_FILES = 1n << 7n; /* ignore spaces before '' */ -module.exports.IGNORE_SPACE = 1 << 8; +module.exports.IGNORE_SPACE = 1n << 8n; /* new 4.1 protocol */ -module.exports.PROTOCOL_41 = 1 << 9; +module.exports.PROTOCOL_41 = 1n << 9n; /* this is an interactive client */ -module.exports.INTERACTIVE = 1 << 10; +module.exports.INTERACTIVE = 1n << 10n; /* switch to ssl after handshake */ -module.exports.SSL = 1 << 11; +module.exports.SSL = 1n << 11n; /* IGNORE sigpipes */ -module.exports.IGNORE_SIGPIPE = 1 << 12; +module.exports.IGNORE_SIGPIPE = 1n << 12n; /* client knows about transactions */ -module.exports.TRANSACTIONS = 1 << 13; +module.exports.TRANSACTIONS = 1n << 13n; /* old flag for 4.1 protocol */ -module.exports.RESERVED = 1 << 14; +module.exports.RESERVED = 1n << 14n; /* new 4.1 authentication */ -module.exports.SECURE_CONNECTION = 1 << 15; +module.exports.SECURE_CONNECTION = 1n << 15n; /* enable/disable multi-stmt support */ -module.exports.MULTI_STATEMENTS = 1 << 16; +module.exports.MULTI_STATEMENTS = 1n << 16n; /* enable/disable multi-results */ -module.exports.MULTI_RESULTS = 1 << 17; +module.exports.MULTI_RESULTS = 1n << 17n; /* multi-results in ps-protocol */ -module.exports.PS_MULTI_RESULTS = 1 << 18; +module.exports.PS_MULTI_RESULTS = 1n << 18n; /* client supports plugin authentication */ -module.exports.PLUGIN_AUTH = 1 << 19; +module.exports.PLUGIN_AUTH = 1n << 19n; /* permits connection attributes */ -module.exports.CONNECT_ATTRS = 1 << 20; +module.exports.CONNECT_ATTRS = 1n << 20n; /* Enable authentication response packet to be larger than 255 bytes. */ -module.exports.PLUGIN_AUTH_LENENC_CLIENT_DATA = 1 << 21; +module.exports.PLUGIN_AUTH_LENENC_CLIENT_DATA = 1n << 21n; /* Don't close the connection for a connection with expired password. */ -module.exports.CAN_HANDLE_EXPIRED_PASSWORDS = 1 << 22; +module.exports.CAN_HANDLE_EXPIRED_PASSWORDS = 1n << 22n; /* Capable of handling server state change information. Its a hint to the server to include the state change information in Ok packet. */ -module.exports.SESSION_TRACK = 1 << 23; +module.exports.SESSION_TRACK = 1n << 23n; /* Client no longer needs EOF packet */ -module.exports.DEPRECATE_EOF = 1 << 24; -module.exports.SSL_VERIFY_SERVER_CERT = 1 << 30; +module.exports.DEPRECATE_EOF = 1n << 24n; +module.exports.SSL_VERIFY_SERVER_CERT = 1n << 30n; /* MariaDB extended capabilities */ /* Permit bulk insert*/ -module.exports.MARIADB_CLIENT_STMT_BULK_OPERATIONS = 1 << (34 - 32); +module.exports.MARIADB_CLIENT_STMT_BULK_OPERATIONS = 1n << 34n; /* Clients supporting extended metadata */ -module.exports.MARIADB_CLIENT_EXTENDED_TYPE_INFO = 1 << (35 - 32); +module.exports.MARIADB_CLIENT_EXTENDED_TYPE_INFO = 1n << 35n; diff --git a/lib/io/bulk-packet.js b/lib/io/bulk-packet.js index 34b8b1c4..a15f749e 100644 --- a/lib/io/bulk-packet.js +++ b/lib/io/bulk-packet.js @@ -23,6 +23,7 @@ class BulkPacket { this.pos = 4; this.datatypes = []; this.encoding = out.encoding; + this.statementId = -1; this.waitingResponseNo = 1; this.singleQuery = false; this.haveErrorResponse = false; @@ -50,6 +51,7 @@ class BulkPacket { if (this.datatypes[r] !== 0x01) return true; break; case 'number': + case 'bigint': if (this.datatypes[r] !== 0x0f) return true; break; case 'object': @@ -86,11 +88,10 @@ class BulkPacket { this.buf[this.pos++] = 0xfa; //use last prepare command - this.buf[this.pos++] = 0xff; - this.buf[this.pos++] = 0xff; - this.buf[this.pos++] = 0xff; - this.buf[this.pos++] = 0xff; - + this.buf[this.pos++] = this.statementId; + this.buf[this.pos++] = this.statementId >> 8; + this.buf[this.pos++] = this.statementId >> 16; + this.buf[this.pos++] = this.statementId >> 24; //set bulk flags to Send types to server this.buf[this.pos++] = 0x80; this.buf[this.pos++] = 0x00; @@ -106,6 +107,7 @@ class BulkPacket { case 'boolean': this.buf[this.pos++] = 0x01; break; + case 'bigint': case 'number': this.buf[this.pos++] = 0x0f; break; diff --git a/lib/io/packet.js b/lib/io/packet.js index 6cab9a5a..62ce9a16 100644 --- a/lib/io/packet.js +++ b/lib/io/packet.js @@ -183,27 +183,9 @@ class Packet { } readInt64() { - const first = this.readInt32(); - const second = this.readInt32(); - - const long = new Long(first, second, false); - if (long.isNegative()) { - if (long.lessThan(-9007199254740991)) return long; - } else if (long.greaterThan(9007199254740991)) return long; - - return long.toNumber(); - } - - readUInt64() { - const first = this.readInt32(); - const second = this.readInt32(); - - const long = new Long(first, second, true); - if (long.isNegative()) { - if (long.lessThan(-9007199254740991)) return long; - } else if (long.greaterThan(9007199254740991)) return long; - - return long.toNumber(); + const val = this.buf.readBigInt64LE(this.pos); + this.pos += 8; + return val; } readUnsignedLength() { @@ -216,7 +198,8 @@ class Packet { case 0xfd: return this.readUInt24(); case 0xfe: - return this.readUInt64(); + // limitation to BigInt signed value + return Number(this.readInt64()); default: return type; } @@ -259,12 +242,28 @@ class Packet { case 0xfd: return this.readUInt24(); case 0xfe: - return this.readInt64(); + return Number(this.readInt64()); default: return type; } } + readSignedLengthBigInt() { + const type = this.buf[this.pos++]; + switch (type) { + case 0xfb: + return null; + case 0xfc: + return BigInt(this.readUInt16()); + case 0xfd: + return BigInt(this.readUInt24()); + case 0xfe: + return this.readInt64(); + default: + return BigInt(type); + } + } + readAsciiStringLengthEncoded() { const len = this.readUnsignedLength(); if (len === null) return null; @@ -283,10 +282,16 @@ class Packet { return Iconv.decode(this.buf.slice(this.pos - len, this.pos), encoding); } - readLongLengthEncoded(supportBigNumbers, bigNumberStrings, unsigned) { + readLongLengthEncoded(supportBigInt, supportBigNumbers, bigNumberStrings, unsigned) { const len = this.readUnsignedLength(); if (len === null) return null; + if (supportBigInt) { + const str = this.buf.toString('ascii', this.pos, this.pos + len); + this.pos += len; + return BigInt(str); + } + let result = 0; let negate = false; let begin = this.pos; @@ -313,7 +318,7 @@ class Packet { return val; } - readDecimalLengthEncoded(supportBigNumbers, bigNumberStrings) { + readDecimalLengthEncoded(bigNumberStrings) { const len = this.readUnsignedLength(); if (len === null) return null; diff --git a/lib/io/rewrite-packet.js b/lib/io/rewrite-packet.js index eec918d5..dfbec26d 100644 --- a/lib/io/rewrite-packet.js +++ b/lib/io/rewrite-packet.js @@ -378,13 +378,12 @@ class ReWritePacket { //not enough room for current query, flush mark. this.flushMark(); flushed = true; - } else { - //just mark ending query - this.markPos = this.pos; - if (isLast) { - this.flushMark(); - flushed = true; - } + } + //just mark ending query + this.markPos = this.pos; + if (isLast) { + this.flushMark(); + flushed = true; } if (!isLast) flushed = this.writeStringAscii(',') || flushed; } @@ -443,13 +442,13 @@ class ReWritePacket { this.pos = 4; this.buf[this.pos++] = 0x03; this.writeString(this.initStr); + this.markPos = undefined; if (afterMark) { if (this.buf.length - this.pos < afterMark.length) this.growBuffer(afterMark.length - (this.buf.length - this.pos)); afterMark.copy(this.buf, this.pos, 0, afterMark.length); this.pos += afterMark.length; } - this.markPos = undefined; this.singleQuery = false; this.singleQuerySequenceNo = undefined; this.singleQueryCompressSequenceNo = undefined; diff --git a/lib/misc/errors.js b/lib/misc/errors.js index 48e99fc7..e0dafe01 100644 --- a/lib/misc/errors.js +++ b/lib/misc/errors.js @@ -101,6 +101,8 @@ module.exports.ER_DUPLICATE_FIELD = 45040; module.exports.ER_CLIENT_OPTION_INCOMPATIBILITY = 45041; module.exports.ER_PING_TIMEOUT = 45042; module.exports.ER_BAD_PARAMETER_VALUE = 45043; +module.exports.ER_CANNOT_RETRIEVE_RSA_KEY = 45044; +module.exports.ER_MINIMUM_NODE_VERSION_REQUIRED = 45045; const keys = Object.keys(module.exports); const errByNo = {}; diff --git a/lib/misc/utils.js b/lib/misc/utils.js index ab6ab297..2b6e1640 100644 --- a/lib/misc/utils.js +++ b/lib/misc/utils.js @@ -122,6 +122,7 @@ module.exports.escape = (opts, info, value) => { switch (typeof value) { case 'boolean': return value ? 'true' : 'false'; + case 'bigint': case 'number': return '' + value; case 'object': @@ -142,12 +143,12 @@ module.exports.escape = (opts, info, value) => { } else if (Long.isLong(value)) { return value.toString(); } else if (Array.isArray(value)) { - let out = '('; + let out = opts.arrayParenthesis ? '(' : ''; for (let i = 0; i < value.length; i++) { if (i !== 0) out += ','; out += this.escape(opts, info, value[i]); } - out += ')'; + if (opts.arrayParenthesis) out += ')'; return out; } else { if ( diff --git a/package.json b/package.json index f8010e93..96387e8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mariadb", - "version": "2.4.2", + "version": "2.5.0", "description": "fast mariadb/mysql connector.", "main": "promise.js", "types": "types/index.d.ts", @@ -46,31 +46,31 @@ "license": "LGPL-2.1-or-later", "dependencies": { "@types/geojson": "^7946.0.7", - "@types/node": "^13.9.8", + "@types/node": "^14.11.2", "denque": "^1.4.1", - "iconv-lite": "^0.5.1", + "iconv-lite": "^0.6.2", "long": "^4.0.0", "moment-timezone": "^0.5.31", "please-upgrade-node": "^3.2.0" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^2.26.0", - "@typescript-eslint/parser": "^2.26.0", + "@typescript-eslint/eslint-plugin": "^4.3.0", + "@typescript-eslint/parser": "^4.3.0", "benchmark": "^2.1.4", "chai": "^4.2.0", - "codecov": "^3.6.4", + "codecov": "^3.8.0", "colors": "^1.4.0", "dom-parser": "^0.1.6", "error-stack-parser": "^2.0.6", - "eslint": "^6.6.0", - "eslint-config-prettier": "^6.10.1", + "eslint": "^7.11.0", + "eslint-config-prettier": "^6.12.0", "eslint-plugin-markdown": "^1.0.1", "eslint-plugin-prettier": "^3.1.0", - "mocha": "^7.1.1", + "mocha": "^8.1.3", "mocha-lcov-reporter": "^1.3.0", "nyc": "^15.0.0", - "prettier": "^2.0.2", - "typescript": "^3.7.5" + "prettier": "^2.1.2", + "typescript": "^4.0.3" }, "bugs": { "url": "https://jira.mariadb.org/projects/CONJS/" diff --git a/test/integration/datatype/test-buffer.js b/test/integration/datatype/test-buffer.js index 6987f35f..e578e222 100644 --- a/test/integration/datatype/test-buffer.js +++ b/test/integration/datatype/test-buffer.js @@ -27,9 +27,14 @@ describe('buffer', () => { ? "_binary'let\\'s rocks\\n😊 🤘'" : "_binary'let\\'s rocks\\nmore simple'" ); - shareConn.query('CREATE TEMPORARY TABLE BufEscape(b blob)'); shareConn - .query(' SELECT ' + shareConn.escape(buf) + ' t') + .query('DROP TABLE IF EXISTS BufEscape') + .then(() => { + return shareConn.query('CREATE TABLE BufEscape(b blob)'); + }) + .then(() => { + return shareConn.query(' SELECT ' + shareConn.escape(buf) + ' t'); + }) .then((rows) => { assert.deepEqual(rows, [{ t: buf }]); return shareConn.query('INSERT INTO BufEscape VALUE (' + shareConn.escape(buf) + ')'); @@ -49,15 +54,26 @@ describe('buffer', () => { it('text multi bytes characters', function (done) { if (!base.utf8Collation()) this.skip(); - shareConn.query( - 'CREATE TEMPORARY TABLE BlobTeststreamtest2 (id int primary key not null, st varchar(20), strm text) CHARSET utf8' - ); const toInsert1 = '\u00D8bbcdefgh\njklmn"'; const toInsert2 = '\u00D8abcdefgh\njklmn"'; - shareConn.query('insert into BlobTeststreamtest2 values(?, ?, ?)', [2, toInsert1, toInsert2]); shareConn - .query('select * from BlobTeststreamtest2') + .query('DROP TABLE IF EXISTS BlobTeststreamtest2') + .then(() => { + return shareConn.query( + 'CREATE TABLE BlobTeststreamtest2 (id int primary key not null, st varchar(20), strm text) CHARSET utf8' + ); + }) + .then(() => { + return shareConn.query('insert into BlobTeststreamtest2 values(?, ?, ?)', [ + 2, + toInsert1, + toInsert2 + ]); + }) + .then(() => { + return shareConn.query('select * from BlobTeststreamtest2'); + }) .then((rows) => { assert.deepEqual(rows, [{ id: 2, st: toInsert1, strm: toInsert2 }]); done(); @@ -76,12 +92,19 @@ describe('buffer', () => { }); it('blobs to buffer type', function (done) { - shareConn.query( - 'CREATE TEMPORARY TABLE blobToBuff (id int not null primary key auto_increment, test longblob, test2 blob, test3 text)' - ); - shareConn.query("insert into blobToBuff values(null, 'a','b','c')"); shareConn - .query('SELECT * FROM blobToBuff', [buf]) + .query('DROP TABLE IF EXISTS blobToBuff') + .then(() => { + return shareConn.query( + 'CREATE TABLE blobToBuff (id int not null primary key auto_increment, test longblob, test2 blob, test3 text)' + ); + }) + .then(() => { + return shareConn.query("insert into blobToBuff values(null, 'a','b','c')"); + }) + .then(() => { + return shareConn.query('SELECT * FROM blobToBuff', [buf]); + }) .then((rows) => { assert.strictEqual(rows.length, 1); assert.strictEqual(rows[0].id, 1); @@ -94,13 +117,23 @@ describe('buffer', () => { }); it('blob empty and null', function (done) { - shareConn.query('CREATE TEMPORARY TABLE blobEmpty (val LONGBLOB)'); - shareConn.query('insert into blobEmpty values (?)', ['']); - shareConn.query('insert into blobEmpty values (?)', ['hello']); - shareConn.query('insert into blobEmpty values (?)', [null]); - shareConn - .query('select * from blobEmpty') + .query('DROP TABLE IF EXISTS blobEmpty') + .then(() => { + return shareConn.query('CREATE TABLE blobEmpty (val LONGBLOB)'); + }) + .then(() => { + return shareConn.query('insert into blobEmpty values (?)', ['']); + }) + .then(() => { + return shareConn.query('insert into blobEmpty values (?)', ['hello']); + }) + .then(() => { + return shareConn.query('insert into blobEmpty values (?)', [null]); + }) + .then(() => { + return shareConn.query('select * from blobEmpty'); + }) .then((rows) => { assert.deepEqual(rows, [ { val: Buffer.from('') }, diff --git a/test/integration/datatype/test-datetime.js b/test/integration/datatype/test-datetime.js index 685447b9..4527f8e0 100644 --- a/test/integration/datatype/test-datetime.js +++ b/test/integration/datatype/test-datetime.js @@ -57,7 +57,10 @@ describe('datetime', () => { it('standard date', function (done) { //using distant server, time might be different - if (Conf.baseConfig.host !== 'localhost' && Conf.baseConfig.host !== 'mariadb.example.com') + if ( + (Conf.baseConfig.host !== 'localhost' && Conf.baseConfig.host !== 'mariadb.example.com') || + process.env.MAXSCALE_TEST_DISABLE + ) this.skip(); shareConn diff --git a/test/integration/datatype/test-enum.js b/test/integration/datatype/test-enum.js index c1255113..f39122ba 100644 --- a/test/integration/datatype/test-enum.js +++ b/test/integration/datatype/test-enum.js @@ -5,18 +5,31 @@ const { assert } = require('chai'); describe('enum', () => { it('enum type verification', (done) => { - shareConn.query( - 'CREATE TEMPORARY TABLE fruits (\n' + - ' id INT NOT NULL auto_increment PRIMARY KEY,\n' + - " fruit ENUM('apple','orange','pear'),\n" + - ' bushels INT)' - ); - shareConn.query('INSERT INTO fruits (fruit,bushels) VALUES (?, ?)', ['pear', 20]); - shareConn.query('INSERT INTO fruits (fruit,bushels) VALUES (?, ?)', ['apple', 100]); - shareConn.query('INSERT INTO fruits (fruit,bushels) VALUES (?, ?)', [2, 110]); - shareConn.query('INSERT INTO fruits (fruit,bushels) VALUES (?, ?)', [null, 120]); shareConn - .query('SELECT * FROM fruits') + .query('DROP TABLE IF EXISTS fruits') + .then(() => { + return shareConn.query( + 'CREATE TABLE fruits (\n' + + ' id INT NOT NULL auto_increment PRIMARY KEY,\n' + + " fruit ENUM('apple','orange','pear'),\n" + + ' bushels INT)' + ); + }) + .then(() => { + return shareConn.query('INSERT INTO fruits (fruit,bushels) VALUES (?, ?)', ['pear', 20]); + }) + .then(() => { + return shareConn.query('INSERT INTO fruits (fruit,bushels) VALUES (?, ?)', ['apple', 100]); + }) + .then(() => { + return shareConn.query('INSERT INTO fruits (fruit,bushels) VALUES (?, ?)', [2, 110]); + }) + .then(() => { + return shareConn.query('INSERT INTO fruits (fruit,bushels) VALUES (?, ?)', [null, 120]); + }) + .then(() => { + return shareConn.query('SELECT * FROM fruits'); + }) .then((rows) => { assert.deepEqual(rows, [ { id: 1, fruit: 'pear', bushels: 20 }, diff --git a/test/integration/datatype/test-float.js b/test/integration/datatype/test-float.js index ad92c9e1..7e2f82b7 100644 --- a/test/integration/datatype/test-float.js +++ b/test/integration/datatype/test-float.js @@ -7,7 +7,10 @@ const Long = require('long'); describe('float', () => { before((done) => { shareConn - .query('CREATE TEMPORARY TABLE testBigfloat (a FLOAT, b DOUBLE)') + .query('DROP TABLE IF EXISTS testBigfloat') + .then(() => { + return shareConn.query('CREATE TABLE testBigfloat (a FLOAT, b DOUBLE)'); + }) .then(() => { done(); }) @@ -29,9 +32,12 @@ describe('float', () => { it('bigint format', (done) => { shareConn - .query( - 'INSERT INTO testBigfloat values (-127.1, -128.2), (19925.0991, 900719925.4740991), (null, null)' - ) + .query('TRUNCATE testBigfloat') + .then(() => { + return shareConn.query( + 'INSERT INTO testBigfloat values (-127.1, -128.2), (19925.0991, 900719925.4740991), (null, null)' + ); + }) .then((rows) => { return shareConn.query('SELECT * FROM testBigfloat'); }) diff --git a/test/integration/datatype/test-geometry.js b/test/integration/datatype/test-geometry.js index 605948e8..7e8eda4d 100644 --- a/test/integration/datatype/test-geometry.js +++ b/test/integration/datatype/test-geometry.js @@ -7,15 +7,20 @@ describe('geometry data type', () => { it('Point format', function (done) { //MySQL 5.5 doesn't have ST_PointFromText function if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_point (g POINT)'); shareConn - .query( - 'INSERT INTO gis_point VALUES\n' + - " (ST_PointFromText('POINT(10 10)')),\n" + - " (ST_PointFromText('POINT(20 10)')),\n" + - " (ST_PointFromText('POINT(20 20)')),\n" + - " (ST_PointFromText('POINT(10 20)'))" - ) + .query('DROP TABLE IF EXISTS gis_point') + .then(() => { + return shareConn.query('CREATE TABLE gis_point (g POINT)'); + }) + .then(() => { + return shareConn.query( + 'INSERT INTO gis_point VALUES\n' + + " (ST_PointFromText('POINT(10 10)')),\n" + + " (ST_PointFromText('POINT(20 10)')),\n" + + " (ST_PointFromText('POINT(20 20)')),\n" + + " (ST_PointFromText('POINT(10 20)'))" + ); + }) .then(() => { return shareConn.query('SELECT * FROM gis_point'); }) @@ -176,10 +181,16 @@ describe('geometry data type', () => { it('Point Insert', function (done) { //mysql < 8 doesn't permit sending empty data if (!shareConn.info.isMariaDB()) this.skip(); - - shareConn.query('CREATE TEMPORARY TABLE gis_point_insert (g POINT)'); shareConn - .query('INSERT INTO gis_point_insert VALUES (?)', [{ type: 'Point', coordinates: [10, 10] }]) + .query('DROP TABLE IF EXISTS gis_point_insert') + .then(() => { + return shareConn.query('CREATE TABLE gis_point_insert (g POINT)'); + }) + .then(() => { + return shareConn.query('INSERT INTO gis_point_insert VALUES (?)', [ + { type: 'Point', coordinates: [10, 10] } + ]); + }) .then(() => { return shareConn.query('INSERT INTO gis_point_insert VALUES (?)', [ { type: 'Point', coordinates: [20, 10] } @@ -212,13 +223,17 @@ describe('geometry data type', () => { }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'Point' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'Point' } : null } @@ -231,15 +246,19 @@ describe('geometry data type', () => { it('LineString format', function (done) { //MySQL 5.5 doesn't have ST_LineFromText function if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); - - shareConn.query('CREATE TEMPORARY TABLE gis_line (g LINESTRING)'); shareConn - .query( - 'INSERT INTO gis_line VALUES\n' + - " (ST_LineFromText('LINESTRING(0 0,0 10,10 0)')),\n" + - " (ST_LineStringFromText('LINESTRING(10 10,20 10,20 20,10 20,10 10)')),\n" + - ' (ST_LineStringFromWKB(ST_AsWKB(LineString(Point(10, 10), Point(40, 10)))))' - ) + .query('DROP TABLE IF EXISTS gis_line') + .then(() => { + return shareConn.query('CREATE TABLE gis_line (g LINESTRING)'); + }) + .then(() => { + return shareConn.query( + 'INSERT INTO gis_line VALUES\n' + + " (ST_LineFromText('LINESTRING(0 0,0 10,10 0)')),\n" + + " (ST_LineStringFromText('LINESTRING(10 10,20 10,20 20,10 20,10 10)')),\n" + + ' (ST_LineStringFromWKB(ST_AsWKB(LineString(Point(10, 10), Point(40, 10)))))' + ); + }) .then(() => { return shareConn.query('SELECT * FROM gis_line'); }) @@ -284,19 +303,23 @@ describe('geometry data type', () => { it('LineString insert', function (done) { if (!shareConn.info.isMariaDB()) this.skip(); - - shareConn.query('CREATE TEMPORARY TABLE gis_line_insert (g LINESTRING)'); shareConn - .query('INSERT INTO gis_line_insert VALUES (?)', [ - { - type: 'LineString', - coordinates: [ - [0, 0], - [0, 10], - [10, 0] - ] - } - ]) + .query('DROP TABLE IF EXISTS gis_line_insert') + .then(() => { + return shareConn.query('CREATE TABLE gis_line_insert (g LINESTRING)'); + }) + .then(() => { + return shareConn.query('INSERT INTO gis_line_insert VALUES (?)', [ + { + type: 'LineString', + coordinates: [ + [0, 0], + [0, 10], + [10, 0] + ] + } + ]); + }) .then(() => { return shareConn.query('INSERT INTO gis_line_insert VALUES (?)', [ { @@ -339,13 +362,17 @@ describe('geometry data type', () => { }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'LineString' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'LineString' } : null } @@ -358,15 +385,19 @@ describe('geometry data type', () => { it('Polygon format', function (done) { //MySQL 5.5 doesn't have ST_PolygonFromText function if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); - - shareConn.query('CREATE TEMPORARY TABLE gis_polygon (g POLYGON)'); shareConn - .query( - 'INSERT INTO gis_polygon VALUES\n' + - " (ST_PolygonFromText('POLYGON((10 10,20 10,20 20,10 20,10 10))')),\n" + - " (ST_PolyFromText('POLYGON((0 0,50 0,50 50,0 50,0 0), (10 10,20 10,20 20,10 20,10 10))')),\n" + - ' (ST_PolyFromWKB(ST_AsWKB(Polygon(LineString(Point(0, 0), Point(30, 0), Point(30, 30), Point(0, 0))))))' - ) + .query('DROP TABLE IF EXISTS gis_polygon') + .then(() => { + return shareConn.query('CREATE TABLE gis_polygon (g POLYGON)'); + }) + .then(() => { + return shareConn.query( + 'INSERT INTO gis_polygon VALUES\n' + + " (ST_PolygonFromText('POLYGON((10 10,20 10,20 20,10 20,10 10))')),\n" + + " (ST_PolyFromText('POLYGON((0 0,50 0,50 50,0 50,0 0), (10 10,20 10,20 20,10 20,10 10))')),\n" + + ' (ST_PolyFromWKB(ST_AsWKB(Polygon(LineString(Point(0, 0), Point(30, 0), Point(30, 30), Point(0, 0))))))' + ); + }) .then(() => { return shareConn.query('SELECT * FROM gis_polygon'); }) @@ -429,23 +460,27 @@ describe('geometry data type', () => { it('Polygon insert', function (done) { //mysql < 8 doesn't permit sending empty data if (!shareConn.info.isMariaDB()) this.skip(); - - shareConn.query('CREATE TEMPORARY TABLE gis_polygon_insert (g POLYGON)'); shareConn - .query('INSERT INTO gis_polygon_insert VALUES (?)', [ - { - type: 'Polygon', - coordinates: [ - [ - [10, 10], - [20, 10], - [20, 20], - [10, 20], - [10, 10] + .query('DROP TABLE IF EXISTS gis_polygon_insert') + .then(() => { + return shareConn.query('CREATE TABLE gis_polygon_insert (g POLYGON)'); + }) + .then(() => { + return shareConn.query('INSERT INTO gis_polygon_insert VALUES (?)', [ + { + type: 'Polygon', + coordinates: [ + [ + [10, 10], + [20, 10], + [20, 20], + [10, 20], + [10, 10] + ] ] - ] - } - ]) + } + ]); + }) .then(() => { return shareConn.query('INSERT INTO gis_polygon_insert VALUES (?)', [ { @@ -522,13 +557,17 @@ describe('geometry data type', () => { }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'Polygon' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'Polygon' } : null } @@ -543,14 +582,19 @@ describe('geometry data type', () => { if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 6)) this.skip(); if (shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 1, 4)) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_multi_point (g MULTIPOINT)'); shareConn - .query( - 'INSERT INTO gis_multi_point VALUES\n' + - " (ST_MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)')),\n" + - " (ST_MPointFromText('MULTIPOINT(1 1,11 11,11 21,21 21)')),\n" + - ' (ST_MPointFromWKB(ST_AsWKB(MultiPoint(Point(3, 6), Point(4, 10)))))' - ) + .query('DROP TABLE IF EXISTS gis_multi_point') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_point (g MULTIPOINT)'); + }) + .then(() => { + return shareConn.query( + 'INSERT INTO gis_multi_point VALUES\n' + + " (ST_MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)')),\n" + + " (ST_MPointFromText('MULTIPOINT(1 1,11 11,11 21,21 21)')),\n" + + ' (ST_MPointFromWKB(ST_AsWKB(MultiPoint(Point(3, 6), Point(4, 10)))))' + ); + }) .then(() => { return shareConn.query('SELECT * FROM gis_multi_point'); }) @@ -597,19 +641,24 @@ describe('geometry data type', () => { //mysql < 8 doesn't permit sending empty data if (!shareConn.info.isMariaDB()) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_multi_point_insert (g MULTIPOINT)'); shareConn - .query('INSERT INTO gis_multi_point_insert VALUES (?)', [ - { - type: 'MultiPoint', - coordinates: [ - [0, 0], - [10, 10], - [10, 20], - [20, 20] - ] - } - ]) + .query('DROP TABLE IF EXISTS gis_multi_point_insert') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_point_insert (g MULTIPOINT)'); + }) + .then(() => { + return shareConn.query('INSERT INTO gis_multi_point_insert VALUES (?)', [ + { + type: 'MultiPoint', + coordinates: [ + [0, 0], + [10, 10], + [10, 20], + [20, 20] + ] + } + ]); + }) .then(() => { return shareConn.query('INSERT INTO gis_multi_point_insert VALUES (?)', [ { type: 'MultiPoint', coordinates: [[10, 0]] } @@ -649,13 +698,17 @@ describe('geometry data type', () => { }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPoint' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPoint' } : null } @@ -670,14 +723,19 @@ describe('geometry data type', () => { if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 6)) this.skip(); if (shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 1, 4)) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_multi_line (g MULTILINESTRING)'); shareConn - .query( - 'INSERT INTO gis_multi_line VALUES\n' + - " (ST_MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))')),\n" + - " (ST_MLineFromText('MULTILINESTRING((10 48,10 21,10 0))')),\n" + - ' (ST_MLineFromWKB(ST_AsWKB(MultiLineString(LineString(Point(1, 2), Point(3, 5)), LineString(Point(2, 5), Point(5, 8), Point(21, 7))))))' - ) + .query('DROP TABLE IF EXISTS gis_multi_line') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_line (g MULTILINESTRING)'); + }) + .then(() => { + return shareConn.query( + 'INSERT INTO gis_multi_line VALUES\n' + + " (ST_MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))')),\n" + + " (ST_MLineFromText('MULTILINESTRING((10 48,10 21,10 0))')),\n" + + ' (ST_MLineFromWKB(ST_AsWKB(MultiLineString(LineString(Point(1, 2), Point(3, 5)), LineString(Point(2, 5), Point(5, 8), Point(21, 7))))))' + ); + }) .then(() => { return shareConn.query('SELECT * FROM gis_multi_line'); }) @@ -738,25 +796,30 @@ describe('geometry data type', () => { //mysql < 8 doesn't permit sending empty data if (!shareConn.info.isMariaDB()) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_multi_line_insert (g MULTILINESTRING)'); shareConn - .query('INSERT INTO gis_multi_line_insert VALUES (?)', [ - { - type: 'MultiLineString', - coordinates: [ - [ - [10, 48], - [10, 21], - [10, 0] - ], - [ - [16, 0], - [16, 23], - [16, 48] + .query('DROP TABLE IF EXISTS gis_multi_line_insert') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_line_insert (g MULTILINESTRING)'); + }) + .then(() => { + return shareConn.query('INSERT INTO gis_multi_line_insert VALUES (?)', [ + { + type: 'MultiLineString', + coordinates: [ + [ + [10, 48], + [10, 21], + [10, 0] + ], + [ + [16, 0], + [16, 23], + [16, 48] + ] ] - ] - } - ]) + } + ]); + }) .then(() => { return shareConn.query('INSERT INTO gis_multi_line_insert VALUES (?)', [ { @@ -822,19 +885,25 @@ describe('geometry data type', () => { }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiLineString' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiLineString' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiLineString' } : null } @@ -849,14 +918,19 @@ describe('geometry data type', () => { if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 6)) this.skip(); if (shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 1, 4)) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_multi_polygon (g MULTIPOLYGON)'); shareConn - .query( - 'INSERT INTO gis_multi_polygon VALUES\n' + - " (ST_MultiPolygonFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))')),\n" + - " (ST_MPolyFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))')),\n" + - ' (ST_MPolyFromWKB(ST_AsWKB(MultiPolygon(Polygon(LineString(Point(0, 3), Point(3, 3), Point(3, 0), Point(0, 3)))))))' - ) + .query('DROP TABLE IF EXISTS gis_multi_polygon') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_polygon (g MULTIPOLYGON)'); + }) + .then(() => { + return shareConn.query( + 'INSERT INTO gis_multi_polygon VALUES\n' + + " (ST_MultiPolygonFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))')),\n" + + " (ST_MPolyFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))')),\n" + + ' (ST_MPolyFromWKB(ST_AsWKB(MultiPolygon(Polygon(LineString(Point(0, 3), Point(3, 3), Point(3, 0), Point(0, 3)))))))' + ); + }) .then(() => { return shareConn.query('SELECT * FROM gis_multi_polygon'); }) @@ -951,40 +1025,45 @@ describe('geometry data type', () => { //mysql < 8 doesn't permit sending empty data if (!shareConn.info.isMariaDB()) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_multi_polygon_insert (g MULTIPOLYGON)'); shareConn - .query('INSERT INTO gis_multi_polygon_insert VALUES (?)', [ - { - type: 'MultiPolygon', - coordinates: [ - [ + .query('DROP TABLE IF EXISTS gis_multi_polygon_insert') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_polygon_insert (g MULTIPOLYGON)'); + }) + .then(() => { + return shareConn.query('INSERT INTO gis_multi_polygon_insert VALUES (?)', [ + { + type: 'MultiPolygon', + coordinates: [ [ - [28, 26], - [28, 0], - [84, 0], - [84, 42], - [28, 26] + [ + [28, 26], + [28, 0], + [84, 0], + [84, 42], + [28, 26] + ], + [ + [52, 18], + [66, 23], + [73, 9], + [48, 6], + [52, 18] + ] ], [ - [52, 18], - [66, 23], - [73, 9], - [48, 6], - [52, 18] - ] - ], - [ - [ - [59, 18], - [67, 18], - [67, 13], - [59, 13], - [59, 18] + [ + [59, 18], + [67, 18], + [67, 13], + [59, 13], + [59, 18] + ] ] ] - ] - } - ]) + } + ]); + }) .then(() => { return shareConn.query('INSERT INTO gis_multi_polygon_insert VALUES (?)', [ { @@ -1101,25 +1180,33 @@ describe('geometry data type', () => { }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPolygon' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPolygon' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPolygon' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPolygon' } : null } @@ -1137,17 +1224,22 @@ describe('geometry data type', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE gis_geometrycollection (g GEOMETRYCOLLECTION)'); conn - .query( - 'INSERT INTO gis_geometrycollection VALUES\n' + - " (ST_GeomCollFromText('GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(0 0,10 10))')),\n" + - ' (ST_GeometryFromWKB(ST_AsWKB(GeometryCollection(Point(44, 6), LineString(Point(3, 6), Point(7, 9))))))' + - (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(8, 0, 0) - ? '' - : ",(ST_GeomFromText('GeometryCollection()')),\n" + - " (ST_GeomFromText('GeometryCollection EMPTY'))") - ) + .query('DROP TABLE IF EXISTS gis_geometrycollection') + .then(() => { + return shareConn.query('CREATE TABLE gis_geometrycollection (g GEOMETRYCOLLECTION)'); + }) + .then(() => { + return conn.query( + 'INSERT INTO gis_geometrycollection VALUES\n' + + " (ST_GeomCollFromText('GEOMETRYCOLLECTION(POINT(0 0), LINESTRING(0 0,10 10))')),\n" + + ' (ST_GeometryFromWKB(ST_AsWKB(GeometryCollection(Point(44, 6), LineString(Point(3, 6), Point(7, 9))))))' + + (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(8, 0, 0) + ? '' + : ",(ST_GeomFromText('GeometryCollection()')),\n" + + " (ST_GeomFromText('GeometryCollection EMPTY'))") + ); + }) .then(() => { return conn.query('SELECT * FROM gis_geometrycollection'); }) @@ -1261,81 +1353,88 @@ describe('geometry data type', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE gis_geometrycollection_ins (g GEOMETRYCOLLECTION)'); conn - .query('INSERT INTO gis_geometrycollection_ins VALUES (?)', [ - { - type: 'GeometryCollection', - geometries: [ - { - type: 'Point', - coordinates: [10, 10] - }, - { - type: 'LineString', - coordinates: [ - [0, 0], - [0, 10], - [10, 0] - ] - }, - { - type: 'MultiPoint', - coordinates: [ - [0, 0], - [10, 10], - [10, 20], - [20, 20] - ] - }, - { - type: 'MultiLineString', - coordinates: [ - [ - [10, 48], - [10, 21], + .query('DROP TABLE IF EXISTS gis_geometrycollection_ins') + .then(() => { + return shareConn.query( + 'CREATE TABLE gis_geometrycollection_ins (g GEOMETRYCOLLECTION)' + ); + }) + .then(() => { + return conn.query('INSERT INTO gis_geometrycollection_ins VALUES (?)', [ + { + type: 'GeometryCollection', + geometries: [ + { + type: 'Point', + coordinates: [10, 10] + }, + { + type: 'LineString', + coordinates: [ + [0, 0], + [0, 10], [10, 0] - ], - [ - [16, 0], - [16, 23], - [16, 48] ] - ] - }, - { - type: 'MultiPolygon', - coordinates: [ - [ + }, + { + type: 'MultiPoint', + coordinates: [ + [0, 0], + [10, 10], + [10, 20], + [20, 20] + ] + }, + { + type: 'MultiLineString', + coordinates: [ [ - [28, 26], - [28, 0], - [84, 0], - [84, 42], - [28, 26] + [10, 48], + [10, 21], + [10, 0] ], [ - [52, 18], - [66, 23], - [73, 9], - [48, 6], - [52, 18] + [16, 0], + [16, 23], + [16, 48] ] - ], - [ + ] + }, + { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [28, 26], + [28, 0], + [84, 0], + [84, 42], + [28, 26] + ], + [ + [52, 18], + [66, 23], + [73, 9], + [48, 6], + [52, 18] + ] + ], [ - [59, 18], - [67, 18], - [67, 13], - [59, 13], - [59, 18] + [ + [59, 18], + [67, 18], + [67, 13], + [59, 13], + [59, 18] + ] ] ] - ] - } - ] - } - ]) + } + ] + } + ]); + }) .then(() => { return conn.query('INSERT INTO gis_geometrycollection_ins VALUES (?)', [ { diff --git a/test/integration/datatype/test-integer.js b/test/integration/datatype/test-integer.js index f73a16ad..432e9fb2 100644 --- a/test/integration/datatype/test-integer.js +++ b/test/integration/datatype/test-integer.js @@ -7,7 +7,12 @@ const Long = require('long'); describe('integer with big value', () => { before((done) => { shareConn - .query('CREATE TEMPORARY TABLE testBigint (v BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY)') + .query('DROP TABLE IF EXISTS testBigint') + .then(() => { + return shareConn.query( + 'CREATE TABLE testBigint (v BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY)' + ); + }) .then(() => { done(); }) @@ -31,7 +36,10 @@ describe('integer with big value', () => { it('decimal value without truncation', function (done) { shareConn - .query('CREATE TEMPORARY TABLE floatTest (t DOUBLE, t2 DECIMAL(32,16))') + .query('DROP TABLE IF EXISTS floatTest') + .then(() => { + return shareConn.query('CREATE TABLE floatTest (t DOUBLE, t2 DECIMAL(32,16))'); + }) .then(() => { return shareConn.query( 'INSERT INTO floatTest VALUES (-0.9999237060546875, 9999237060546875.9999237060546875)' @@ -62,16 +70,21 @@ describe('integer with big value', () => { return shareConn.query('INSERT INTO testBigint values ()'); }) .then((rows) => { - assert.strictEqual(rows.insertId.toNumber(), 9007199254740992); + assert.strictEqual(rows.insertId, 9007199254740992); + return shareConn.query('INSERT INTO testBigint values ()'); + }) + .then((rows) => { + assert.strictEqual(rows.insertId, 9007199254740993); return shareConn.query('SELECT * FROM testBigint'); }) .then((rows) => { - assert.strictEqual(rows.length, 5); + assert.strictEqual(rows.length, 6); assert.strictEqual(rows[0].v, -9007199254740991); assert.strictEqual(rows[1].v, 127); assert.strictEqual(rows[2].v, 128); assert.strictEqual(rows[3].v, 9007199254740991); assert.strictEqual(rows[4].v, 9007199254740992); + assert.strictEqual(rows[4].v, 9007199254740993); assert.strictEqual(typeof rows[3].v, 'number'); return shareConn.query({ supportBigNumbers: true, @@ -79,67 +92,121 @@ describe('integer with big value', () => { }); }) .then((rows) => { - assert.strictEqual(rows.length, 5); + assert.strictEqual(rows.length, 6); assert.strictEqual(rows[0].v, -9007199254740991); assert.strictEqual(rows[1].v, 127); assert.strictEqual(rows[2].v, 128); assert.strictEqual(rows[3].v, 9007199254740991); assert.strictEqual(typeof rows[4].v, 'object'); assert.strictEqual(rows[4].v.toString(), '9007199254740992'); + assert.strictEqual(rows[5].v.toString(), '9007199254740993'); return shareConn.query({ bigNumberStrings: true, sql: 'SELECT * FROM testBigint' }); }) .then((rows) => { - assert.strictEqual(rows.length, 5); + assert.strictEqual(rows.length, 6); assert.strictEqual(rows[0].v, -9007199254740991); assert.strictEqual(rows[1].v, 127); assert.strictEqual(rows[2].v, 128); assert.strictEqual(rows[3].v, 9007199254740991); assert.strictEqual(rows[4].v, '9007199254740992'); + assert.strictEqual(rows[5].v, '9007199254740993'); assert.strictEqual(typeof rows[4].v, 'string'); - done(); + return shareConn.query({ + supportBigInt: true, + sql: 'SELECT * FROM testBigint' + }); + }) + .then((rows) => { + assert.strictEqual(rows.length, 6); + assert.strictEqual(rows[0].v, -9007199254740991n); + assert.strictEqual(rows[1].v, 127n); + assert.strictEqual(rows[2].v, 128n); + assert.strictEqual(rows[3].v, 9007199254740991n); + assert.strictEqual(rows[4].v, 9007199254740992n); + assert.strictEqual(rows[5].v, 9007199254740993n); + assert.strictEqual(typeof rows[4].v, 'bigint'); + return base.createConnection({ supportBigInt: true }); + }) + .then((conn2) => { + conn2 + .query('INSERT INTO testBigint values ()') + .then((rows) => { + assert.strictEqual(rows.insertId, 9007199254740994n); + conn2.end(); + done(); + }) + .catch(done); }) .catch(done); }); it('bigint format null ', (done) => { - shareConn.query('CREATE TEMPORARY TABLE testBigintNull (v BIGINT)'); - shareConn.query('INSERT INTO testBigintNull values (127), (null)'); - - const checkResult = (rows) => { - assert.strictEqual(rows.length, 2); - assert.strictEqual(rows[0].v, 127); - assert.strictEqual(rows[1].v, null); - }; - - shareConn.query('SELECT * FROM testBigintNull').then(checkResult); - shareConn - .query({ supportBigNumbers: true, sql: 'SELECT * FROM testBigintNull' }) - .then(checkResult); shareConn - .query({ bigNumberStrings: true, sql: 'SELECT * FROM testBigintNull' }) + .query('DROP TABLE IF EXISTS testBigintNull') + .then(() => { + return shareConn.query('CREATE TABLE testBigintNull (v BIGINT)'); + }) + .then(() => { + return shareConn.query('INSERT INTO testBigintNull values (127), (null)'); + }) + .then(() => { + return shareConn.query('SELECT * FROM testBigintNull'); + }) .then((rows) => { - checkResult(rows); + assert.strictEqual(rows.length, 2); + assert.strictEqual(rows[0].v, 127); + assert.strictEqual(rows[1].v, null); + return shareConn.query({ supportBigNumbers: true, sql: 'SELECT * FROM testBigintNull' }); + }) + .then((rows) => { + assert.strictEqual(rows.length, 2); + assert.strictEqual(rows[0].v, 127); + assert.strictEqual(rows[1].v, null); + return shareConn.query({ bigNumberStrings: true, sql: 'SELECT * FROM testBigintNull' }); + }) + .then((rows) => { + assert.strictEqual(rows.length, 2); + assert.strictEqual(rows[0].v, 127); + assert.strictEqual(rows[1].v, null); + return shareConn.query({ supportBigInt: true, sql: 'SELECT * FROM testBigintNull' }); + }) + .then((rows) => { + assert.strictEqual(rows.length, 2); + assert.strictEqual(rows[0].v, 127n); + assert.strictEqual(rows[1].v, null); done(); }); }); it('numeric fields conversion to int', (done) => { - shareConn.query( - 'CREATE TEMPORARY TABLE intAllField (' + - 't1 TINYINT(1), t2 SMALLINT(1), t3 MEDIUMINT(1), t4 INT(1), t5 BIGINT(1), t6 DECIMAL(1), t7 FLOAT, t8 DOUBLE)' - ); - shareConn.query( - 'INSERT INTO intAllField VALUES (null, null, null, null, null, null, null, null)' - ); - shareConn.query('INSERT INTO intAllField VALUES (0, 0, 0, 0, 0, 0, 0, 0)'); - shareConn.query('INSERT INTO intAllField VALUES (1, 1, 1, 1, 1, 1, 1, 1)'); - shareConn.query('INSERT INTO intAllField VALUES (2, 2, 2, 2, 2, 2, 2, 2)'); - shareConn - .query('SELECT * FROM intAllField') + .query('DROP TABLE IF EXISTS intAllField') + .then(() => { + return shareConn.query( + 'CREATE TABLE intAllField (' + + 't1 TINYINT(1), t2 SMALLINT(1), t3 MEDIUMINT(1), t4 INT(1), t5 BIGINT(1), t6 DECIMAL(1), t7 FLOAT, t8 DOUBLE)' + ); + }) + .then(() => { + return shareConn.query( + 'INSERT INTO intAllField VALUES (null, null, null, null, null, null, null, null)' + ); + }) + .then(() => { + return shareConn.query('INSERT INTO intAllField VALUES (0, 0, 0, 0, 0, 0, 0, 0)'); + }) + .then(() => { + return shareConn.query('INSERT INTO intAllField VALUES (1, 1, 1, 1, 1, 1, 1, 1)'); + }) + .then(() => { + return shareConn.query('INSERT INTO intAllField VALUES (2, 2, 2, 2, 2, 2, 2, 2)'); + }) + .then(() => { + return shareConn.query('SELECT * FROM intAllField'); + }) .then((res) => { assert.deepEqual(res, [ { @@ -164,9 +231,14 @@ describe('integer with big value', () => { it('using very big number', function (done) { const maxValue = Long.fromString('18446744073709551615', true); base.createConnection({ supportBigNumbers: true }).then((conn) => { - conn.query('CREATE TEMPORARY TABLE BIG_NUMBER (val BIGINT unsigned)'); conn - .query('INSERT INTO BIG_NUMBER values (?), (?)', [10, maxValue]) + .query('DROP TABLE IF EXISTS BIG_NUMBER') + .then(() => { + return conn.query('CREATE TABLE BIG_NUMBER (val BIGINT unsigned)'); + }) + .then(() => { + return conn.query('INSERT INTO BIG_NUMBER values (?), (?)', [10, maxValue]); + }) .then(() => { return conn.query('SELECT * FROM BIG_NUMBER LIMIT ?', [maxValue]); }) @@ -178,4 +250,27 @@ describe('integer with big value', () => { .catch(done); }); }); + + it('using very big number bigint', function (done) { + const maxValue = 18446744073709551615n; + base.createConnection({ supportBigInt: true }).then((conn) => { + conn + .query('DROP TABLE IF EXISTS BIG_NUMBER') + .then(() => { + return conn.query('CREATE TABLE BIG_NUMBER (val BIGINT unsigned)'); + }) + .then(() => { + return conn.query('INSERT INTO BIG_NUMBER values (?), (?)', [10, maxValue]); + }) + .then(() => { + return conn.query('SELECT * FROM BIG_NUMBER LIMIT ?', [maxValue]); + }) + .then((res) => { + assert.deepEqual(res, [{ val: 10n }, { val: maxValue }]); + conn.end(); + done(); + }) + .catch(done); + }); + }); }); diff --git a/test/integration/datatype/test-json.js b/test/integration/datatype/test-json.js index da5d579d..5336e2d1 100644 --- a/test/integration/datatype/test-json.js +++ b/test/integration/datatype/test-json.js @@ -26,24 +26,40 @@ describe('json', () => { this.skip(); } - shareConn.query('CREATE TEMPORARY TABLE `test-json-insert-type` (val1 JSON)'); const obj = { id: 2, val: 'test' }; - shareConn.query( - { - stringifyObjects: true, - sql: 'INSERT INTO `test-json-insert-type` values (?)' - }, - [obj] - ); - shareConn.query('INSERT INTO `test-json-insert-type` values (?)', [JSON.stringify(obj)]); - validateJSON('test-json-insert-type', done); + shareConn + .query('DROP TABLE IF EXISTS `test-json-insert-type`') + .then(() => { + return shareConn.query('CREATE TABLE `test-json-insert-type` (val1 JSON)'); + }) + .then(() => { + return shareConn.query( + { + stringifyObjects: true, + sql: 'INSERT INTO `test-json-insert-type` values (?)' + }, + [obj] + ); + }) + .then(() => { + return shareConn.query('INSERT INTO `test-json-insert-type` values (?)', [ + JSON.stringify(obj) + ]); + }) + .then(() => { + validateJSON('test-json-insert-type', done); + }) + .catch(done); }); function validateJSON(tableName, done) { shareConn .query('SELECT * FROM `' + tableName + '`') .then((rows) => { - if (shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 5, 2)) { + if ( + (shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 5, 2)) || + process.env.MAXSCALE_TEST_DISABLE + ) { const val1 = JSON.parse(rows[0].val1); const val2 = JSON.parse(rows[1].val1); assert.equal(val1.id, 2); @@ -70,26 +86,37 @@ describe('json', () => { this.skip(); } - shareConn.query( - 'CREATE TEMPORARY TABLE `test-json-return-type` (val1 JSON, val2 LONGTEXT, val3 LONGBLOB)' - ); const obj = { id: 2, val: 'test' }; const jsonString = JSON.stringify(obj); - shareConn.query( - "INSERT INTO `test-json-return-type` values ('" + - jsonString + - "','" + - jsonString + - "','" + - jsonString + - "')" - ); shareConn - .query('SELECT * FROM `test-json-return-type`') + .query('DROP TABLE IF EXISTS `test-json-return-type`') + .then(() => { + return shareConn.query( + 'CREATE TABLE `test-json-return-type` (val1 JSON, val2 LONGTEXT, val3 LONGBLOB)' + ); + }) + .then(() => { + return shareConn.query( + "INSERT INTO `test-json-return-type` values ('" + + jsonString + + "','" + + jsonString + + "','" + + jsonString + + "')" + ); + }) + .then(() => { + return shareConn.query('SELECT * FROM `test-json-return-type`'); + }) .then((rows) => { if (shareConn.info.isMariaDB()) { - if (shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2)) { + if ( + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE + ) { assert.deepEqual(rows[0].val1, obj); } else { assert.equal(rows[0].val1, jsonString); @@ -108,29 +135,36 @@ describe('json', () => { it('disable json format', function (done) { //server permit JSON format if ( - (shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 5, 2)) || + (shareConn.info.isMariaDB() && + (!shareConn.info.hasMinVersion(10, 5, 2) || process.env.MAXSCALE_TEST_DISABLE)) || !shareConn.info.isMariaDB() ) { this.skip(); } base.createConnection({ autoJsonMap: false }).then((conn) => { - conn.query( - 'CREATE TEMPORARY TABLE `test-json-return-type` (val1 JSON, val2 LONGTEXT, val3 LONGBLOB)' - ); const obj = { id: 2, val: 'test' }; const jsonString = JSON.stringify(obj); - conn.query( - "INSERT INTO `test-json-return-type` values ('" + - jsonString + - "','" + - jsonString + - "','" + - jsonString + - "')" - ); - conn - .query('SELECT * FROM `test-json-return-type`') + .query('DROP TABLE IF EXISTS `test-json-return-type`') + .then(() => { + return conn.query( + 'CREATE TABLE `test-json-return-type` (val1 JSON, val2 LONGTEXT, val3 LONGBLOB)' + ); + }) + .then(() => { + return conn.query( + "INSERT INTO `test-json-return-type` values ('" + + jsonString + + "','" + + jsonString + + "','" + + jsonString + + "')" + ); + }) + .then(() => { + return conn.query('SELECT * FROM `test-json-return-type`'); + }) .then((rows) => { assert.equal(rows[0].val1, jsonString); assert.equal(rows[0].val2, jsonString); diff --git a/test/integration/datatype/test-mapping.js b/test/integration/datatype/test-mapping.js index c62e06ad..2f8e0524 100644 --- a/test/integration/datatype/test-mapping.js +++ b/test/integration/datatype/test-mapping.js @@ -79,98 +79,109 @@ describe('mapping', () => { ]; before((done) => { - shareConn.query( - 'CREATE TEMPORARY TABLE nullMappingTable(' + - 't1 BIT(1) NULL,' + - 't2 BIT(2) NULL,' + - 't3 TINYINT NULL,' + - 't4 TINYINT UNSIGNED NULL,' + - 't5 BOOL NULL,' + - 't6 SMALLINT NULL,' + - 't7 SMALLINT UNSIGNED NULL,' + - 't8 MEDIUMINT NULL,' + - 't9 MEDIUMINT UNSIGNED NULL,' + - 't10 INT NULL,' + - 't11 INT UNSIGNED NULL,' + - 't12 BIGINT NULL,' + - 't13 BIGINT UNSIGNED NULL,' + - 't14 FLOAT NULL,' + - 't15 DOUBLE NULL,' + - 't16 DECIMAL NULL,' + - 't17 DATE NULL,' + - 't18 DATETIME NULL,' + - 't19 TIMESTAMP NULL,' + - 't20 TIME NULL,' + - 't21 YEAR NULL,' + - 't22 CHAR(1) NULL,' + - 't23 CHAR(1) binary NULL,' + - 't24 VARCHAR(1) NULL,' + - 't25 VARCHAR(10) BINARY NULL,' + - 't26 BINARY(10) NULL,' + - 't27 VARBINARY(10) NULL)' - ); - shareConn.query('INSERT INTO nullMappingTable values ()'); - if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6)) { - //MySQL 5.5 doesn't permit DATETIME/TIMESTAMP with microseconds - done(); - } else { - //MySQL 5.6 delete YEAR(2) type - if (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 6)) { - initValue[23] = 2018; - nullValue[23] = 1999; - } - shareConn - .query( - 'CREATE TEMPORARY TABLE mappingTable(' + - 't1 BIT(1),' + - 't2 BIT(2),' + - 't3 TINYINT,' + - 't4 TINYINT UNSIGNED,' + - 't5 BOOL default 1,' + - 't6 SMALLINT default 1,' + - 't7 SMALLINT UNSIGNED default 0,' + - 't8 MEDIUMINT default 1,' + - 't9 MEDIUMINT UNSIGNED default 0,' + - 't10 INT default 1,' + - 't11 INT UNSIGNED default 0,' + - 't12 BIGINT default 1,' + - 't13 BIGINT UNSIGNED default 0,' + - 't14 FLOAT default 0,' + - 't15 DOUBLE default 1,' + - 't16 DECIMAL default 0,' + - 't17 DECIMAL(15,4) default 0,' + - "t18 DATE default '2001-01-01'," + - "t19 DATETIME(6) default '2001-01-01 00:00:00'," + - "t20 TIMESTAMP(6) default '2001-01-01 00:00:00'," + - "t21 TIMESTAMP(0) null default '2001-01-01 00:00:00'," + - 't22 TIMESTAMP null, ' + - "t23 TIME(6) default '22:11:00.560001'," + - (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 6) - ? 't24 YEAR(4) default 99,' - : 't24 YEAR(2) default 99,') + - 't25 YEAR(4) default 2011,' + - "t26 CHAR(1) default '0'," + - "t27 CHAR(1) binary default '0'," + - "t28 VARCHAR(1) default '1'," + - 't29 VARCHAR(10) BINARY default 0x5a,' + - 't30 BINARY(10) default 0x1,' + - 't31 VARBINARY(10) default 0x1' + - ')' - ) - .then(() => { - return shareConn.query( - 'INSERT INTO mappingTable VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', - initValue - ); - }) - .then(() => { - return shareConn.query('INSERT INTO mappingTable VALUES ()'); - }) - .then(() => { + shareConn + .query('DROP TABLE IF EXISTS nullMappingTable') + .then(() => { + return shareConn.query('DROP TABLE IF EXISTS mappingTable'); + }) + .then(() => { + return shareConn.query( + 'CREATE TABLE nullMappingTable(' + + 't1 BIT(1) NULL,' + + 't2 BIT(2) NULL,' + + 't3 TINYINT NULL,' + + 't4 TINYINT UNSIGNED NULL,' + + 't5 BOOL NULL,' + + 't6 SMALLINT NULL,' + + 't7 SMALLINT UNSIGNED NULL,' + + 't8 MEDIUMINT NULL,' + + 't9 MEDIUMINT UNSIGNED NULL,' + + 't10 INT NULL,' + + 't11 INT UNSIGNED NULL,' + + 't12 BIGINT NULL,' + + 't13 BIGINT UNSIGNED NULL,' + + 't14 FLOAT NULL,' + + 't15 DOUBLE NULL,' + + 't16 DECIMAL NULL,' + + 't17 DATE NULL,' + + 't18 DATETIME NULL,' + + 't19 TIMESTAMP NULL,' + + 't20 TIME NULL,' + + 't21 YEAR NULL,' + + 't22 CHAR(1) NULL,' + + 't23 CHAR(1) binary NULL,' + + 't24 VARCHAR(1) NULL,' + + 't25 VARCHAR(10) BINARY NULL,' + + 't26 BINARY(10) NULL,' + + 't27 VARBINARY(10) NULL)' + ); + }) + .then(() => { + return shareConn.query('INSERT INTO nullMappingTable values ()'); + }) + .then(() => { + if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6)) { + //MySQL 5.5 doesn't permit DATETIME/TIMESTAMP with microseconds done(); - }) - .catch(done); - } + } else { + //MySQL 5.6 delete YEAR(2) type + if (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 6)) { + initValue[23] = 2018; + nullValue[23] = 1999; + } + shareConn + .query( + 'CREATE TABLE mappingTable(' + + 't1 BIT(1),' + + 't2 BIT(2),' + + 't3 TINYINT,' + + 't4 TINYINT UNSIGNED,' + + 't5 BOOL default 1,' + + 't6 SMALLINT default 1,' + + 't7 SMALLINT UNSIGNED default 0,' + + 't8 MEDIUMINT default 1,' + + 't9 MEDIUMINT UNSIGNED default 0,' + + 't10 INT default 1,' + + 't11 INT UNSIGNED default 0,' + + 't12 BIGINT default 1,' + + 't13 BIGINT UNSIGNED default 0,' + + 't14 FLOAT default 0,' + + 't15 DOUBLE default 1,' + + 't16 DECIMAL default 0,' + + 't17 DECIMAL(15,4) default 0,' + + "t18 DATE default '2001-01-01'," + + "t19 DATETIME(6) default '2001-01-01 00:00:00'," + + "t20 TIMESTAMP(6) default '2001-01-01 00:00:00'," + + "t21 TIMESTAMP(0) null default '2001-01-01 00:00:00'," + + 't22 TIMESTAMP null, ' + + "t23 TIME(6) default '22:11:00.560001'," + + (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 6) + ? 't24 YEAR(4) default 99,' + : 't24 YEAR(2) default 99,') + + 't25 YEAR(4) default 2011,' + + "t26 CHAR(1) default '0'," + + "t27 CHAR(1) binary default '0'," + + "t28 VARCHAR(1) default '1'," + + 't29 VARCHAR(10) BINARY default 0x5a,' + + 't30 BINARY(10) default 0x1,' + + 't31 VARBINARY(10) default 0x1' + + ')' + ) + .then(() => { + return shareConn.query( + 'INSERT INTO mappingTable VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', + initValue + ); + }) + .then(() => { + return shareConn.query('INSERT INTO mappingTable VALUES ()'); + }) + .then(() => { + done(); + }) + .catch(done); + } + }); }); it('query mapping field', function (done) { @@ -206,12 +217,19 @@ describe('mapping', () => { }); it('dataType with null', (done) => { - shareConn.query( - 'CREATE TEMPORARY TABLE dataTypeWithNull (id int not null primary key auto_increment, test longblob, test2 blob, test3 text)' - ); - shareConn.query("insert into dataTypeWithNull values(null, 'a','b','c')"); shareConn - .query('SELECT * FROM dataTypeWithNull') + .query('DROP TABLE IF EXISTS dataTypeWithNull') + .then(() => { + return shareConn.query( + 'CREATE TABLE dataTypeWithNull (id int not null primary key auto_increment, test longblob, test2 blob, test3 text)' + ); + }) + .then(() => { + return shareConn.query("insert into dataTypeWithNull values(null, 'a','b','c')"); + }) + .then(() => { + return shareConn.query('SELECT * FROM dataTypeWithNull'); + }) .then((rows) => { assert.ok(Buffer.isBuffer(rows[0].test)); assert.ok(Buffer.isBuffer(rows[0].test2)); diff --git a/test/integration/datatype/test-set.js b/test/integration/datatype/test-set.js index ee5f45b6..11a1beee 100644 --- a/test/integration/datatype/test-set.js +++ b/test/integration/datatype/test-set.js @@ -5,21 +5,26 @@ const { assert } = require('chai'); describe('set', () => { it('set array', (done) => { - shareConn.query("CREATE TEMPORARY TABLE set_array(tt SET('v1','v2', 'v3'))"); - - shareConn.query( - 'INSERT INTO set_array values ' + - "('v1'), " + - "('v2'), " + - "('v1,v2'), " + - "('v3'), " + - "('v3,v2'), " + - "('')," + - '(null)' - ); - shareConn - .query('SELECT * from set_array') + .query('DROP TABLE IF EXISTS set_array') + .then(() => { + return shareConn.query("CREATE TABLE set_array(tt SET('v1','v2', 'v3'))"); + }) + .then(() => { + return shareConn.query( + 'INSERT INTO set_array values ' + + "('v1'), " + + "('v2'), " + + "('v1,v2'), " + + "('v3'), " + + "('v3,v2'), " + + "('')," + + '(null)' + ); + }) + .then(() => { + return shareConn.query('SELECT * from set_array'); + }) .then((rows) => { assert.deepEqual(rows, [ { tt: ['v1'] }, diff --git a/test/integration/datatype/test-string.js b/test/integration/datatype/test-string.js index 5d765360..0d2dda74 100644 --- a/test/integration/datatype/test-string.js +++ b/test/integration/datatype/test-string.js @@ -27,13 +27,20 @@ describe('string', () => { it('utf8 buffer verification', function (done) { if (!base.utf8Collation()) this.skip(); - shareConn.query( - 'CREATE TEMPORARY TABLE buf_utf8_chars(tt text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci)' - ); const buf = Buffer.from([0xf0, 0x9f, 0xa4, 0x98, 0xf0, 0x9f, 0x92, 0xaa]); // 🤘💪 - shareConn.query('INSERT INTO buf_utf8_chars VALUES (?)', buf); shareConn - .query("SELECT _binary'🤘💪' t1, '🤘💪' t2, tt FROM buf_utf8_chars") + .query('DROP TABLE IF EXISTS buf_utf8_chars') + .then(() => { + return shareConn.query( + 'CREATE TABLE buf_utf8_chars(tt text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci)' + ); + }) + .then(() => { + return shareConn.query('INSERT INTO buf_utf8_chars VALUES (?)', buf); + }) + .then(() => { + return shareConn.query("SELECT _binary'🤘💪' t1, '🤘💪' t2, tt FROM buf_utf8_chars"); + }) .then((results) => { assert.equal(results[0].t1, '🤘💪'); assert.equal(results[0].t2, '🤘💪'); @@ -55,27 +62,31 @@ describe('string', () => { it('utf8 strings', function (done) { if (!base.utf8Collation()) this.skip(); - - shareConn.query( - 'CREATE TEMPORARY TABLE buf_utf8_string(tt text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci)' - ); - - //F0 9F 98 8E 😎 unicode 6 smiling face with sunglasses - //F0 9F 8C B6 🌶 unicode 7 hot pepper - //F0 9F 8E A4 🎤 unicode 8 no microphones - //F0 9F A5 82 🥂 unicode 9 champagne glass - - shareConn.query( - 'INSERT INTO buf_utf8_string values ' + - "('hel\\'lo'), " + - "('您好 (chinese)'), " + - "('नमस्ते (Hindi)'), " + - "('привет (Russian)'), " + - "('😎🌶🎤🥂')" - ); - shareConn - .query('SELECT * from buf_utf8_string') + .query('DROP TABLE IF EXISTS buf_utf8_string') + .then(() => { + return shareConn.query( + 'CREATE TABLE buf_utf8_string(tt text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci)' + ); + }) + .then(() => { + //F0 9F 98 8E 😎 unicode 6 smiling face with sunglasses + //F0 9F 8C B6 🌶 unicode 7 hot pepper + //F0 9F 8E A4 🎤 unicode 8 no microphones + //F0 9F A5 82 🥂 unicode 9 champagne glass + + return shareConn.query( + 'INSERT INTO buf_utf8_string values ' + + "('hel\\'lo'), " + + "('您好 (chinese)'), " + + "('नमस्ते (Hindi)'), " + + "('привет (Russian)'), " + + "('😎🌶🎤🥂')" + ); + }) + .then(() => { + return shareConn.query('SELECT * from buf_utf8_string'); + }) .then((rows) => { checkUtf8String(rows); done(); @@ -114,12 +125,26 @@ describe('string', () => { it('table encoding not affecting query', function (done) { if (!base.utf8Collation()) this.skip(); const str = '財團法人資訊工業策進會'; - shareConn.query('CREATE TEMPORARY TABLE utf8_encoding_table(t1 text) CHARSET utf8'); - shareConn.query('CREATE TEMPORARY TABLE big5_encoding_table(t2 text) CHARSET big5'); - shareConn.query('INSERT INTO utf8_encoding_table values (?)', [str]); - shareConn.query('INSERT INTO big5_encoding_table values (?)', [str]); shareConn - .query('SELECT * from utf8_encoding_table, big5_encoding_table') + .query('DROP TABLE IF EXISTS utf8_encoding_table') + .then(() => { + return shareConn.query('DROP TABLE IF EXISTS big5_encoding_table'); + }) + .then(() => { + return shareConn.query('CREATE TABLE utf8_encoding_table(t1 text) CHARSET utf8'); + }) + .then(() => { + return shareConn.query('CREATE TABLE big5_encoding_table(t2 text) CHARSET big5'); + }) + .then(() => { + return shareConn.query('INSERT INTO utf8_encoding_table values (?)', [str]); + }) + .then(() => { + return shareConn.query('INSERT INTO big5_encoding_table values (?)', [str]); + }) + .then(() => { + return shareConn.query('SELECT * from utf8_encoding_table, big5_encoding_table'); + }) .then((res) => { assert.deepEqual(res, [{ t1: str, t2: str }]); done(); @@ -128,10 +153,17 @@ describe('string', () => { }); it('string escape', (done) => { - shareConn.query('CREATE TEMPORARY TABLE escape_utf8_string(tt text) CHARSET utf8'); - shareConn.query('INSERT INTO escape_utf8_string values (?)', ['a \'b\\"c']); shareConn - .query('SELECT * from escape_utf8_string') + .query('DROP TABLE IF EXISTS escape_utf8_string') + .then(() => { + return shareConn.query('CREATE TABLE escape_utf8_string(tt text) CHARSET utf8'); + }) + .then(() => { + return shareConn.query('INSERT INTO escape_utf8_string values (?)', ['a \'b\\"c']); + }) + .then(() => { + return shareConn.query('SELECT * from escape_utf8_string'); + }) .then((res) => { assert.deepEqual(res, [{ tt: 'a \'b\\"c' }]); done(); @@ -144,10 +176,17 @@ describe('string', () => { const wrongString = 'a\ue800\ud800b\udc01c\ud800'; base.createConnection().then((conn) => { - conn.query('CREATE TEMPORARY TABLE wrong_utf8_string(tt text) CHARSET utf8mb4'); - conn.query('INSERT INTO wrong_utf8_string values (?)', [wrongString]); conn - .query('SELECT * from wrong_utf8_string') + .query('DROP TABLE IF EXISTS wrong_utf8_string') + .then(() => { + return conn.query('CREATE TABLE wrong_utf8_string(tt text) CHARSET utf8mb4'); + }) + .then(() => { + return conn.query('INSERT INTO wrong_utf8_string values (?)', [wrongString]); + }) + .then(() => { + return conn.query('SELECT * from wrong_utf8_string'); + }) .then((res) => { assert.deepEqual(res, [{ tt: 'a?b?c?' }]); conn.end(); diff --git a/test/integration/test-additional-server-info.js b/test/integration/test-additional-server-info.js index cb0a159c..ce76adbd 100644 --- a/test/integration/test-additional-server-info.js +++ b/test/integration/test-additional-server-info.js @@ -9,8 +9,8 @@ describe('server additional information API', () => { .query('SELECT VERSION() a') .then((res) => { if (process.env.MAXSCALE_VERSION) { - //maxscale version is set to 10.4.99-MariaDB-maxScale - assert.deepEqual(shareConn.serverVersion(), '10.4.99-MariaDB-maxScale'); + //maxscale version is set to 10.5.99-MariaDB-maxScale + assert.deepEqual(shareConn.serverVersion(), '10.5.99-MariaDB-maxScale'); } else { assert.deepEqual(res, [{ a: shareConn.serverVersion() }]); } diff --git a/test/integration/test-auth-plugin.js b/test/integration/test-auth-plugin.js index e6726d24..8d77f9f7 100644 --- a/test/integration/test-auth-plugin.js +++ b/test/integration/test-auth-plugin.js @@ -5,8 +5,66 @@ const { assert } = require('chai'); const Conf = require('../conf'); describe('authentication plugin', () => { + let rsaPublicKey = process.env.TEST_RSA_PUBLIC_KEY; + let cachingRsaPublicKey = process.env.TEST_CACHING_RSA_PUBLIC_KEY; + + before(async function () { + if (!rsaPublicKey) { + if (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 7, 0)) { + const res = await shareConn.query({ + sql: "SHOW STATUS LIKE 'Rsa_public_key'", + rowsAsArray: true + }); + rsaPublicKey = res[0][1]; + } + } + + if (!cachingRsaPublicKey) { + if (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(8, 0, 0)) { + const res = await shareConn.query({ + sql: "SHOW STATUS LIKE 'Caching_sha2_password_rsa_public_key'", + rowsAsArray: true + }); + cachingRsaPublicKey = res[0][1]; + } + } + + await shareConn.query("DROP USER IF EXISTS 'sha256User'@'%'"); + await shareConn.query("DROP USER IF EXISTS 'cachingSha256User'@'%'"); + await shareConn.query("DROP USER IF EXISTS 'cachingSha256User2'@'%'"); + await shareConn.query("DROP USER IF EXISTS 'cachingSha256User3'@'%'"); + + if (!shareConn.info.isMariaDB()) { + if (shareConn.info.hasMinVersion(8, 0, 0)) { + await shareConn.query( + "CREATE USER 'sha256User'@'%' IDENTIFIED WITH sha256_password BY 'password'" + ); + await shareConn.query("GRANT ALL PRIVILEGES ON *.* TO 'sha256User'@'%'"); + + await shareConn.query( + "CREATE USER 'cachingSha256User'@'%' IDENTIFIED WITH caching_sha2_password BY 'password'" + ); + await shareConn.query("GRANT ALL PRIVILEGES ON *.* TO 'cachingSha256User'@'%'"); + await shareConn.query( + "CREATE USER 'cachingSha256User2'@'%' IDENTIFIED WITH caching_sha2_password BY 'password'" + ); + await shareConn.query("GRANT ALL PRIVILEGES ON *.* TO 'cachingSha256User2'@'%'"); + await shareConn.query( + "CREATE USER 'cachingSha256User3'@'%' IDENTIFIED WITH caching_sha2_password BY 'password'" + ); + await shareConn.query("GRANT ALL PRIVILEGES ON *.* TO 'cachingSha256User3'@'%'"); + } else { + await shareConn.query("CREATE USER 'sha256User'@'%'"); + await shareConn.query( + "GRANT ALL PRIVILEGES ON *.* TO 'sha256User'@'%' IDENTIFIED WITH " + + "sha256_password BY 'password'" + ); + } + } + }); + it('ed25519 authentication plugin', function (done) { - if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); const self = this; if (!shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(10, 1, 22)) this.skip(); @@ -72,6 +130,7 @@ describe('authentication plugin', () => { it('name pipe authentication plugin', function (done) { if (process.platform !== 'win32') this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(10, 1, 11)) this.skip(); if (Conf.baseConfig.host !== 'localhost' && Conf.baseConfig.host !== 'mariadb.example.com') this.skip(); @@ -161,8 +220,8 @@ describe('authentication plugin', () => { }); it('dialog authentication plugin', function (done) { - //pam is set using .travis/entrypoint/pam.sh - if (!process.env.TRAVIS || process.env.MAXSCALE_VERSION) this.skip(); + //pam is set using .travis/sql/pam.sh + if (!process.env.TRAVIS || process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); this.timeout(10000); @@ -191,8 +250,8 @@ describe('authentication plugin', () => { }); it('dialog authentication plugin multiple password', function (done) { - //pam is set using .travis/entrypoint/pam.sh - if (!process.env.TRAVIS || process.env.MAXSCALE_VERSION) this.skip(); + //pam is set using .travis/sql/pam.sh + if (!process.env.TRAVIS || process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); this.timeout(10000); @@ -221,7 +280,7 @@ describe('authentication plugin', () => { }); it('multi authentication plugin', function (done) { - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); if (!shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(10, 4, 3)) this.skip(); shareConn.query("drop user IF EXISTS mysqltest1@'%'").catch((err) => {}); shareConn @@ -275,4 +334,233 @@ describe('authentication plugin', () => { }) .catch(done); }); + + it('sha256 authentication plugin', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); + if (process.platform === 'win32') this.skip(); + if (!rsaPublicKey || shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(5, 7, 0)) + this.skip(); + + const self = this; + base + .createConnection({ + user: 'sha256User', + password: 'password', + rsaPublicKey: rsaPublicKey + }) + .then((conn) => { + conn.end(); + done(); + }) + .catch((err) => { + if (err.message.includes('sha256_password authentication plugin require node 11.6+')) + self.skip(); + done(err); + }); + }); + + it('sha256 authentication plugin with public key retrieval', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); + if (process.platform === 'win32') this.skip(); + if (shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(5, 7, 0)) this.skip(); + + const self = this; + base + .createConnection({ + user: 'sha256User', + password: 'password', + allowPublicKeyRetrieval: true + }) + .then((conn) => { + conn.end(); + done(); + }) + .catch((err) => { + if (err.message.includes('sha256_password authentication plugin require node 11.6+')) + self.skip(); + done(err); + }); + }); + + it('sha256 authentication plugin without public key retrieval', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); + if (shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(5, 7, 0)) this.skip(); + + base + .createConnection({ + user: 'sha256User', + password: 'password' + }) + .then((conn) => { + conn.end(); + done(new Error('must have thrown error')); + }) + .catch((err) => { + assert.isTrue( + err.message.includes('RSA public key is not available client side.') || + err.message.includes('sha256_password authentication plugin require node 11.6+') + ); + done(); + }); + }); + + it('sha256 authentication plugin with ssl', function (done) { + if ( + process.env.MAXSCALE_TEST_DISABLE || + shareConn.info.isMariaDB() || + !shareConn.info.hasMinVersion(5, 7, 0) + ) + this.skip(); + + const self = this; + shareConn + .query("SHOW VARIABLES LIKE 'have_ssl'") + .then((rows) => { + // console.log("ssl is not enable on database, skipping test :"); + if (rows[0].Value === 'YES') { + base + .createConnection({ + user: 'sha256User', + password: 'password', + ssl: { + rejectUnauthorized: false + } + }) + .then((conn) => { + conn.end(); + done(); + }) + .catch((err) => { + if (err.message.includes('sha256_password authentication plugin require node 11.6+')) + self.skip(); + done(err); + }); + } else { + this.skip(); + } + }) + .catch(done); + }); + + it('cachingsha256 authentication plugin', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); + if (process.platform === 'win32') this.skip(); + if (!rsaPublicKey || shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(8, 0, 0)) + this.skip(); + + const self = this; + base + .createConnection({ + user: 'cachingSha256User', + password: 'password', + cachingRsaPublicKey: rsaPublicKey + }) + .then((conn) => { + conn.end(); + //using fast auth + base + .createConnection({ + user: 'cachingSha256User', + password: 'password', + cachingRsaPublicKey: rsaPublicKey + }) + .then((conn) => { + conn.end(); + done(); + }) + .catch(done); + }) + .catch((err) => { + if (err.message.includes('caching_sha2_password authentication plugin require node 11.6+')) + self.skip(); + done(err); + }); + }); + + it('cachingsha256 authentication plugin with public key retrieval', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); + if (process.platform === 'win32') this.skip(); + if (shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(8, 0, 0)) this.skip(); + + const self = this; + base + .createConnection({ + user: 'cachingSha256User2', + password: 'password', + allowPublicKeyRetrieval: true + }) + .then((conn) => { + conn.end(); + done(); + }) + .catch((err) => { + if (err.message.includes('caching_sha2_password authentication plugin require node 11.6+')) + self.skip(); + done(err); + }); + }); + + it('cachingsha256 authentication plugin without public key retrieval', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); + if (shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(8, 0, 0)) this.skip(); + + base + .createConnection({ + user: 'cachingSha256User3', + password: 'password' + }) + .then((conn) => { + conn.end(); + done(new Error('must have thrown error')); + }) + .catch((err) => { + assert.isTrue( + err.message.includes('RSA public key is not available client side.') || + err.message.includes('caching_sha2_password authentication plugin require node 11.6+') + ); + done(); + }); + }); + + it('cachingsha256 authentication plugin with ssl', function (done) { + if ( + process.env.MAXSCALE_TEST_DISABLE || + shareConn.info.isMariaDB() || + !shareConn.info.hasMinVersion(8, 0, 0) + ) + this.skip(); + + const self = this; + shareConn + .query("SHOW VARIABLES LIKE 'have_ssl'") + .then((rows) => { + // console.log("ssl is not enable on database, skipping test :"); + if (rows[0].Value === 'YES') { + base + .createConnection({ + user: 'cachingSha256User3', + password: 'password', + ssl: { + rejectUnauthorized: false + } + }) + .then((conn) => { + conn.end(); + done(); + }) + .catch((err) => { + if ( + err.message.includes( + 'caching_sha2_password authentication plugin require node 11.6+' + ) + ) + self.skip(); + done(); + }); + } else { + self.skip(); + } + }) + .catch(done); + }); }); diff --git a/test/integration/test-batch-callback.js b/test/integration/test-batch-callback.js index 0d66ab02..a2698d27 100644 --- a/test/integration/test-batch-callback.js +++ b/test/integration/test-batch-callback.js @@ -15,9 +15,7 @@ describe('batch callback', () => { let supportBulk; before(function (done) { supportBulk = (Conf.baseConfig.bulk === undefined ? true : Conf.baseConfig.bulk) - ? (shareConn.info.serverCapabilities.high & - Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > - 0 + ? (shareConn.info.serverCapabilities & Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > 0 : false; const hourOffset = Math.round((-1 * new Date().getTimezoneOffset()) / 60); @@ -207,7 +205,7 @@ describe('batch callback', () => { ); conn.query('select 1', (err, rows) => { if (err) return done(err); - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); }); }); }; @@ -274,7 +272,7 @@ describe('batch callback', () => { done(err); }); } - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); }); }); }; @@ -327,7 +325,7 @@ describe('batch callback', () => { done(err); }); } - assert.deepEqual(rows, [{ '2': 2 }]); + assert.deepEqual(rows, [{ 2: 2 }]); }); }); }; @@ -453,7 +451,7 @@ describe('batch callback', () => { done(err); }); } - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); }); }); }; @@ -543,7 +541,7 @@ describe('batch callback', () => { done(err); }); } - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); clearTimeout(timeout); return conn.end(() => { done(); @@ -1098,6 +1096,24 @@ describe('batch callback', () => { describe('standard question mark using rewrite', () => { const useCompression = false; + it('immediate batch after callback', function (done) { + let conn = base.createCallbackConnection(); + conn.batch( + 'INSERT INTO contacts(first_name, last_name, email) VALUES(?, ?, ?)', + ['John', 'Smith', 'js@example.com'], + (err, res, meta) => { + conn.end(); + if (err) { + if (err.message.includes('Parameter at position 1 is not set for values 0')) { + done(); + } else done(err); + } else { + done(new Error('Must have throw error')); + } + } + ); + }); + it('simple batch, local date', function (done) { if (!base.utf8Collation()) this.skip(); this.timeout(30000); @@ -1133,7 +1149,7 @@ describe('batch callback', () => { }) .catch((err) => { assert.isTrue( - err.message.includes('Parameter at position 2 is not set for values 1'), + err.message.includes('Parameter at position 1 is not set for values 1'), err.message ); conn.end(); diff --git a/test/integration/test-batch-geometry-type.js b/test/integration/test-batch-geometry-type.js index cffe68b3..7b76dc73 100644 --- a/test/integration/test-batch-geometry-type.js +++ b/test/integration/test-batch-geometry-type.js @@ -9,54 +9,56 @@ describe('batch geometry type', () => { let supportBulk; before(function () { supportBulk = (Conf.baseConfig.bulk === undefined ? true : Conf.baseConfig.bulk) - ? (shareConn.info.serverCapabilities.high & - Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > - 0 + ? (shareConn.info.serverCapabilities & Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > 0 : false; }); it('Point format', function (done) { if (!shareConn.info.isMariaDB()) this.skip(); - - shareConn.query('CREATE TEMPORARY TABLE gis_point_batch (g POINT)'); shareConn - .batch('INSERT INTO gis_point_batch VALUES (?)', [ - [ - { - type: 'Point', - coordinates: [10, 10] - } - ], - [ - { - type: 'Point', - coordinates: [20, 10] - } - ], - [ - { - type: 'Point', - coordinates: [20, 20] - } - ], - [ - { - type: 'Point', - coordinates: [10, 20] - } - ], - [ - { - type: 'Point', - coordinates: [] - } - ], - [ - { - type: 'Point' - } - ] - ]) + .query('DROP TABLE IF EXISTS gis_point_batch') + .then(() => { + return shareConn.query('CREATE TABLE gis_point_batch (g POINT)'); + }) + .then(() => { + return shareConn.batch('INSERT INTO gis_point_batch VALUES (?)', [ + [ + { + type: 'Point', + coordinates: [10, 10] + } + ], + [ + { + type: 'Point', + coordinates: [20, 10] + } + ], + [ + { + type: 'Point', + coordinates: [20, 20] + } + ], + [ + { + type: 'Point', + coordinates: [10, 20] + } + ], + [ + { + type: 'Point', + coordinates: [] + } + ], + [ + { + type: 'Point' + } + ] + ]); + }) .then(() => { return shareConn.query('SELECT * FROM gis_point_batch'); }) @@ -88,13 +90,17 @@ describe('batch geometry type', () => { }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'Point' } : null }, { g: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'Point' } : null } @@ -106,37 +112,42 @@ describe('batch geometry type', () => { it('LineString insert', function (done) { if (!shareConn.info.isMariaDB()) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_line_batch (g LINESTRING)'); shareConn - .batch('INSERT INTO gis_line_batch VALUES (?)', [ - [ - { - type: 'LineString', - coordinates: [ - [0, 0], - [0, 10], - [10, 0] - ] - } - ], - [ - { - type: 'LineString', - coordinates: [[0, 10]] - } - ], - [ - { - type: 'LineString', - coordinates: [] - } - ], - [ - { - type: 'LineString' - } - ] - ]) + .query('DROP TABLE IF EXISTS gis_line_batch') + .then(() => { + return shareConn.query('CREATE TABLE gis_line_batch (g LINESTRING)'); + }) + .then(() => { + return shareConn.batch('INSERT INTO gis_line_batch VALUES (?)', [ + [ + { + type: 'LineString', + coordinates: [ + [0, 0], + [0, 10], + [10, 0] + ] + } + ], + [ + { + type: 'LineString', + coordinates: [[0, 10]] + } + ], + [ + { + type: 'LineString', + coordinates: [] + } + ], + [ + { + type: 'LineString' + } + ] + ]); + }) .then(() => { return shareConn.query('SELECT * FROM gis_line_batch'); }) @@ -165,12 +176,15 @@ describe('batch geometry type', () => { coordinates: [], type: 'LineString' } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'LineString' } : null }, { - g: shareConn.info.hasMinVersion(10, 5, 2) ? { type: 'LineString' } : null + g: + shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE + ? { type: 'LineString' } + : null } ]); } else { @@ -206,68 +220,73 @@ describe('batch geometry type', () => { it('Polygon insert', function (done) { if (!shareConn.info.isMariaDB()) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_polygon_batch (g POLYGON)'); shareConn - .batch('INSERT INTO gis_polygon_batch VALUES (?)', [ - [ - { - type: 'Polygon', - coordinates: [ - [ - [10, 10], - [20, 10], - [20, 20], - [10, 20], - [10, 10] + .query('DROP TABLE IF EXISTS gis_polygon_batch') + .then(() => { + return shareConn.query('CREATE TABLE gis_polygon_batch (g POLYGON)'); + }) + .then(() => { + return shareConn.batch('INSERT INTO gis_polygon_batch VALUES (?)', [ + [ + { + type: 'Polygon', + coordinates: [ + [ + [10, 10], + [20, 10], + [20, 20], + [10, 20], + [10, 10] + ] ] - ] - } - ], - [ - { - type: 'Polygon', - coordinates: [ - [ - [0, 0], - [50, 0], - [50, 50], - [0, 50], - [0, 0] - ], - [ - [10, 10], - [20, 10], - [20, 20], - [10, 20], - [10, 10] + } + ], + [ + { + type: 'Polygon', + coordinates: [ + [ + [0, 0], + [50, 0], + [50, 50], + [0, 50], + [0, 0] + ], + [ + [10, 10], + [20, 10], + [20, 20], + [10, 20], + [10, 10] + ] ] - ] - } - ], - [ - { - type: 'Polygon', - coordinates: [ - [[0, 0], [50]], - [ - [10, 10], - [20, 10] + } + ], + [ + { + type: 'Polygon', + coordinates: [ + [[0, 0], [50]], + [ + [10, 10], + [20, 10] + ] ] - ] - } - ], - [ - { - type: 'Polygon', - coordinates: [] - } - ], - [ - { - type: 'Polygon' - } - ] - ]) + } + ], + [ + { + type: 'Polygon', + coordinates: [] + } + ], + [ + { + type: 'Polygon' + } + ] + ]); + }) .then(() => { return shareConn.query('SELECT * FROM gis_polygon_batch'); }) @@ -310,7 +329,10 @@ describe('batch geometry type', () => { } }, { - g: shareConn.info.hasMinVersion(10, 5, 2) ? { type: 'Polygon' } : null + g: + shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE + ? { type: 'Polygon' } + : null }, { g: supportBulk @@ -318,12 +340,15 @@ describe('batch geometry type', () => { type: 'Polygon', coordinates: [] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'Polygon' } : null }, { - g: shareConn.info.hasMinVersion(10, 5, 2) ? { type: 'Polygon' } : null + g: + shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE + ? { type: 'Polygon' } + : null } ]); } else { @@ -381,24 +406,29 @@ describe('batch geometry type', () => { it('MultiPoint insert', function (done) { if (!shareConn.info.isMariaDB()) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_multi_point_batch (g MULTIPOINT)'); shareConn - .batch('INSERT INTO gis_multi_point_batch VALUES (?)', [ - [ - { - type: 'MultiPoint', - coordinates: [ - [30, 30], - [10, 10], - [10, 20], - [20, 20] - ] - } - ], - [{ type: 'MultiPoint', coordinates: [[10, 0]] }], - [{ type: 'MultiPoint', coordinates: [] }], - [{ type: 'MultiPoint' }] - ]) + .query('DROP TABLE IF EXISTS gis_multi_point_batch') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_point_batch (g MULTIPOINT)'); + }) + .then(() => { + return shareConn.batch('INSERT INTO gis_multi_point_batch VALUES (?)', [ + [ + { + type: 'MultiPoint', + coordinates: [ + [30, 30], + [10, 10], + [10, 20], + [20, 20] + ] + } + ], + [{ type: 'MultiPoint', coordinates: [[10, 0]] }], + [{ type: 'MultiPoint', coordinates: [] }], + [{ type: 'MultiPoint' }] + ]); + }) .then(() => { return shareConn.query('SELECT * FROM gis_multi_point_batch'); }) @@ -428,7 +458,7 @@ describe('batch geometry type', () => { type: 'MultiPoint', coordinates: [] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPoint' } : null }, @@ -438,7 +468,7 @@ describe('batch geometry type', () => { type: 'MultiPoint', coordinates: [] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPoint' } : null } @@ -477,42 +507,48 @@ describe('batch geometry type', () => { it('Multi-line insert', function (done) { if (!shareConn.info.isMariaDB()) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE gis_multi_line_batch (g MULTILINESTRING)'); shareConn - .batch('INSERT INTO gis_multi_line_batch VALUES (?)', [ - [ - { - type: 'MultiLineString', - coordinates: [ - [ - [10, 48], - [10, 21], - [10, 0] - ], - [ - [16, 0], - [16, 23], - [16, 48] + .query('DROP TABLE IF EXISTS gis_multi_line_batch') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_line_batch (g MULTILINESTRING)'); + }) + .then(() => { + return shareConn.batch('INSERT INTO gis_multi_line_batch VALUES (?)', [ + [ + { + type: 'MultiLineString', + coordinates: [ + [ + [10, 48], + [10, 21], + [10, 0] + ], + [ + [16, 0], + [16, 23], + [16, 48] + ] ] - ] - } - ], - [ - { - type: 'MultiLineString', - coordinates: [ - [ - [10, 48], - [10, 21], - [10, 0] + } + ], + [ + { + type: 'MultiLineString', + coordinates: [ + [ + [10, 48], + [10, 21], + [10, 0] + ] ] - ] - } - ], - [{ type: 'MultiLineString', coordinates: [[]] }], - [{ type: 'MultiLineString', coordinates: [] }], - [{ type: 'MultiLineString' }] - ]) + } + ], + [{ type: 'MultiLineString', coordinates: [[]] }], + [{ type: 'MultiLineString', coordinates: [] }], + [{ type: 'MultiLineString' }] + ]); + }) + .then(() => { return shareConn.query('SELECT * FROM gis_multi_line_batch'); }) @@ -554,7 +590,7 @@ describe('batch geometry type', () => { type: 'MultiLineString', coordinates: [[]] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiLineString' } : null }, @@ -564,7 +600,7 @@ describe('batch geometry type', () => { type: 'MultiLineString', coordinates: [] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiLineString' } : null }, @@ -574,7 +610,7 @@ describe('batch geometry type', () => { type: 'MultiLineString', coordinates: [] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiLineString' } : null } @@ -628,89 +664,93 @@ describe('batch geometry type', () => { it('Multi-polygon insert', function (done) { if (!shareConn.info.isMariaDB()) this.skip(); - - shareConn.query('CREATE TEMPORARY TABLE gis_multi_polygon_batch (g MULTIPOLYGON)'); shareConn - .batch('INSERT INTO gis_multi_polygon_batch VALUES (?)', [ - [ - { - type: 'MultiPolygon', - coordinates: [ - [ + .query('DROP TABLE IF EXISTS gis_multi_polygon_batch') + .then(() => { + return shareConn.query('CREATE TABLE gis_multi_polygon_batch (g MULTIPOLYGON)'); + }) + .then(() => { + return shareConn.batch('INSERT INTO gis_multi_polygon_batch VALUES (?)', [ + [ + { + type: 'MultiPolygon', + coordinates: [ [ - [28, 26], - [28, 0], - [84, 0], - [84, 42], - [28, 26] + [ + [28, 26], + [28, 0], + [84, 0], + [84, 42], + [28, 26] + ], + [ + [52, 18], + [66, 23], + [73, 9], + [48, 6], + [52, 18] + ] ], [ - [52, 18], - [66, 23], - [73, 9], - [48, 6], - [52, 18] - ] - ], - [ - [ - [59, 18], - [67, 18], - [67, 13], - [59, 13], - [59, 18] + [ + [59, 18], + [67, 18], + [67, 13], + [59, 13], + [59, 18] + ] ] ] - ] - } - ], - [ - { - type: 'MultiPolygon', - coordinates: [ - [ - [ - [28, 26], - [28, 0], - [84, 0], - [84, 42], - [28, 26] - ], + } + ], + [ + { + type: 'MultiPolygon', + coordinates: [ [ - [52, 18], - [66, 23], - [73, 9], - [48, 6], - [52, 18] + [ + [28, 26], + [28, 0], + [84, 0], + [84, 42], + [28, 26] + ], + [ + [52, 18], + [66, 23], + [73, 9], + [48, 6], + [52, 18] + ] ] ] - ] - } - ], - [ - { - type: 'MultiPolygon', - coordinates: [[[]]] - } - ], - [ - { - type: 'MultiPolygon', - coordinates: [[]] - } - ], - [ - { - type: 'MultiPolygon', - coordinates: [] - } - ], - [ - { - type: 'MultiPolygon' - } - ] - ]) + } + ], + [ + { + type: 'MultiPolygon', + coordinates: [[[]]] + } + ], + [ + { + type: 'MultiPolygon', + coordinates: [[]] + } + ], + [ + { + type: 'MultiPolygon', + coordinates: [] + } + ], + [ + { + type: 'MultiPolygon' + } + ] + ]); + }) .then(() => { return shareConn.query('SELECT * FROM gis_multi_polygon_batch'); }) @@ -778,7 +818,7 @@ describe('batch geometry type', () => { type: 'MultiPolygon', coordinates: [[[]]] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPolygon' } : null }, @@ -788,7 +828,7 @@ describe('batch geometry type', () => { type: 'MultiPolygon', coordinates: [[]] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPolygon' } : null }, @@ -798,7 +838,7 @@ describe('batch geometry type', () => { type: 'MultiPolygon', coordinates: [] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPolygon' } : null }, @@ -808,7 +848,7 @@ describe('batch geometry type', () => { type: 'MultiPolygon', coordinates: [] } - : shareConn.info.hasMinVersion(10, 5, 2) + : shareConn.info.hasMinVersion(10, 5, 2) && !process.env.MAXSCALE_TEST_DISABLE ? { type: 'MultiPolygon' } : null } @@ -895,111 +935,116 @@ describe('batch geometry type', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE gis_geometrycollection_batch (g GEOMETRYCOLLECTION)'); conn - .batch('INSERT INTO gis_geometrycollection_batch VALUES (?)', [ - [ - { - type: 'GeometryCollection', - geometries: [ - { - type: 'Point', - coordinates: [10, 10] - }, - { - type: 'LineString', - coordinates: [ - [0, 0], - [0, 10], - [10, 0] - ] - }, - { - type: 'MultiPoint', - coordinates: [ - [0, 0], - [10, 10], - [10, 20], - [20, 20] - ] - }, - { - type: 'MultiLineString', - coordinates: [ - [ - [10, 48], - [10, 21], + .query('DROP TABLE IF EXISTS gis_geometrycollection_batch') + .then(() => { + return conn.query('CREATE TABLE gis_geometrycollection_batch (g GEOMETRYCOLLECTION)'); + }) + .then(() => { + return conn.batch('INSERT INTO gis_geometrycollection_batch VALUES (?)', [ + [ + { + type: 'GeometryCollection', + geometries: [ + { + type: 'Point', + coordinates: [10, 10] + }, + { + type: 'LineString', + coordinates: [ + [0, 0], + [0, 10], [10, 0] - ], - [ - [16, 0], - [16, 23], - [16, 48] ] - ] - }, - { - type: 'MultiPolygon', - coordinates: [ - [ + }, + { + type: 'MultiPoint', + coordinates: [ + [0, 0], + [10, 10], + [10, 20], + [20, 20] + ] + }, + { + type: 'MultiLineString', + coordinates: [ [ - [28, 26], - [28, 0], - [84, 0], - [84, 42], - [28, 26] + [10, 48], + [10, 21], + [10, 0] ], [ - [52, 18], - [66, 23], - [73, 9], - [48, 6], - [52, 18] + [16, 0], + [16, 23], + [16, 48] ] - ], - [ + ] + }, + { + type: 'MultiPolygon', + coordinates: [ [ - [59, 18], - [67, 18], - [67, 13], - [59, 13], - [59, 18] + [ + [28, 26], + [28, 0], + [84, 0], + [84, 42], + [28, 26] + ], + [ + [52, 18], + [66, 23], + [73, 9], + [48, 6], + [52, 18] + ] + ], + [ + [ + [59, 18], + [67, 18], + [67, 13], + [59, 13], + [59, 18] + ] ] ] - ] - } - ] - } - ], - [ - { - type: 'GeometryCollection', - geometries: [ - { - type: 'Point', - coordinates: [10, 20] - } - ] - } - ], - [ - { - type: 'GeometryCollection', - geometries: [{}] - } - ], - [ - { - type: 'GeometryCollection', - geometries: [] - } - ], - [ - { - type: 'GeometryCollection' - } - ] - ]) + } + ] + } + ], + [ + { + type: 'GeometryCollection', + geometries: [ + { + type: 'Point', + coordinates: [10, 20] + } + ] + } + ], + [ + { + type: 'GeometryCollection', + geometries: [{}] + } + ], + [ + { + type: 'GeometryCollection', + geometries: [] + } + ], + [ + { + type: 'GeometryCollection' + } + ] + ]); + }) .then(() => { return conn.query('SELECT * FROM gis_geometrycollection_batch'); }) diff --git a/test/integration/test-batch.js b/test/integration/test-batch.js index f3429062..19d4b999 100644 --- a/test/integration/test-batch.js +++ b/test/integration/test-batch.js @@ -20,9 +20,7 @@ describe('batch', () => { before(function (done) { timezoneParam = 'America/New_York'; supportBulk = (Conf.baseConfig.bulk === undefined ? true : Conf.baseConfig.bulk) - ? (shareConn.info.serverCapabilities.high & - Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > - 0 + ? (shareConn.info.serverCapabilities & Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > 0 : false; shareConn .query('SELECT @@max_allowed_packet as t') @@ -74,133 +72,133 @@ describe('batch', () => { console.log(conn.info.getLastPackets()); }, 25000); - 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' - ); - const f = {}; - f.toSqlString = () => { - return 'blabla'; - }; 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'), + .query('DROP TABLE IF EXISTS simpleBatch') + .then(() => { + return 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' + ); + }) + .then(() => { + const f = {}; + f.toSqlString = () => { + return 'blabla'; + }; + return 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] + } + ] + ]); + }) + .then((res) => { + assert.equal(res.affectedRows, 4); + return conn.query('select * from `simpleBatch`'); + }) + .then((res) => { + assert.deepEqual(res, [ { - 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'), + 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 + }, { - type: 'Point', - coordinates: [10, 10] - } - ], - [ - false, - { name: 'jack\u000Aमस्', val: 'tt' }, - null, - new Date('2018-01-21 11:30:20.123456+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 + }, { - 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'), + 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 + }, { - type: 'Point', - coordinates: [20, 20] + 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 } - ] - ]) + ]); + return conn.query('DROP TABLE simpleBatch'); + }) .then((res) => { - assert.equal(res.affectedRows, 4); - conn - .query('select * from `simpleBatch`') - .then((res) => { - assert.deepEqual(res, [ - { - 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 - } - ]); - conn - .query('DROP TABLE simpleBatch') - .then((res) => { - clearTimeout(timeout); - conn.end(); - done(); - }) - .catch(done); - }) - .catch((err) => { - done(err); - }); - }); - conn + clearTimeout(timeout); + conn.end(); + done(); + }) + .catch(done); + + return conn .query('select 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); }) .catch(done); }) @@ -215,55 +213,55 @@ describe('batch', () => { console.log(conn.info.getLastPackets()); }, 25000); - conn.query('DROP TABLE IF EXISTS simpleBatchWithOptions'); - conn.query('CREATE TABLE simpleBatchWithOptions(id int, d datetime)'); - const f = {}; - f.toSqlString = () => { - return 'blabla'; - }; conn - .batch( - { - sql: 'INSERT INTO `simpleBatchWithOptions` values (?, ?)', - maxAllowedPacket: 1048576 - }, - [ - [1, new Date('2001-12-31 23:59:58')], - [2, new Date('2001-12-31 23:59:58')] - ] - ) + .query('DROP TABLE IF EXISTS simpleBatchWithOptions') + .then(() => { + return conn.query('CREATE TABLE simpleBatchWithOptions(id int, d datetime)'); + }) + .then(() => { + const f = {}; + f.toSqlString = () => { + return 'blabla'; + }; + return conn.batch( + { + sql: 'INSERT INTO `simpleBatchWithOptions` values (?, ?)', + maxAllowedPacket: 1048576 + }, + [ + [1, new Date('2001-12-31 23:59:58')], + [2, new Date('2001-12-31 23:59:58')] + ] + ); + }) .then((res) => { assert.equal(res.affectedRows, 2); - conn - .query('select * from `simpleBatchWithOptions`') - .then((res) => { - assert.deepEqual(res, [ - { - id: 1, - d: new Date('2001-12-31 23:59:58') - }, - { - id: 2, - d: new Date('2001-12-31 23:59:58') - } - ]); - conn - .query('DROP TABLE simpleBatchWithOptions') - .then((res) => { - clearTimeout(timeout); - conn.end(); - done(); - }) - .catch(done); - }) - .catch((err) => { - done(err); - }); - }); - conn + return conn.query('select * from `simpleBatchWithOptions`'); + }) + .then((res) => { + assert.deepEqual(res, [ + { + id: 1, + d: new Date('2001-12-31 23:59:58') + }, + { + id: 2, + d: new Date('2001-12-31 23:59:58') + } + ]); + return conn.query('DROP TABLE simpleBatchWithOptions'); + }) + .then((res) => { + clearTimeout(timeout); + conn.end(); + done(); + }) + .catch(done); + + return conn .query('select 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); }) .catch(done); }) @@ -282,39 +280,41 @@ describe('batch', () => { console.log(conn.info.getLastPackets()); }, 25000); - conn.query('DROP TABLE IF EXISTS simpleBatchCP1251'); - conn.query('CREATE TABLE simpleBatchCP1251(t varchar(128), id int) CHARSET utf8mb4'); conn - .batch('INSERT INTO `simpleBatchCP1251` values (?, ?)', [ - ['john', 2], - ['©°', 3] - ]) + .query('DROP TABLE IF EXISTS simpleBatchCP1251') + .then(() => { + return conn.query( + 'CREATE TABLE simpleBatchCP1251(t varchar(128), id int) CHARSET utf8mb4' + ); + }) + .then(() => { + return conn.batch('INSERT INTO `simpleBatchCP1251` values (?, ?)', [ + ['john', 2], + ['©°', 3] + ]); + }) .then((res) => { assert.equal(res.affectedRows, 2); - conn - .query('select * from `simpleBatchCP1251`') - .then((res) => { - assert.deepEqual(res, [ - { id: 2, t: 'john' }, - { id: 3, t: '©°' } - ]); - conn - .query('DROP TABLE simpleBatchCP1251') - .then((res) => { - clearTimeout(timeout); - conn.end(); - done(); - }) - .catch(done); - }) - .catch((err) => { - done(err); - }); - }); - conn - .query('select 2') + return conn.query('select * from `simpleBatchCP1251`'); + }) + .then((res) => { + assert.deepEqual(res, [ + { id: 2, t: 'john' }, + { id: 3, t: '©°' } + ]); + return conn.query('DROP TABLE simpleBatchCP1251'); + }) + .then((res) => { + clearTimeout(timeout); + conn.end(); + done(); + }) + .catch(done); + + return conn + .query('select 1') .then((rows) => { - assert.deepEqual(rows, [{ '2': 2 }]); + assert.deepEqual(rows, [{ 1: 1 }]); }) .catch(done); }) @@ -461,7 +461,7 @@ describe('batch', () => { conn .query('select 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); }) .catch(done); }) @@ -655,7 +655,7 @@ describe('batch', () => { conn .query('select 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); clearTimeout(timeout); conn.end(); done(); @@ -885,7 +885,7 @@ describe('batch', () => { conn .query('select 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); clearTimeout(timeout); done(); @@ -1614,6 +1614,38 @@ describe('batch', () => { }); }); + it('rewrite split for maxAllowedPacket', function (done) { + const t = makeid(100); + base + .createConnection({ bulk: false, maxAllowedPacket: 150 }) + .then((conn) => { + conn + .query('DROP TABLE IF EXISTS my_table') + .then(() => { + return conn.query('CREATE TABLE my_table(id int, val LONGTEXT)'); + }) + .then(() => { + return conn.batch('INSERT INTO my_table(id,val) VALUES( ?, ?) ', [ + [1, t], + [2, t] + ]); + }) + .then((res) => { + return conn.query('SELECT * FROM my_table'); + }) + .then((res) => { + assert.deepEqual(res, [ + { id: 1, val: t }, + { id: 2, val: t } + ]); + conn.end(); + done(); + }) + .catch(done); + }) + .catch(done); + }); + it('batch with erroneous parameter', function (done) { if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); base.createConnection({ compress: useCompression, bulk: false }).then((conn) => { @@ -1625,7 +1657,7 @@ describe('batch', () => { }) .catch((err) => { assert.isTrue( - err.message.includes('Parameter at position 2 is not set for values 1'), + err.message.includes('Parameter at position 1 is not set for values 1'), err.message ); conn.end(); @@ -2034,3 +2066,13 @@ describe('batch', () => { }); }); }); + +function makeid(length) { + var result = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} diff --git a/test/integration/test-big-query.js b/test/integration/test-big-query.js index 9c5fca4c..493a85ee 100644 --- a/test/integration/test-big-query.js +++ b/test/integration/test-big-query.js @@ -26,9 +26,14 @@ describe('Big query', function () { it('parameter bigger than 16M packet size', function (done) { if (maxAllowedSize <= testSize) this.skip(); this.timeout(20000); //can take some time - shareConn.query('CREATE TEMPORARY TABLE bigParameterBigParam (b longblob)'); shareConn - .query('insert into bigParameterBigParam(b) values(?)', [buf]) + .query('DROP TABLE IF EXISTS bigParameterBigParam') + .then(() => { + return shareConn.query('CREATE TABLE bigParameterBigParam (b longblob)'); + }) + .then(() => { + return shareConn.query('insert into bigParameterBigParam(b) values(?)', [buf]); + }) .then(() => { return shareConn.query('SELECT * from bigParameterBigParam'); }) @@ -40,11 +45,16 @@ describe('Big query', function () { }); it('int8 buffer overflow', function (done) { + const buf = Buffer.alloc(979, '0'); base.createConnection({ collation: 'latin1_swedish_ci' }).then((conn) => { - conn.query('CREATE TEMPORARY TABLE bigParameterInt8 (a varchar(1024), b varchar(10))'); - const buf = Buffer.alloc(979, '0'); conn - .query('insert into bigParameterInt8 values(?, ?)', [buf.toString(), 'test']) + .query('DROP TABLE IF EXISTS bigParameterInt8') + .then(() => { + return conn.query('CREATE TABLE bigParameterInt8 (a varchar(1024), b varchar(10))'); + }) + .then(() => { + return conn.query('insert into bigParameterInt8 values(?, ?)', [buf.toString(), 'test']); + }) .then(() => { return conn.query('SELECT * from bigParameterInt8'); }) @@ -84,7 +94,7 @@ describe('Big query', function () { const st = Buffer.alloc(65536, '0').toString(); const st2 = Buffer.alloc(1048576, '0').toString(); const params = [st]; - let sql = 'CREATE TEMPORARY TABLE bigParameter (a0 MEDIUMTEXT '; + let sql = 'CREATE TABLE bigParameter (a0 MEDIUMTEXT '; let sqlInsert = 'insert into bigParameter values (?'; for (let i = 1; i < 10; i++) { sql += ',a' + i + ' MEDIUMTEXT '; @@ -93,9 +103,14 @@ describe('Big query', function () { } sql += ')'; sqlInsert += ')'; - conn.query(sql); conn - .query(sqlInsert, params) + .query('DROP TABLE IF EXISTS bigParameter') + .then(() => { + return conn.query(sql); + }) + .then(() => { + return conn.query(sqlInsert, params); + }) .then(() => { return conn.query('SELECT * from bigParameter'); }) @@ -106,6 +121,9 @@ describe('Big query', function () { conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); } }); diff --git a/test/integration/test-change-user.js b/test/integration/test-change-user.js index 89e818f9..f5ad0d19 100644 --- a/test/integration/test-change-user.js +++ b/test/integration/test-change-user.js @@ -7,35 +7,55 @@ const Conf = require('../conf'); describe('change user', () => { before((done) => { - shareConn.query("DROP USER ChangeUser@'%'").catch((err) => {}); - shareConn.query("DROP USER ChangeUser2@'%'").catch((err) => {}); - shareConn.query('CREATE DATABASE IF NOT EXISTS test'); - shareConn.query("CREATE USER ChangeUser@'%' IDENTIFIED BY 'm1P4ssw0@rd'"); - shareConn.query( - 'GRANT SELECT,EXECUTE ON `' + Conf.baseConfig.database + "`.* TO ChangeUser@'%'" - ); - shareConn.query("CREATE USER ChangeUser2@'%' IDENTIFIED BY 'm1SecondP@rd'"); - shareConn.query( - 'GRANT SELECT,EXECUTE ON `' + - Conf.baseConfig.database + - "`.* TO ChangeUser2@'%' with grant option" - ); - shareConn - .query('FLUSH PRIVILEGES') + Promise.all([ + shareConn.query("DROP USER IF EXISTS ChangeUser@'%'"), + shareConn.query("DROP USER IF EXISTS ChangeUser2@'%'") + ]) + .then(() => { + return shareConn.query('CREATE DATABASE IF NOT EXISTS test'); + }) + .then(() => { + return shareConn.query("CREATE USER ChangeUser@'%' IDENTIFIED BY 'm1P4ssw0@rd'"); + }) + .then(() => { + return shareConn.query( + 'GRANT SELECT,EXECUTE ON `' + Conf.baseConfig.database + "`.* TO ChangeUser@'%'" + ); + }) + .then(() => { + return shareConn.query("CREATE USER ChangeUser2@'%' IDENTIFIED BY 'm1SecondP@rd'"); + }) + .then(() => { + return shareConn.query( + 'GRANT SELECT,EXECUTE ON `' + + Conf.baseConfig.database + + "`.* TO ChangeUser2@'%' with grant option" + ); + }) + .then(() => { + return shareConn.query('FLUSH PRIVILEGES'); + }) .then(() => done()) .catch((err) => done()); }); after((done) => { - shareConn.query("DROP USER ChangeUser@'%'"); - shareConn.query("DROP USER ChangeUser2@'%'"); shareConn - .query('FLUSH PRIVILEGES') - .then(() => done()) + .query("DROP USER IF EXISTS ChangeUser@'%'") + .then(() => { + return shareConn.query("DROP USER IF EXISTS ChangeUser2@'%'"); + }) + .then(() => { + return shareConn.query('FLUSH PRIVILEGES'); + }) + .then(() => { + done(); + }) .catch((err) => done()); }); it('basic change user using callback', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); const conn = base.createCallbackConnection(); conn.connect((err) => { @@ -61,6 +81,7 @@ describe('change user', () => { }); it('wrong charset', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); base.createConnection().then((conn) => { conn @@ -81,6 +102,7 @@ describe('change user', () => { }); it('wrong collation in charset', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); base.createConnection().then((conn) => { conn @@ -96,7 +118,9 @@ describe('change user', () => { .catch(done); }); }); + it('wrong collation', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); base.createConnection().then((conn) => { conn @@ -117,7 +141,7 @@ describe('change user', () => { }); it('basic change user using callback no function', function (done) { - if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); const conn = base.createCallbackConnection(); conn.connect((err) => { @@ -137,7 +161,7 @@ describe('change user', () => { }); it('callback change user without option', function (done) { - if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); const conn = base.createCallbackConnection(); conn.connect((err) => { @@ -157,7 +181,7 @@ describe('change user', () => { }); it('basic change user using promise', function (done) { - if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); base @@ -196,7 +220,7 @@ describe('change user', () => { }); it('basic change user using promise non node.js encoding', function (done) { - if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); base @@ -236,6 +260,7 @@ describe('change user', () => { }); it('change user with collation', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); base .createConnection() @@ -275,6 +300,7 @@ describe('change user', () => { }); it('autocommit state after changing user', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); base .createConnection() @@ -296,7 +322,7 @@ describe('change user', () => { if ( shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 2, 2) && - !process.env.MAXSCALE_VERSION && + !process.env.MAXSCALE_TEST_DISABLE && !process.env.SKYSQL ) { assert.equal(conn.info.database, 'test'); diff --git a/test/integration/test-cluster.js b/test/integration/test-cluster.js index e3747071..9ba54488 100644 --- a/test/integration/test-cluster.js +++ b/test/integration/test-cluster.js @@ -486,7 +486,7 @@ describe('cluster', function () { it('server close connection during query', function (done) { if (process.env.SKYSQL) this.skip(); - if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(10000); const poolCluster = basePromise.createPoolCluster({}); @@ -525,7 +525,7 @@ describe('cluster', function () { }); it('socket close connection during query', function (done) { - if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(10, 1, 2)) this.skip(); this.timeout(10000); const poolCluster = basePromise.createPoolCluster({}); diff --git a/test/integration/test-compression.js b/test/integration/test-compression.js index 765adb22..a778fe2e 100644 --- a/test/integration/test-compression.js +++ b/test/integration/test-compression.js @@ -42,7 +42,7 @@ describe('Compression', function () { conn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); done(); }) .catch(done); @@ -56,13 +56,13 @@ describe('Compression', function () { .query('select 1; DO 1;select 2') .then((rows) => { assert.equal(rows.length, 3); - assert.deepEqual(rows[0], [{ '1': 1 }]); + assert.deepEqual(rows[0], [{ 1: 1 }]); assert.deepEqual(rows[1], { affectedRows: 0, insertId: 0, warningStatus: 0 }); - assert.deepEqual(rows[2], [{ '2': 2 }]); + assert.deepEqual(rows[2], [{ 2: 2 }]); done(); }) .catch(done); @@ -71,9 +71,14 @@ describe('Compression', function () { it('parameter bigger than 16M packet size', function (done) { if (maxAllowedSize <= testSize) this.skip(); this.timeout(20000); //can take some time - conn.query('CREATE TEMPORARY TABLE bigParameter (b longblob)'); conn - .query('insert into bigParameter(b) values(?)', [buf]) + .query('DROP TABLE IF EXISTS bigParameter') + .then(() => { + return conn.query('CREATE TABLE bigParameter (b longblob)'); + }) + .then(() => { + return conn.query('insert into bigParameter(b) values(?)', [buf]); + }) .then(() => { return conn.query('SELECT * from bigParameter'); }) @@ -87,9 +92,14 @@ describe('Compression', function () { it('multi compression packet size', function (done) { if (maxAllowedSize <= testSize) this.skip(); this.timeout(20000); //can take some time - conn.query('CREATE TEMPORARY TABLE bigParameter2 (b longblob)'); conn - .query('insert into bigParameter2(b) values(?)', [randomBuf]) + .query('DROP TABLE IF EXISTS bigParameter2') + .then(() => { + return conn.query('CREATE TABLE bigParameter2 (b longblob)'); + }) + .then(() => { + return conn.query('insert into bigParameter2(b) values(?)', [randomBuf]); + }) .then(() => { return conn.query('SELECT * from bigParameter2'); }) diff --git a/test/integration/test-connection-meta.js b/test/integration/test-connection-meta.js index 141fd481..2c3cdd8d 100644 --- a/test/integration/test-connection-meta.js +++ b/test/integration/test-connection-meta.js @@ -12,10 +12,10 @@ describe('Connection meta', function () { assert(serverVersion.startsWith('10.5')); } else { const version = - process.platform === 'win32' - ? process.env.DB - : process.env.DB.substr(process.env.DB.indexOf(':') + 1); - assert(serverVersion.startsWith(version)); + process.env.DB.indexOf(':') != -1 + ? process.env.DB.substr(process.env.DB.indexOf(':') + 1) + : process.env.DB; + assert(serverVersion.startsWith(version), serverVersion + '/' + version); } } }); diff --git a/test/integration/test-connection-opts.js b/test/integration/test-connection-opts.js index 6425fe0e..b53dca5d 100644 --- a/test/integration/test-connection-opts.js +++ b/test/integration/test-connection-opts.js @@ -219,6 +219,7 @@ describe('connection option', () => { }); it('Server with different tz', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); //MySQL 5.5 doesn't have milliseconds if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); @@ -226,15 +227,27 @@ describe('connection option', () => { .createConnection({ timezone: 'Etc/GMT+5' }) .then((conn) => { const now = new Date(); - conn.query("SET SESSION time_zone = '-05:00'"); - conn.query('CREATE TEMPORARY TABLE t1 (a timestamp(6))'); - conn.query('INSERT INTO t1 values (?)', now); - conn.query('SELECT NOW() as b, t1.a FROM t1').then((res) => { - assert.deepEqual(res[0].a, now); - assert.isOk(Math.abs(res[0].b.getTime() - now.getTime()) < 5000); - conn.end(); - done(); - }); + conn + .query("SET SESSION time_zone = '-05:00'") + .then(() => { + return conn.query('DROP TABLE IF EXISTS t1'); + }) + .then(() => { + return conn.query('CREATE TABLE t1 (a timestamp(6))'); + }) + .then(() => { + return conn.query('INSERT INTO t1 values (?)', now); + }) + .then(() => { + return conn.query('SELECT NOW() as b, t1.a FROM t1'); + }) + .then((res) => { + assert.deepEqual(res[0].a, now); + assert.isOk(Math.abs(res[0].b.getTime() - now.getTime()) < 5000); + conn.end(); + done(); + }) + .catch(done); }) .catch(done); }); @@ -243,23 +256,38 @@ describe('connection option', () => { base .createConnection({ nestTables: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t1 (a varchar(20))'); - conn.query('CREATE TEMPORARY TABLE t2 (b varchar(20))'); - conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); - conn.query("INSERT INTO t2 VALUES ('bou')"); conn - .query('SELECT * FROM t1, t2') + .query('DROP TABLE IF EXISTS t1') + .then(() => { + return conn.query('DROP TABLE IF EXISTS t2'); + }) + .then(() => { + return conn.query('CREATE TABLE t1 (a varchar(20))'); + }) + .then(() => { + return conn.query('CREATE TABLE t2 (b varchar(20))'); + }) + .then(() => { + return conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); + }) + .then(() => { + return conn.query("INSERT INTO t2 VALUES ('bou')"); + }) + .then(() => { + return conn.query('SELECT * FROM t1, t2'); + }) .then((rows) => { assert.deepEqual(rows, [ { t1: { a: 'bla' }, t2: { b: 'bou' } }, { t1: { a: 'bla2' }, t2: { b: 'bou' } } ]); - return conn.end(); - }) - .then(() => { + conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); @@ -268,23 +296,38 @@ describe('connection option', () => { base .createConnection({ nestTables: '_' }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t1 (a varchar(20))'); - conn.query('CREATE TEMPORARY TABLE t2 (b varchar(20))'); - conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); - conn.query("INSERT INTO t2 VALUES ('bou')"); conn - .query('SELECT * FROM t1, t2') + .query('DROP TABLE IF EXISTS t1') + .then(() => { + return conn.query('DROP TABLE IF EXISTS t2'); + }) + .then(() => { + return conn.query('CREATE TABLE t1 (a varchar(20))'); + }) + .then(() => { + return conn.query('CREATE TABLE t2 (b varchar(20))'); + }) + .then(() => { + return conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); + }) + .then(() => { + return conn.query("INSERT INTO t2 VALUES ('bou')"); + }) + .then(() => { + return conn.query('SELECT * FROM t1, t2'); + }) .then((rows) => { assert.deepEqual(rows, [ { t1_a: 'bla', t2_b: 'bou' }, { t1_a: 'bla2', t2_b: 'bou' } ]); - return conn.end(); - }) - .then(() => { + conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); @@ -293,12 +336,26 @@ describe('connection option', () => { base .createConnection({ rowsAsArray: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t1 (a varchar(20))'); - conn.query('CREATE TEMPORARY TABLE t2 (b varchar(20))'); - conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); - conn.query("INSERT INTO t2 VALUES ('bou')"); conn - .query('SELECT * FROM t1, t2') + .query('DROP TABLE IF EXISTS t1') + .then(() => { + return conn.query('DROP TABLE IF EXISTS t2'); + }) + .then(() => { + return conn.query('CREATE TABLE t1 (a varchar(20))'); + }) + .then(() => { + return conn.query('CREATE TABLE t2 (b varchar(20))'); + }) + .then(() => { + return conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); + }) + .then(() => { + return conn.query("INSERT INTO t2 VALUES ('bou')"); + }) + .then(() => { + return conn.query('SELECT * FROM t1, t2'); + }) .then((rows) => { assert.deepEqual(rows, [ ['bla', 'bou'], @@ -318,12 +375,26 @@ describe('connection option', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t1 (a varchar(20))'); - conn.query('CREATE TEMPORARY TABLE t2 (b varchar(20))'); - conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); - conn.query("INSERT INTO t2 VALUES ('bou')"); conn - .query({ rowsAsArray: true, sql: 'SELECT * FROM t1, t2' }) + .query('DROP TABLE IF EXISTS t1') + .then(() => { + return conn.query('DROP TABLE IF EXISTS t2'); + }) + .then(() => { + return conn.query('CREATE TABLE t1 (a varchar(20))'); + }) + .then(() => { + return conn.query('CREATE TABLE t2 (b varchar(20))'); + }) + .then(() => { + return conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); + }) + .then(() => { + return conn.query("INSERT INTO t2 VALUES ('bou')"); + }) + .then(() => { + return conn.query({ rowsAsArray: true, sql: 'SELECT * FROM t1, t2' }); + }) .then((rows) => { assert.deepEqual(rows, [ ['bla', 'bou'], @@ -343,12 +414,26 @@ describe('connection option', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t1 (a varchar(20))'); - conn.query('CREATE TEMPORARY TABLE t2 (b varchar(20))'); - conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); - conn.query("INSERT INTO t2 VALUES ('bou')"); conn - .query({ nestTables: true, sql: 'SELECT * FROM t1, t2' }) + .query('DROP TABLE IF EXISTS t1') + .then(() => { + return conn.query('DROP TABLE IF EXISTS t2'); + }) + .then(() => { + return conn.query('CREATE TABLE t1 (a varchar(20))'); + }) + .then(() => { + return conn.query('CREATE TABLE t2 (b varchar(20))'); + }) + .then(() => { + return conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); + }) + .then(() => { + return conn.query("INSERT INTO t2 VALUES ('bou')"); + }) + .then(() => { + return conn.query({ nestTables: true, sql: 'SELECT * FROM t1, t2' }); + }) .then((rows) => { assert.deepEqual(rows, [ { t1: { a: 'bla' }, t2: { b: 'bou' } }, @@ -368,12 +453,26 @@ describe('connection option', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t1 (a varchar(20))'); - conn.query('CREATE TEMPORARY TABLE t2 (b varchar(20))'); - conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); - conn.query("INSERT INTO t2 VALUES ('bou')"); conn - .query({ nestTables: '_', sql: 'SELECT * FROM t1, t2' }) + .query('DROP TABLE IF EXISTS t1') + .then(() => { + return conn.query('DROP TABLE IF EXISTS t2'); + }) + .then(() => { + return conn.query('CREATE TABLE t1 (a varchar(20))'); + }) + .then(() => { + return conn.query('CREATE TABLE t2 (b varchar(20))'); + }) + .then(() => { + return conn.query("INSERT INTO t1 VALUES ('bla'), ('bla2')"); + }) + .then(() => { + return conn.query("INSERT INTO t2 VALUES ('bou')"); + }) + .then(() => { + return conn.query({ nestTables: '_', sql: 'SELECT * FROM t1, t2' }); + }) .then((rows) => { assert.deepEqual(rows, [ { t1_a: 'bla', t2_b: 'bou' }, @@ -396,7 +495,7 @@ describe('connection option', () => { conn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }) diff --git a/test/integration/test-connection.js b/test/integration/test-connection.js index d791699e..45271da2 100644 --- a/test/integration/test-connection.js +++ b/test/integration/test-connection.js @@ -25,7 +25,7 @@ describe('connection', () => { }); it('with medium connection attributes', function (done) { - const mediumAttribute = Buffer.alloc(20000, 'a').toString(); + const mediumAttribute = Buffer.alloc(512, 'a').toString(); connectWithAttributes({ par1: 'bouh', par2: mediumAttribute }, done); }); @@ -36,7 +36,7 @@ describe('connection', () => { conn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }) @@ -46,7 +46,7 @@ describe('connection', () => { } it('connection attributes with encoding not supported by node.js', function (done) { - const mediumAttribute = Buffer.alloc(20000, 'a').toString(); + const mediumAttribute = Buffer.alloc(500, 'a').toString(); base .createConnection({ connectAttributes: { par1: 'bouh', par2: mediumAttribute }, @@ -56,7 +56,7 @@ describe('connection', () => { conn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }) @@ -91,7 +91,7 @@ describe('connection', () => { if (err) { done(err); } else { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); } @@ -182,7 +182,7 @@ describe('connection', () => { }); it('connection error event', function (done) { - if (process.env.SKYSQL) this.skip(); + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 6, 0)) this.skip(); base .createConnection() @@ -254,7 +254,7 @@ describe('connection', () => { conn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }) @@ -425,7 +425,7 @@ describe('connection', () => { }); it('connection.destroy() during query execution', function (done) { - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); this.timeout(10000); base.createConnection().then((conn) => { @@ -591,7 +591,7 @@ describe('connection', () => { if ( (shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 2, 2)) || (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 4)) || - process.env.MAXSCALE_VERSION || + process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL ) { //session tracking not implemented @@ -642,7 +642,8 @@ describe('connection', () => { it('connection.connect() error code validation callback', function (done) { const conn = base.createCallbackConnection({ user: 'fooUser', - password: 'myPwd' + password: 'myPwd', + allowPublicKeyRetrieval: true }); conn.connect((err) => { if (!err) done(new Error('must have thrown error')); @@ -685,7 +686,7 @@ describe('connection', () => { it('connection.connect() error code validation promise', function (done) { base - .createConnection({ user: 'fooUser', password: 'myPwd' }) + .createConnection({ user: 'fooUser', password: 'myPwd', allowPublicKeyRetrieval: true }) .then(() => { done(new Error('must have thrown error')); }) @@ -773,7 +774,7 @@ describe('connection', () => { if ( ((shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 2)) || (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 7))) && - !process.env.MAXSCALE_VERSION && + !process.env.MAXSCALE_TEST_DISABLE && !process.env.SKYSQL ) { //ok packet contain meta change @@ -796,7 +797,7 @@ describe('connection', () => { shareConn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); const diff = process.hrtime(startTime); //query has take more than 500ms assert.isTrue( @@ -848,7 +849,7 @@ describe('connection', () => { if ( !shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(10, 4, 3) || - process.env.MAXSCALE_VERSION || + process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL ) { //session tracking not implemented @@ -889,7 +890,7 @@ describe('connection', () => { if ( !shareConn.info.isMariaDB() || !shareConn.info.hasMinVersion(10, 4, 3) || - process.env.MAXSCALE_VERSION || + process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL ) { //session tracking not implemented diff --git a/test/integration/test-debug.js b/test/integration/test-debug.js index 3848ab29..994f9040 100644 --- a/test/integration/test-debug.js +++ b/test/integration/test-debug.js @@ -66,7 +66,7 @@ describe('debug', () => { .then(() => { if ( compress && - process.env.MAXSCALE_VERSION == undefined && + process.env.MAXSCALE_TEST_DISABLE == undefined && process.env.SKYSQL == undefined ) { conn.debugCompress(true); @@ -78,7 +78,7 @@ describe('debug', () => { .then(() => { if ( compress && - process.env.MAXSCALE_VERSION == undefined && + process.env.MAXSCALE_TEST_DISABLE == undefined && process.env.SKYSQL == undefined ) { conn.debugCompress(false); @@ -96,13 +96,13 @@ describe('debug', () => { console.log = initialStdOut; const serverVersion = conn.serverVersion(); - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) compress = false; + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) compress = false; const rangeWithEOF = compress ? [900, 1200] : [1800, 2400]; const rangeWithoutEOF = compress ? [900, 1200] : [1750, 2000]; if ( ((conn.info.isMariaDB() && conn.info.hasMinVersion(10, 2, 2)) || (!conn.info.isMariaDB() && conn.info.hasMinVersion(5, 7, 5))) && - !process.env.MAXSCALE_VERSION && + !process.env.MAXSCALE_TEST_DISABLE && !process.env.SKYSQL ) { assert( @@ -145,7 +145,7 @@ describe('debug', () => { } it('select big request (compressed data) debug', function (done) { - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); initialStdOut = console.log; let data = ''; console.log = function () { @@ -216,23 +216,26 @@ describe('debug', () => { compress: compress }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE smallLocalInfile(id int, test varchar(100))'); conn - .query( - "LOAD DATA LOCAL INFILE '" + - smallFileName.replace(/\\/g, '/') + - "' INTO TABLE smallLocalInfile FIELDS TERMINATED BY ',' (id, test)" - ) + .query('DROP TABLE IF EXISTS smallLocalInfile') .then(() => { - return conn.end(); + return conn.query('CREATE TABLE smallLocalInfile(id int, test varchar(100))'); + }) + .then(() => { + return conn.query( + "LOAD DATA LOCAL INFILE '" + + smallFileName.replace(/\\/g, '/') + + "' INTO TABLE smallLocalInfile FIELDS TERMINATED BY ',' (id, test)" + ); }) .then(() => { + conn.end(); //wait 100ms to ensure stream has been written setTimeout(() => { console.log = initialStdOut; const serverVersion = conn.serverVersion(); - const range = [5500, 6800]; + const range = [5500, 7000]; assert( data.length > range[0] && data.length < range[1], 'wrong data length : ' + diff --git a/test/integration/test-error.js b/test/integration/test-error.js index 30e2463d..9e6ef9a0 100644 --- a/test/integration/test-error.js +++ b/test/integration/test-error.js @@ -334,7 +334,7 @@ describe('Error', () => { it('server close connection without warning', function (done) { //removed for maxscale, since wait_timeout will be set to other connections - if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(20000); let connectionErr = false; base @@ -374,7 +374,7 @@ describe('Error', () => { it('server close connection - no connection error event', function (done) { this.timeout(20000); - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); // Remove Mocha's error listener const originalException = process.listeners('uncaughtException').pop(); process.removeListener('uncaughtException', originalException); @@ -415,7 +415,7 @@ describe('Error', () => { }); it('server close connection during query', function (done) { - if (process.env.SKYSQL) this.skip(); + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(20000); base .createConnection() @@ -427,7 +427,7 @@ describe('Error', () => { done(new Error('must have thrown error !')); }) .catch((err) => { - if (process.env.MAXSCALE_VERSION) { + if (process.env.MAXSCALE_TEST_DISABLE) { assert.isTrue(err.message.includes('Lost connection to backend server'), err.message); assert.equal(err.sqlState, 'HY000'); } else { @@ -448,7 +448,7 @@ describe('Error', () => { }); it('end connection query error', function (done) { - // if (process.env.MAXSCALE_VERSION) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); base .createConnection() .then((conn) => { @@ -502,7 +502,7 @@ describe('Error', () => { shareConn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); done(); }) .catch(done); @@ -521,10 +521,18 @@ describe('Error', () => { ) ); }; - - shareConn.query('CREATE TEMPORARY TABLE undefinedParameter (id int, id2 int, id3 int)'); shareConn - .query('INSERT INTO undefinedParameter values (?, ?, ?)', [1, undefined, 3]) + .query('DROP TABLE IF EXISTS undefinedParameter') + .then(() => { + return shareConn.query('CREATE TABLE undefinedParameter (id int, id2 int, id3 int)'); + }) + .then(() => { + return shareConn.query('INSERT INTO undefinedParameter values (?, ?, ?)', [ + 1, + undefined, + 3 + ]); + }) .then(() => { done(new Error('must have thrown error !')); }) @@ -533,7 +541,7 @@ describe('Error', () => { shareConn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); done(); }) .catch(done); @@ -552,9 +560,14 @@ describe('Error', () => { ) ); }; - shareConn.query('CREATE TEMPORARY TABLE execute_missing_parameter (id int, id2 int, id3 int)'); shareConn - .query('INSERT INTO execute_missing_parameter values (?, ?, ?)', [1, 3]) + .query('DROP TABLE IF EXISTS execute_missing_parameter') + .then(() => { + return shareConn.query('CREATE TABLE execute_missing_parameter (id int, id2 int, id3 int)'); + }) + .then(() => { + return shareConn.query('INSERT INTO execute_missing_parameter values (?, ?, ?)', [1, 3]); + }) .then(() => { done(new Error('must have thrown error !')); }) @@ -562,51 +575,60 @@ describe('Error', () => { shareConn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); done(); }) .catch(done); }); it('query missing parameter with compression', function (done) { - const handleResult = function (err) { - assert.equal(err.errno, 45016); - assert.equal(err.sqlState, 'HY000'); - assert.equal(err.code, 'ER_MISSING_PARAMETER'); - assert.isTrue(!err.fatal); - assert.ok( - err.message.includes( - 'Parameter at position 3 is not set\n' + - 'sql: INSERT INTO execute_missing_parameter values (?, ?, ?) - parameters:[1,3]' - ) - ); - }; base .createConnection({ compress: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE execute_missing_parameter (id int, id2 int, id3 int)'); conn - .query('INSERT INTO execute_missing_parameter values (?, ?, ?)', [1, 3]) + .query('DROP TABLE IF EXISTS execute_missing_parameter') .then(() => { - done(new Error('must have thrown error !')); + return conn.query('CREATE TABLE execute_missing_parameter (id int, id2 int, id3 int)'); }) - .catch(handleResult); - conn - .query('SELECT 1') - .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); - conn.end(); - done(); + .then(() => { + return conn.query('INSERT INTO execute_missing_parameter values (?, ?, ?)', [1, 3]); + }) + .then(() => { + done(new Error('must have thrown error !')); }) - .catch(done); + .catch((err) => { + assert.equal(err.errno, 45016); + assert.equal(err.sqlState, 'HY000'); + assert.equal(err.code, 'ER_MISSING_PARAMETER'); + assert.isTrue(!err.fatal); + assert.ok( + err.message.includes( + 'Parameter at position 3 is not set\n' + + 'sql: INSERT INTO execute_missing_parameter values (?, ?, ?) - parameters:[1,3]' + ) + ); + return conn + .query('SELECT 1') + .then((rows) => { + assert.deepEqual(rows, [{ 1: 1 }]); + conn.end(); + done(); + }) + .catch(done); + }); }) .catch(done); }); it('query no parameter', function (done) { - shareConn.query('CREATE TEMPORARY TABLE execute_no_parameter (id int, id2 int, id3 int)'); shareConn - .query('INSERT INTO execute_no_parameter values (?, ?, ?)', []) + .query('DROP TABLE IF EXISTS execute_no_parameter') + .then(() => { + return shareConn.query('CREATE TABLE execute_no_parameter (id int, id2 int, id3 int)'); + }) + .then(() => { + return shareConn.query('INSERT INTO execute_no_parameter values (?, ?, ?)', []); + }) .then(() => { done(new Error('must have thrown error !')); }) @@ -625,16 +647,22 @@ describe('Error', () => { shareConn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); done(); }) .catch(done); }); it('query to much parameter', function (done) { - shareConn.query('CREATE TEMPORARY TABLE to_much_parameters (id int, id2 int, id3 int)'); shareConn - .query('INSERT INTO to_much_parameters values (?, ?, ?) ', [1, 2, 3, 4]) + .query('DROP TABLE IF EXISTS to_much_parameters') + .then(() => { + return shareConn.query('CREATE TABLE to_much_parameters (id int, id2 int, id3 int)'); + }) + .then(() => { + return shareConn.query('INSERT INTO to_much_parameters values (?, ?, ?) ', [1, 2, 3, 4]); + }) + .then(() => done()) .catch(done); }); diff --git a/test/integration/test-initial-commands.js b/test/integration/test-initial-commands.js index 82da9f40..9748d785 100644 --- a/test/integration/test-initial-commands.js +++ b/test/integration/test-initial-commands.js @@ -12,7 +12,7 @@ describe('initial connection commands', () => { conn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }) @@ -77,7 +77,7 @@ describe('initial connection commands', () => { conn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }) diff --git a/test/integration/test-local-infile.js b/test/integration/test-local-infile.js index 3c6414a6..ccee8a4f 100644 --- a/test/integration/test-local-infile.js +++ b/test/integration/test-local-infile.js @@ -82,6 +82,7 @@ describe('local-infile', () => { conn .query("LOAD DATA LOCAL INFILE 'dummy.tsv' INTO TABLE t (id, test)") .then(() => { + conn.end(); done(new Error('must have thrown error !')); }) .catch((err) => { @@ -112,8 +113,11 @@ describe('local-infile', () => { base .createConnection({ permitLocalInfile: true }) .then((conn) => { - conn - .query('CREATE TEMPORARY TABLE smallLocalInfile(id int, test varchar(100))') + shareConn + .query('DROP TABLE IF EXISTS smallLocalInfile') + .then(() => { + return conn.query('CREATE TABLE smallLocalInfile(id int, test varchar(100))'); + }) .then(() => { return conn.query( "LOAD DATA LOCAL INFILE '" + @@ -160,13 +164,18 @@ describe('local-infile', () => { base .createConnection({ permitLocalInfile: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE smallLocalInfile(id int, test varchar(100))'); - conn - .query( - "LOAD DATA LOCAL INFILE '" + - smallFileName.replace(/\\/g, '/') + - "' INTO TABLE smallLocalInfile FIELDS TERMINATED BY ',' (id, test)" - ) + shareConn + .query('DROP TABLE IF EXISTS smallLocalInfile') + .then(() => { + return conn.query('CREATE TABLE smallLocalInfile(id int, test varchar(100))'); + }) + .then(() => { + return conn.query( + "LOAD DATA LOCAL INFILE '" + + smallFileName.replace(/\\/g, '/') + + "' INTO TABLE smallLocalInfile FIELDS TERMINATED BY ',' (id, test)" + ); + }) .then(() => { return conn.query('SELECT * FROM smallLocalInfile'); }) @@ -204,13 +213,18 @@ describe('local-infile', () => { base .createConnection({ permitLocalInfile: true, charset: 'big5' }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE smallLocalInfile(id int, test varchar(100))'); - conn - .query( - "LOAD DATA LOCAL INFILE '" + - smallFileName.replace(/\\/g, '/') + - "' INTO TABLE smallLocalInfile FIELDS TERMINATED BY ',' (id, test)" - ) + shareConn + .query('DROP TABLE IF EXISTS smallLocalInfile') + .then(() => { + return conn.query('CREATE TABLE smallLocalInfile(id int, test varchar(100))'); + }) + .then(() => { + return conn.query( + "LOAD DATA LOCAL INFILE '" + + smallFileName.replace(/\\/g, '/') + + "' INTO TABLE smallLocalInfile FIELDS TERMINATED BY ',' (id, test)" + ); + }) .then(() => { return conn.query('SELECT * FROM smallLocalInfile'); }) @@ -252,13 +266,18 @@ describe('local-infile', () => { base .createConnection({ permitLocalInfile: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE nonReadableFile(id int, test varchar(100))'); - conn - .query( - "LOAD DATA LOCAL INFILE '" + - nonReadableFile.replace(/\\/g, '/') + - "' INTO TABLE nonReadableFile FIELDS TERMINATED BY ',' (id, test)" - ) + shareConn + .query('DROP TABLE IF EXISTS nonReadableFile') + .then(() => { + return conn.query('CREATE TABLE nonReadableFile(id int, test varchar(100))'); + }) + .then(() => { + return conn.query( + "LOAD DATA LOCAL INFILE '" + + nonReadableFile.replace(/\\/g, '/') + + "' INTO TABLE nonReadableFile FIELDS TERMINATED BY ',' (id, test)" + ); + }) .then(() => { conn.end(); done('must have thrown error'); @@ -308,16 +327,21 @@ describe('local-infile', () => { base .createConnection({ permitLocalInfile: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE bigLocalInfile(t1 varchar(10), t2 varchar(2))'); - conn - .query( - "LOAD DATA LOCAL INFILE '" + - bigFileName.replace(/\\/g, '/') + - "' INTO TABLE bigLocalInfile " + - "COLUMNS TERMINATED BY ',' ENCLOSED BY '\\\"' ESCAPED BY '\\\\' " + - "LINES TERMINATED BY '\\n' IGNORE 1 LINES " + - '(t1, t2)' - ) + shareConn + .query('DROP TABLE IF EXISTS bigLocalInfile') + .then(() => { + return conn.query('CREATE TABLE bigLocalInfile(t1 varchar(10), t2 varchar(2))'); + }) + .then(() => { + return conn.query( + "LOAD DATA LOCAL INFILE '" + + bigFileName.replace(/\\/g, '/') + + "' INTO TABLE bigLocalInfile " + + "COLUMNS TERMINATED BY ',' ENCLOSED BY '\\\"' ESCAPED BY '\\\\' " + + "LINES TERMINATED BY '\\n' IGNORE 1 LINES " + + '(t1, t2)' + ); + }) .then(() => { return conn.query('SELECT * FROM bigLocalInfile'); }) diff --git a/test/integration/test-metadata.js b/test/integration/test-metadata.js index bdeacd7a..d942ca9e 100644 --- a/test/integration/test-metadata.js +++ b/test/integration/test-metadata.js @@ -8,15 +8,18 @@ const FieldType = require('../../lib/const/field-type'); describe('metadata', () => { it('result metadata values', function (done) { shareConn - .query( - 'CREATE TEMPORARY TABLE metadatatable (id BIGINT not null primary key auto_increment, ' + - 't varchar(32) UNIQUE, ' + - 'd DECIMAL(10,4) UNSIGNED ZEROFILL, ' + - 'ds DECIMAL(10,4) SIGNED, ' + - 'd2 DECIMAL(10,0) UNSIGNED, ' + - 'ds2 DECIMAL(10,0) SIGNED ' + - ") COLLATE='utf8mb4_unicode_ci'" - ) + .query('DROP TABLE IF EXISTS metadatatable') + .then(() => { + return shareConn.query( + 'CREATE TABLE metadatatable (id BIGINT not null primary key auto_increment, ' + + 't varchar(32) UNIQUE, ' + + 'd DECIMAL(10,4) UNSIGNED ZEROFILL, ' + + 'ds DECIMAL(10,4) SIGNED, ' + + 'd2 DECIMAL(10,0) UNSIGNED, ' + + 'ds2 DECIMAL(10,0) SIGNED ' + + ") COLLATE='utf8mb4_unicode_ci'" + ); + }) .then(() => { return shareConn.query( 'SELECT id as id1, t as t1, d as d1, ds as d2, d2 as d3, ds2 as d4 FROM metadatatable as tm' diff --git a/test/integration/test-multi-results.js b/test/integration/test-multi-results.js index 1b5af4e0..ec66e25e 100644 --- a/test/integration/test-multi-results.js +++ b/test/integration/test-multi-results.js @@ -42,10 +42,17 @@ describe('multi-results', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t (i int)'); - conn.query('INSERT INTO t(i) VALUES (1)'); - conn - .query({ rowsAsArray: true, sql: 'SELECT i, i FROM t' }) + shareConn + .query('DROP TABLE IF EXISTS t') + .then(() => { + return conn.query('CREATE TABLE t (i int)'); + }) + .then(() => { + return conn.query('INSERT INTO t(i) VALUES (1)'); + }) + .then(() => { + return conn.query({ rowsAsArray: true, sql: 'SELECT i, i FROM t' }); + }) .then((res) => { conn .query('SELECT i, i FROM t') @@ -82,10 +89,17 @@ describe('multi-results', () => { base .createConnection({ checkDuplicate: false }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t (i int)'); - conn.query('INSERT INTO t(i) VALUES (1)'); - conn - .query({ rowsAsArray: true, sql: 'SELECT i, i FROM t' }) + shareConn + .query('DROP TABLE IF EXISTS t') + .then(() => { + return conn.query('CREATE TABLE t (i int)'); + }) + .then(() => { + return conn.query('INSERT INTO t(i) VALUES (1)'); + }) + .then(() => { + return conn.query({ rowsAsArray: true, sql: 'SELECT i, i FROM t' }); + }) .then((res) => { conn .query('SELECT i, i FROM t') @@ -112,10 +126,17 @@ describe('multi-results', () => { base .createConnection({ nestTables: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t (i int)'); - conn.query('INSERT INTO t(i) VALUES (1)'); - conn - .query({ rowsAsArray: true, sql: 'SELECT i, i FROM t' }) + shareConn + .query('DROP TABLE IF EXISTS t') + .then(() => { + return conn.query('CREATE TABLE t (i int)'); + }) + .then(() => { + return conn.query('INSERT INTO t(i) VALUES (1)'); + }) + .then(() => { + return conn.query({ rowsAsArray: true, sql: 'SELECT i, i FROM t' }); + }) .then((res) => { conn .query('SELECT i, i FROM t') @@ -146,10 +167,18 @@ describe('multi-results', () => { base .createConnection({ checkDuplicate: false, nestTables: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE t (i int)'); - conn.query('INSERT INTO t(i) VALUES (1)'); - conn - .query({ rowsAsArray: true, sql: 'SELECT i, i FROM t' }) + shareConn + .query('DROP TABLE IF EXISTS t') + .then(() => { + return conn.query('CREATE TABLE t (i int)'); + }) + .then(() => { + return conn.query('INSERT INTO t(i) VALUES (1)'); + }) + .then(() => { + return conn.query({ rowsAsArray: true, sql: 'SELECT i, i FROM t' }); + }) + .then((res) => { conn .query('SELECT i, i FROM t') @@ -234,7 +263,7 @@ describe('multi-results', () => { shareConn .query('SELECT 1') .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); done(); }) .catch(done); @@ -244,7 +273,7 @@ describe('multi-results', () => { shareConn .query('select 1', (err, rows) => {}) .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); done(); }) .catch(done); @@ -256,7 +285,7 @@ describe('multi-results', () => { .query('select 1') .then((obj) => { assert.equal(obj.length, 2); - assert.deepEqual(obj[0], [{ '1': 1 }]); + assert.deepEqual(obj[0], [{ 1: 1 }]); conn.end(); done(); }) @@ -272,7 +301,7 @@ describe('multi-results', () => { .then((obj) => { assert.equal(obj[0].length, 2); assert.equal(obj[1].length, 2); - assert.deepEqual(obj[0], [[{ '1': 1 }], [{ '2': 2 }]]); + assert.deepEqual(obj[0], [[{ 1: 1 }], [{ 2: 2 }]]); conn.end(); done(); }) @@ -290,7 +319,7 @@ describe('multi-results', () => { if (err) { done(err); } else { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); callbackConn.end(); done(); } @@ -410,8 +439,8 @@ describe('multi-results', () => { .query('call myProc()') .then((rows) => { assert.equal(rows.length, 3); - assert.deepEqual(rows[0], [{ '1': 1 }]); - assert.deepEqual(rows[1], [{ '2': 2 }]); + assert.deepEqual(rows[0], [{ 1: 1 }]); + assert.deepEqual(rows[1], [{ 2: 2 }]); assert.deepEqual(rows[2], { affectedRows: 0, insertId: 0, diff --git a/test/integration/test-ok-packet.js b/test/integration/test-ok-packet.js index f56331ee..1f0b5c09 100644 --- a/test/integration/test-ok-packet.js +++ b/test/integration/test-ok-packet.js @@ -6,7 +6,12 @@ const { assert } = require('chai'); describe('ok packet', () => { it('insertId', function (done) { shareConn - .query('CREATE TEMPORARY TABLE autoInc (id BIGINT not null primary key auto_increment)') + .query('DROP TABLE IF EXISTS autoInc') + .then(() => { + return shareConn.query( + 'CREATE TABLE autoInc (id BIGINT not null primary key auto_increment)' + ); + }) .then(() => { return shareConn.query('INSERT INTO autoInc values ()'); }) @@ -59,7 +64,12 @@ describe('ok packet', () => { it('negative insertId', function (done) { shareConn - .query('CREATE TEMPORARY TABLE negAutoInc (id BIGINT not null primary key auto_increment)') + .query('DROP TABLE IF EXISTS negAutoInc') + .then(() => { + return shareConn.query( + 'CREATE TABLE negAutoInc (id BIGINT not null primary key auto_increment)' + ); + }) .then(() => { return shareConn.query('INSERT INTO negAutoInc values (-9007199254740990)'); }) @@ -103,15 +113,19 @@ describe('ok packet', () => { }); it('basic insert result', function (done) { - shareConn.query( - 'CREATE TEMPORARY TABLE insertResultSet1(' + - 'id int(11) unsigned NOT NULL AUTO_INCREMENT,' + - 'val varchar(256),' + - 'PRIMARY KEY (id))' - ); - shareConn - .query('INSERT INTO insertResultSet1(val) values (?)', ['t']) + .query('DROP TABLE IF EXISTS insertResultSet1') + .then(() => { + return shareConn.query( + 'CREATE TABLE insertResultSet1(' + + 'id int(11) unsigned NOT NULL AUTO_INCREMENT,' + + 'val varchar(256),' + + 'PRIMARY KEY (id))' + ); + }) + .then(() => { + return shareConn.query('INSERT INTO insertResultSet1(val) values (?)', ['t']); + }) .then((rows) => { assert.ok(!Array.isArray(rows)); assert.strictEqual(typeof rows, 'object'); @@ -129,11 +143,14 @@ describe('ok packet', () => { .createConnection({ multipleStatements: true }) .then((conn) => { conn - .query( - 'CREATE TEMPORARY TABLE multiple_insert_result(' + - 'id int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,' + - 'val varchar(256))' - ) + .query('DROP TABLE IF EXISTS multiple_insert_result') + .then(() => { + return conn.query( + 'CREATE TABLE multiple_insert_result(' + + 'id int(11) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,' + + 'val varchar(256))' + ); + }) .then(() => { return conn.query( 'INSERT INTO multiple_insert_result(val) values (?); ' + @@ -164,7 +181,10 @@ describe('ok packet', () => { it('update result text', function (done) { shareConn - .query('CREATE TEMPORARY TABLE updateResultSet1(id int(11))') + .query('DROP TABLE IF EXISTS updateResultSet1') + .then(() => { + return shareConn.query('CREATE TABLE updateResultSet1(id int(11))'); + }) .then(() => { return shareConn.query('INSERT INTO updateResultSet1 values (1), (1), (2), (3)'); }) @@ -195,7 +215,10 @@ describe('ok packet', () => { .createConnection({ foundRows: false }) .then((conn) => { conn - .query('CREATE TEMPORARY TABLE updateResultSet1(id int(11))') + .query('DROP TABLE IF EXISTS updateResultSet1') + .then(() => { + return conn.query('CREATE TABLE updateResultSet1(id int(11))'); + }) .then(() => { return conn.query('INSERT INTO updateResultSet1 values (1), (1), (2), (3)'); }) diff --git a/test/integration/test-pipelining.js b/test/integration/test-pipelining.js index 39238c5d..059c54cc 100644 --- a/test/integration/test-pipelining.js +++ b/test/integration/test-pipelining.js @@ -59,7 +59,7 @@ describe('pipelining', () => { conn.connect((err) => {}); conn.query('DO 1'); conn.query('SELECT 1', (err, rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }); @@ -69,9 +69,15 @@ describe('pipelining', () => { this.timeout(60000); let diff, pipelineDiff; conn1 - .query('CREATE TEMPORARY TABLE pipeline1 (test int)') + .query('DROP TABLE IF EXISTS pipeline1') .then(() => { - return conn2.query('CREATE TEMPORARY TABLE pipeline2 (test int)'); + return conn2.query('DROP TABLE IF EXISTS pipeline2'); + }) + .then(() => { + return conn1.query('CREATE TABLE pipeline1 (test int)'); + }) + .then(() => { + return conn2.query('CREATE TABLE pipeline2 (test int)'); }) .then(() => { return insertBulk(conn1, 'pipeline1'); diff --git a/test/integration/test-placholders.js b/test/integration/test-placholders.js index 115cd895..ed026e20 100644 --- a/test/integration/test-placholders.js +++ b/test/integration/test-placholders.js @@ -50,7 +50,7 @@ describe('Placeholder', () => { { 'param-3': 30, 'param-1': 10, 'param-2': 20 } ) .then((rows) => { - assert.deepEqual(rows, [{ val1: 10, val3: 30, '20': 20 }]); + assert.deepEqual(rows, [{ val1: 10, val3: 30, 20: 20 }]); done(); }) .catch(done); @@ -108,12 +108,17 @@ describe('Placeholder', () => { base .createConnection({ namedPlaceholders: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE undefinedParameter (id int, id2 int, id3 int)'); conn - .query('INSERT INTO undefinedParameter values (:param3, :param1, :param2)', { - param1: 1, - param3: 3, - param4: 4 + .query('DROP TABLE IF EXISTS undefinedParameter') + .then(() => { + return conn.query('CREATE TABLE undefinedParameter (id int, id2 int, id3 int)'); + }) + .then(() => { + return conn.query('INSERT INTO undefinedParameter values (:param3, :param1, :param2)', { + param1: 1, + param3: 3, + param4: 4 + }); }) .then(() => { done(new Error('must have thrown error!')); @@ -143,11 +148,16 @@ describe('Placeholder', () => { base .createConnection({ namedPlaceholders: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE execute_missing_parameter (id int, id2 int, id3 int)'); conn - .query('INSERT INTO execute_missing_parameter values (:t1, :t2, :t3)', { - t1: 1, - t3: 3 + .query('DROP TABLE IF EXISTS execute_missing_parameter') + .then(() => { + return conn.query('CREATE TABLE execute_missing_parameter (id int, id2 int, id3 int)'); + }) + .then(() => { + return conn.query('INSERT INTO execute_missing_parameter values (:t1, :t2, :t3)', { + t1: 1, + t3: 3 + }); }) .then(() => { done(new Error('must have thrown error!')); @@ -176,9 +186,14 @@ describe('Placeholder', () => { base .createConnection({ namedPlaceholders: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE execute_no_parameter (id int, id2 int, id3 int)'); conn - .query('INSERT INTO execute_no_parameter values (:t1, :t2, :t3)', []) + .query('DROP TABLE IF EXISTS execute_no_parameter') + .then(() => { + return conn.query('CREATE TABLE execute_no_parameter (id int, id2 int, id3 int)'); + }) + .then(() => { + return conn.query('INSERT INTO execute_no_parameter values (:t1, :t2, :t3)', []); + }) .then(() => { done(new Error('must have thrown error!')); }) @@ -195,13 +210,18 @@ describe('Placeholder', () => { base .createConnection({ namedPlaceholders: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE to_much_parameters (id int, id2 int, id3 int)'); conn - .query('INSERT INTO to_much_parameters values (:t2, :t0, :t1)', { - t0: 0, - t1: 1, - t2: 2, - t3: 3 + .query('DROP TABLE IF EXISTS to_much_parameters') + .then(() => { + return conn.query('CREATE TABLE to_much_parameters (id int, id2 int, id3 int)'); + }) + .then(() => { + return conn.query('INSERT INTO to_much_parameters values (:t2, :t0, :t1)', { + t0: 0, + t1: 1, + t2: 2, + t3: 3 + }); }) .then(() => { conn.end(); @@ -217,10 +237,17 @@ describe('Placeholder', () => { base .createConnection({ namedPlaceholders: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE parse(t varchar(128))'); - conn.query('INSERT INTO `parse` value (:val)', { val: value }); conn - .query('select * from `parse` where t = :val', { val: value }) + .query('DROP TABLE IF EXISTS parse') + .then(() => { + return conn.query('CREATE TABLE parse(t varchar(128))'); + }) + .then(() => { + return conn.query('INSERT INTO `parse` value (:val)', { val: value }); + }) + .then(() => { + return conn.query('select * from `parse` where t = :val', { val: value }); + }) .then((res) => { assert.strictEqual(res[0].t, value); conn.end(); @@ -238,7 +265,7 @@ describe('Placeholder', () => { conn .query('select 1', [2]) .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }) diff --git a/test/integration/test-pool-callback-event.js b/test/integration/test-pool-callback-event.js index 44770866..d771d831 100644 --- a/test/integration/test-pool-callback-event.js +++ b/test/integration/test-pool-callback-event.js @@ -72,8 +72,8 @@ describe('Pool callback event', () => { assert.equal(releaseNumber, 500, releaseNumber); pool.end(); done(); - }, 1000); + }, 5000); }); - }, 500); + }, 1000); }); }); diff --git a/test/integration/test-pool-callback.js b/test/integration/test-pool-callback.js index f0ed94f1..7adacd16 100644 --- a/test/integration/test-pool-callback.js +++ b/test/integration/test-pool-callback.js @@ -6,10 +6,11 @@ const Conf = require('../conf'); describe('Pool callback', () => { before(function () { - if (process.env.SKYSQL) this.skip(); + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); }); it('pool with wrong authentication', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); //to avoid host beeing blocked this.timeout(10000); const pool = base.createPoolCallback({ acquireTimeout: 4000, @@ -29,7 +30,8 @@ describe('Pool callback', () => { err.errno === 1045 || err.errno === 1698 || err.errno === 45028 || - err.errno === 45025, + err.errno === 45025 || + err.errno === 45044, err.message ); pool.end(); @@ -67,7 +69,8 @@ describe('Pool callback', () => { err.errno === 1045 || err.errno === 1698 || err.errno === 45028 || - err.errno === 45025, + err.errno === 45025 || + err.errno === 45044, err.errno + ' - ' + err.message ); done(); @@ -256,10 +259,14 @@ describe('Pool callback', () => { assert.equal(pool.idleConnections(), 10); assert.equal(pool.taskQueueSize(), 0); let closed = false; + let doneSend = false; for (let i = 0; i < 10000; i++) { pool.query('SELECT ? as a', [i], (err, rows) => { if (err) { - if (!closed) done(err); + if (!doneSend) { + doneSend = true; + done(err); + } } else { assert.deepEqual(rows, [{ a: i }]); } @@ -290,7 +297,7 @@ describe('Pool callback', () => { assert.equal(pool.idleConnections(), 0); assert.equal(pool.taskQueueSize(), 0); } - done(); + if (!doneSend) done(); }); }, 5000); }); @@ -298,7 +305,7 @@ describe('Pool callback', () => { }); it('connection fail handling', function (done) { - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPoolCallback({ connectionLimit: 2, minDelayValidation: 200 @@ -339,7 +346,7 @@ describe('Pool callback', () => { }); it('query fail handling', function (done) { - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPoolCallback({ connectionLimit: 2, minDelayValidation: 200 @@ -438,6 +445,7 @@ describe('Pool callback', () => { }); it('connection destroy', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); const pool = base.createPoolCallback({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -504,44 +512,49 @@ describe('Pool callback', () => { connectionLimit: 1, resetAfterUse: false }); - pool.query('DROP TABLE IF EXISTS parse'); - pool.query('CREATE TABLE parse(id int, id2 int, id3 int, t varchar(128), id4 int)'); - pool.batch( - 'INSERT INTO `parse` values (1, ?, 2, ?, 3)', - [ - [1, 'john'], - [2, 'jack'] - ], - (err, res) => { - if (err) { - done(err); - } else { - assert.equal(res.affectedRows, 2); - pool.query('select * from `parse`', (err2, res2) => { - assert.deepEqual(res2, [ - { - id: 1, - id2: 1, - id3: 2, - t: 'john', - id4: 3 - }, - { - id: 1, - id2: 2, - id3: 2, - t: 'jack', - id4: 3 + 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) => { + pool.batch( + 'INSERT INTO `parse` values (1, ?, 2, ?, 3)', + [ + [1, 'john'], + [2, 'jack'] + ], + (err, res) => { + if (err) { + done(err); + } else { + assert.equal(res.affectedRows, 2); + pool.query('select * from `parse`', (err2, res2) => { + assert.deepEqual(res2, [ + { + id: 1, + id2: 1, + id3: 2, + t: 'john', + id4: 3 + }, + { + id: 1, + id2: 2, + id3: 2, + t: 'jack', + id4: 3 + } + ]); + pool.query('DROP TABLE parse'); + pool.end(() => { + done(); + }); + }); } - ]); - pool.query('DROP TABLE parse'); - pool.end(() => { - done(); - }); - }); + } + ); } - } - ); + ); + }); }); it('pool batch without parameters', function (done) { @@ -565,25 +578,43 @@ describe('Pool callback', () => { connectionLimit: 1, resetAfterUse: false }); - pool.query('CREATE TEMPORARY TABLE singleBatchArrayCallback(id int)'); - pool.batch('INSERT INTO `singleBatchArrayCallback` values (?)', [1, 2, 3], (err, res) => { + pool.query('DROP TABLE IF EXISTS singleBatchArrayCallback', (err, res) => { if (err) { + pool.end(); done(err); } else { - pool.query('select * from `singleBatchArrayCallback`', (err, res) => { - assert.deepEqual(res, [ - { - id: 1 - }, - { - id: 2 - }, - { - id: 3 - } - ]); - pool.end(); - done(); + pool.query('CREATE TABLE singleBatchArrayCallback(id int)', (err, res) => { + if (err) { + pool.end(); + done(err); + } else { + pool.batch( + 'INSERT INTO `singleBatchArrayCallback` values (?)', + [1, 2, 3], + (err, res) => { + if (err) { + pool.end(); + done(err); + } else { + pool.query('select * from `singleBatchArrayCallback`', (err, res) => { + assert.deepEqual(res, [ + { + id: 1 + }, + { + id: 2 + }, + { + id: 3 + } + ]); + pool.end(); + done(); + }); + } + } + ); + } }); } }); @@ -595,32 +626,34 @@ describe('Pool callback', () => { const pool = base.createPoolCallback({ connectionLimit: 10, minimumIdle: 4, - idleTimeout: 2 + idleTimeout: 2, + acquireTimeout: 20000 }); - - for (let i = 0; i < 15000; i++) { - pool.query('SELECT ' + i); - } - pool.query('SELECT 15000', [], (err) => { - if (err) { - pool.end(); - done(err); - } else { - setTimeout(() => { - assert.equal(pool.totalConnections(), 10); - assert.equal(pool.idleConnections(), 10); - }, 5); - - setTimeout(() => { - //minimumIdle-1 is possible after reaching idleTimeout and connection - // is still not recreated - assert.isTrue(pool.totalConnections() === 4 || pool.totalConnections() === 3); - assert.isTrue(pool.idleConnections() === 4 || pool.idleConnections() === 3); - pool.end(); - done(); - }, 7000); + setTimeout(() => { + for (let i = 0; i < 5000; i++) { + pool.query('SELECT ' + i); } - }); + pool.query('SELECT 5000', [], (err) => { + if (err) { + pool.end(); + done(err); + } else { + setTimeout(() => { + assert.equal(pool.totalConnections(), 10); + assert.equal(pool.idleConnections(), 10); + }, 5); + + setTimeout(() => { + //minimumIdle-1 is possible after reaching idleTimeout and connection + // is still not recreated + assert.isTrue(pool.totalConnections() === 4 || pool.totalConnections() === 3); + assert.isTrue(pool.idleConnections() === 4 || pool.idleConnections() === 3); + pool.end(); + done(); + }, 7000); + } + }); + }, 4000); }); it('test minimum idle', function (done) { @@ -628,7 +661,8 @@ describe('Pool callback', () => { const pool = base.createPoolCallback({ connectionLimit: 10, minimumIdle: 4, - idleTimeout: 2 + idleTimeout: 2, + acquireTimeout: 20000 }); setTimeout(() => { diff --git a/test/integration/test-pool.js b/test/integration/test-pool.js index d6b469b7..830d8cf8 100644 --- a/test/integration/test-pool.js +++ b/test/integration/test-pool.js @@ -50,9 +50,11 @@ describe('Pool', () => { }); }); - it('pool escape', function () { + it('pool escape', function (done) { if (!base.utf8Collation()) this.skip(); const pool = base.createPool({ connectionLimit: 1 }); + const pool2 = base.createPool({ connectionLimit: 1, arrayParenthesis: true }); + pool.on('connection', (conn) => { assert.equal(pool.escape(new Date('1999-01-31 12:13:14.000')), "'1999-01-31 12:13:14.000'"); assert.equal( @@ -82,15 +84,17 @@ describe('Pool', () => { assert.equal(pool.escape("let'g'o😊"), "'let\\'g\\'o😊'"); assert.equal(pool.escape("a'\nb\tc\rd\\e%_\u001a"), "'a\\'\\nb\\tc\\rd\\\\e%_\\Z'"); const arr = ["let'g'o😊", false, null, fctStr]; - assert.equal(pool.escape(arr), "('let\\'g\\'o😊',false,NULL,'bla\\'bla')"); + assert.equal(pool.escape(arr), "'let\\'g\\'o😊',false,NULL,'bla\\'bla'"); + assert.equal(pool2.escape(arr), "('let\\'g\\'o😊',false,NULL,'bla\\'bla')"); assert.equal(pool.escapeId('good_$one'), '`good_$one`'); assert.equal(pool.escape(''), "''"); assert.equal(pool.escapeId('f:a'), '`f:a`'); assert.equal(pool.escapeId('`f:a`'), '`f:a`'); assert.equal(pool.escapeId('good_`è`one'), '`good_``è``one`'); - pool.end(); + pool2.end(); + done(); }); }); @@ -105,6 +109,7 @@ describe('Pool', () => { }); it('pool with wrong authentication', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); //to avoid host beeing blocked this.timeout(10000); const pool = base.createPool({ acquireTimeout: 4000, @@ -123,7 +128,8 @@ describe('Pool', () => { err.errno === 1045 || err.errno === 1698 || err.errno === 45025 || - err.errno === 45028, + err.errno === 45028 || + err.errno === 45044, err.message ); pool @@ -139,7 +145,8 @@ describe('Pool', () => { err.errno === 1045 || err.errno === 1698 || err.errno === 45028 || - err.errno === 45025, + err.errno === 45025 || + err.errno === 45044, err.message ); done(); @@ -157,13 +164,15 @@ describe('Pool', () => { err.errno === 1045 || err.errno === 1698 || err.errno === 45025 || - err.errno === 45028, + err.errno === 45028 || + err.errno === 45044, err.message ); }); }); it('pool with wrong authentication connection', function (done) { + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(10000); const pool = base.createPool({ acquireTimeout: 4000, @@ -182,7 +191,8 @@ describe('Pool', () => { err.errno === 1045 || err.errno === 1698 || err.errno === 45028 || - err.errno === 45025, + err.errno === 45025 || + err.errno === 45044, err.message ); pool @@ -198,7 +208,8 @@ describe('Pool', () => { err.errno === 1045 || err.errno === 1698 || err.errno === 45028 || - err.errno === 45025, + err.errno === 45025 || + err.errno === 45044, err.message ); done(); @@ -216,13 +227,15 @@ describe('Pool', () => { err.errno === 1045 || err.errno === 1698 || err.errno === 45028 || - err.errno === 45025, + err.errno === 45025 || + err.errno === 45044, err.message ); }); }); it('create pool', function (done) { + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(5000); const pool = base.createPool({ connectionLimit: 1 }); const initTime = Date.now(); @@ -249,7 +262,7 @@ describe('Pool', () => { }); it('create pool with multipleStatement', function (done) { - if (process.env.SKYSQL) this.skip(); + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(5000); const pool = base.createPool({ connectionLimit: 5, @@ -259,7 +272,7 @@ describe('Pool', () => { .query('select 1; select 2') .then((results) => { //select 1 results - assert.deepEqual(results, [[{ '1': 1 }], [{ '2': 2 }]]); + assert.deepEqual(results, [[{ 1: 1 }], [{ 2: 2 }]]); pool.end(); done(); }) @@ -270,10 +283,14 @@ describe('Pool', () => { }); it('ensure commit', function (done) { - shareConn.query('DROP TABLE IF EXISTS ensureCommit'); - shareConn.query('CREATE TABLE ensureCommit(firstName varchar(32))'); shareConn - .query("INSERT INTO ensureCommit values ('john')") + .query('DROP TABLE IF EXISTS ensureCommit') + .then(() => { + return shareConn.query('CREATE TABLE ensureCommit(firstName varchar(32))'); + }) + .then(() => { + return shareConn.query("INSERT INTO ensureCommit values ('john')"); + }) .then((res) => { const pool = base.createPool({ connectionLimit: 1 }); pool.getConnection().then((conn) => { @@ -306,10 +323,14 @@ describe('Pool', () => { }); it('pool without control after use', function (done) { - shareConn.query('DROP TABLE IF EXISTS ensureCommit'); - shareConn.query('CREATE TABLE ensureCommit(firstName varchar(32))'); shareConn - .query("INSERT INTO ensureCommit values ('john')") + .query('DROP TABLE IF EXISTS ensureCommit') + .then(() => { + return shareConn.query('CREATE TABLE ensureCommit(firstName varchar(32))'); + }) + .then(() => { + return shareConn.query("INSERT INTO ensureCommit values ('john')"); + }) .then((res) => { const pool = base.createPool({ connectionLimit: 1, @@ -363,6 +384,7 @@ describe('Pool', () => { }); it('pool ending during requests', function (done) { + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(20000); const initial = new Date(); const pool = base.createPool({ connectionLimit: 1 }); @@ -471,7 +493,7 @@ describe('Pool', () => { }); it('pool getConnection timeout', function (done) { - if (process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPool({ connectionLimit: 1, acquireTimeout: 200 }); let errorThrown = false; pool @@ -495,7 +517,7 @@ describe('Pool', () => { }); it('pool leakDetectionTimeout timeout', function (done) { - if (process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPool({ connectionLimit: 1, acquireTimeout: 200, @@ -518,7 +540,7 @@ describe('Pool', () => { }); it('pool getConnection timeout recovery', function (done) { - if (process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); this.timeout(5000); const pool = base.createPool({ connectionLimit: 10, @@ -530,7 +552,6 @@ describe('Pool', () => { for (let i = 0; i < 10; i++) { pool.query('SELECT SLEEP(1)').catch((err) => { console.log('SLEEP ERROR'); - console.log(err); done(err); }); } @@ -571,6 +592,7 @@ describe('Pool', () => { }); it('pool query timeout', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); this.timeout(5000); const pool = base.createPool({ connectionLimit: 1, acquireTimeout: 500 }); const initTime = Date.now(); @@ -677,7 +699,7 @@ describe('Pool', () => { }); it('connection fail handling', function (done) { - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPool({ connectionLimit: 2, minDelayValidation: 200 @@ -721,7 +743,7 @@ describe('Pool', () => { }); it('query fail handling', function (done) { - if (process.env.MAXSCALE_VERSION || process.env.SKYSQL) this.skip(); + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPool({ connectionLimit: 2, minDelayValidation: 200 @@ -763,6 +785,7 @@ describe('Pool', () => { }); it('connection end', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPool({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -796,6 +819,7 @@ describe('Pool', () => { }); it('connection release alias', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPool({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -829,6 +853,7 @@ describe('Pool', () => { }); it('connection destroy', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE || process.env.SKYSQL) this.skip(); const pool = base.createPool({ connectionLimit: 2 }); setTimeout(() => { //check available connections in pool @@ -901,12 +926,17 @@ describe('Pool', () => { it('pool batch', function (done) { const pool = base.createPool({ connectionLimit: 1, resetAfterUse: false }); - pool.query('CREATE TEMPORARY TABLE parse(id int, id2 int, id3 int, t varchar(128), id4 int)'); pool - .batch('INSERT INTO `parse` values (1, ?, 2, ?, 3)', [ - [1, 'john'], - [2, 'jack'] - ]) + .query('DROP TABLE IF EXISTS parse') + .then(() => { + return pool.query('CREATE TABLE parse(id int, id2 int, id3 int, t varchar(128), id4 int)'); + }) + .then(() => { + return pool.batch('INSERT INTO `parse` values (1, ?, 2, ?, 3)', [ + [1, 'john'], + [2, 'jack'] + ]); + }) .then((res) => { assert.equal(res.affectedRows, 2); return pool.query('select * from `parse`'); @@ -938,9 +968,15 @@ describe('Pool', () => { it('pool batch single array', function (done) { const pool = base.createPool({ connectionLimit: 1, resetAfterUse: false }); - pool.query('CREATE TEMPORARY TABLE singleBatchArray(id int)'); + pool - .batch('INSERT INTO `singleBatchArray` values (?)', [1, 2, 3]) + .query('DROP TABLE IF EXISTS singleBatchArray') + .then(() => { + return pool.query('CREATE TABLE singleBatchArray(id int)'); + }) + .then(() => { + return pool.batch('INSERT INTO `singleBatchArray` values (?)', [1, 2, 3]); + }) .then((res) => { assert.equal(res.affectedRows, 3); return pool.query('select * from `singleBatchArray`'); @@ -966,6 +1002,7 @@ describe('Pool', () => { }); it("ensure pipe ending doesn't stall connection", function (done) { + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); //sequence engine only exist in MariaDB if (!shareConn.info.isMariaDB()) this.skip(); const ver = process.version.substring(1).split('.'); @@ -1008,48 +1045,59 @@ describe('Pool', () => { }); it('test minimum idle decrease', function (done) { - if (process.env.SKYSQL) this.skip(); + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(30000); const pool = base.createPool({ connectionLimit: 10, - minimumIdle: 4, + minimumIdle: 8, idleTimeout: 2, acquireTimeout: 20000 }); const requests = []; - for (let i = 0; i < 15000; i++) { + for (let i = 0; i < 5000; i++) { requests.push(pool.query('SELECT ' + i)); } - Promise.all(requests) - .then(() => { - setTimeout(() => { - assert.equal(pool.totalConnections(), 10); - assert.equal(pool.idleConnections(), 10); - }, 5); + setTimeout(() => { + Promise.all(requests) + .then(() => { + setTimeout(() => { + assert.isTrue( + pool.totalConnections() === 8 || + pool.totalConnections() === 9 || + pool.totalConnections() === 10 + ); + assert.isTrue( + pool.idleConnections() === 8 || + pool.idleConnections() === 9 || + pool.idleConnections() === 10 + ); + }, 5); - setTimeout(() => { - //wait for 1 second - assert.equal(pool.totalConnections(), 10); - assert.equal(pool.idleConnections(), 10); - }, 1000); + setTimeout(() => { + //wait for 1 second + assert.equal(pool.totalConnections(), 8); + assert.equal(pool.idleConnections(), 8); + }, 1000); - setTimeout(() => { - //minimumIdle-1 is possible after reaching idleTimeout and connection - // is still not recreated - assert.isTrue(pool.totalConnections() === 4 || pool.totalConnections() === 3); - assert.isTrue(pool.idleConnections() === 4 || pool.idleConnections() === 3); + setTimeout(() => { + //minimumIdle-1 is possible after reaching idleTimeout and connection + // is still not recreated + assert.isTrue(pool.totalConnections() === 8 || pool.totalConnections() === 7); + assert.isTrue(pool.idleConnections() === 8 || pool.idleConnections() === 7); + pool.end(); + done(); + }, 3000); + }) + .catch((err) => { pool.end(); - done(); - }, 3000); - }) - .catch((err) => { - pool.end(); - done(err); - }); + done(err); + }); + }, 4000); }); it('test minimum idle', function (done) { + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(5000); const pool = base.createPool({ connectionLimit: 10, @@ -1070,6 +1118,7 @@ describe('Pool', () => { }); it('pool immediate error', function (done) { + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); const pool = base.createPool({}); pool .getConnection() @@ -1086,6 +1135,7 @@ describe('Pool', () => { }); it('pool server defect timeout', function (done) { + if (process.env.SKYSQL || process.env.MAXSCALE_TEST_DISABLE) this.skip(); this.timeout(5000); const proxy = new Proxy({ port: Conf.baseConfig.port, diff --git a/test/integration/test-query-values-in-sql.js b/test/integration/test-query-values-in-sql.js index 6aed5445..eb3f3536 100644 --- a/test/integration/test-query-values-in-sql.js +++ b/test/integration/test-query-values-in-sql.js @@ -5,14 +5,28 @@ const { assert } = require('chai'); describe('sql template strings', () => { const value = "'`\\"; + it('query with parameters', (done) => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE parse(t varchar(128))'); - conn.query({ sql: 'INSERT INTO parse value (?)', values: [value] }); conn - .query({ sql: 'select * from parse where t = ?', values: [value] }) + .query('DROP TABLE IF EXISTS query_with_parameter') + .then(() => { + return conn.query('CREATE TABLE query_with_parameter(t varchar(128))'); + }) + .then(() => { + return conn.query({ + sql: 'INSERT INTO query_with_parameter value (?)', + values: [value] + }); + }) + .then(() => { + return conn.query({ + sql: 'select * from query_with_parameter where t = ?', + values: [value] + }); + }) .then((res) => { assert.strictEqual(res[0].t, value); conn.end(); @@ -27,16 +41,32 @@ describe('sql template strings', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE parse(t varchar(128))'); - conn.batch({ sql: 'INSERT INTO parse value (?)', values: [value] }); conn - .query({ sql: 'select * from parse where t = ?', values: [value] }) + .query('DROP TABLE IF EXISTS batch_with_parameters') + .then(() => { + return conn.query('CREATE TABLE batch_with_parameters(t varchar(128))'); + }) + .then(() => { + return conn.batch({ + sql: 'INSERT INTO batch_with_parameters value (?)', + values: [value] + }); + }) + .then(() => { + return conn.query({ + sql: 'select * from batch_with_parameters where t = ?', + values: [value] + }); + }) .then((res) => { assert.strictEqual(res[0].t, value); conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); @@ -47,15 +77,44 @@ describe('sql template strings', () => { if (err) { done(err); } else { - conn.query('CREATE TEMPORARY TABLE parse(t varchar(128))'); - conn.query({ sql: 'INSERT INTO parse value (?)', values: [value] }); - conn.query({ sql: 'select * from parse where t = ?', values: [value] }, (err, res) => { + conn.query('DROP TABLE IF EXISTS callback_with_parameters', (err) => { if (err) { + conn.end(); done(err); } else { - assert.strictEqual(res[0].t, value); - conn.end(); - done(); + conn.query('CREATE TABLE callback_with_parameters(t varchar(128))', (err) => { + if (err) { + conn.end(); + done(err); + } else { + conn.query( + { sql: 'INSERT INTO callback_with_parameters value (?)', values: [value] }, + (err) => { + if (err) { + conn.end(); + done(err); + } else { + conn.query( + { + sql: 'select * from callback_with_parameters where t = ?', + values: [value] + }, + (err, res) => { + if (err) { + conn.end(); + done(err); + } else { + assert.strictEqual(res[0].t, value); + conn.end(); + done(); + } + } + ); + } + } + ); + } + }); } }); } @@ -68,15 +127,44 @@ describe('sql template strings', () => { if (err) { done(err); } else { - conn.query('CREATE TEMPORARY TABLE parse(t varchar(128))'); - conn.batch({ sql: 'INSERT INTO parse value (?)', values: [value] }); - conn.query({ sql: 'select * from parse where t = ?', values: [value] }, (err, res) => { + conn.query('DROP TABLE IF EXISTS callback_batch_with_parameters', (err) => { if (err) { + conn.end(); done(err); } else { - assert.strictEqual(res[0].t, value); - conn.end(); - done(); + conn.query('CREATE TABLE callback_batch_with_parameters(t varchar(128))', (err) => { + if (err) { + conn.end(); + done(err); + } else { + conn.batch( + { sql: 'INSERT INTO callback_batch_with_parameters value (?)', values: [value] }, + (err) => { + if (err) { + conn.end(); + done(err); + } else { + conn.query( + { + sql: 'select * from callback_batch_with_parameters where t = ?', + values: [value] + }, + (err, res) => { + if (err) { + conn.end(); + done(err); + } else { + assert.strictEqual(res[0].t, value); + conn.end(); + done(); + } + } + ); + } + } + ); + } + }); } }); } @@ -86,20 +174,20 @@ describe('sql template strings', () => { it('pool query with parameters', (done) => { const pool = base.createPool(); pool - .query('drop table IF EXISTS pool_parse') + .query('drop table IF EXISTS pool_query_param') .catch((err) => {}) .then(() => { - return pool.query('CREATE TABLE pool_parse(t varchar(128))'); + return pool.query('CREATE TABLE pool_query_param(t varchar(128))'); }) .then(() => { - return pool.query({ sql: 'INSERT INTO pool_parse value (?)', values: [value] }); + return pool.query({ sql: 'INSERT INTO pool_query_param value (?)', values: [value] }); }) .then(() => { - return pool.query({ sql: 'select * from pool_parse where t = ?', values: [value] }); + return pool.query({ sql: 'select * from pool_query_param where t = ?', values: [value] }); }) .then((res) => { assert.strictEqual(res[0].t, value); - return pool.query('drop table pool_parse'); + return pool.query('drop table pool_query_param'); }) .then(() => { pool.end(); @@ -143,6 +231,7 @@ describe('sql template strings', () => { { sql: 'select * from pool_parse_call where t = ?', values: [value] }, (err, res) => { if (err) { + pool.end(); done(err); } else { assert.strictEqual(res[0].t, value); diff --git a/test/integration/test-query.js b/test/integration/test-query.js index 9ed0787c..af8d89bb 100644 --- a/test/integration/test-query.js +++ b/test/integration/test-query.js @@ -11,7 +11,7 @@ describe('basic query', () => { conn .query('select 1', [2]) .then((rows) => { - assert.deepEqual(rows, [{ '1': 1 }]); + assert.deepEqual(rows, [{ 1: 1 }]); conn.end(); done(); }) @@ -25,16 +25,26 @@ describe('basic query', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE parse(t varchar(128))'); - conn.query('INSERT INTO `parse` value (?)', value); conn - .query('select * from `parse` where t = ?', value) + .query('DROP TABLE IF EXISTS parse') + .then(() => { + return conn.query('CREATE TABLE parse(t varchar(128))'); + }) + .then(() => { + return conn.query('INSERT INTO `parse` value (?)', value); + }) + .then(() => { + return conn.query('select * from `parse` where t = ?', value); + }) .then((res) => { assert.strictEqual(res[0].t, value); conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); @@ -43,10 +53,19 @@ describe('basic query', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE arrayParam (id int, val varchar(10))'); - 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]]) + .query('DROP TABLE IF EXISTS arrayParam') + .then(() => { + return conn.query('CREATE TABLE arrayParam (id int, val varchar(10))'); + }) + .then(() => { + return conn.query( + "INSERT INTO arrayParam VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')" + ); + }) + .then(() => { + return conn.query('SELECT * FROM arrayParam WHERE val IN (?)', [['b', 'c', 1]]); + }) .then((rows) => { assert.deepEqual(rows, [ { @@ -61,7 +80,10 @@ describe('basic query', () => { conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); @@ -70,11 +92,20 @@ describe('basic query', () => { base .createConnection() .then((conn) => { - conn.query('CREATE TEMPORARY TABLE arrayParam (id int, val varchar(10))'); - conn.query('INSERT INTO arrayParam VALUES ?', [[1, null]]); - conn.query('INSERT INTO arrayParam VALUES ?', [[2, 'a']]); conn - .query('SELECT * FROM arrayParam') + .query('DROP TABLE IF EXISTS arrayParamNull') + .then(() => { + return conn.query('CREATE TABLE arrayParamNull (id int, val varchar(10))'); + }) + .then(() => { + return conn.query('INSERT INTO arrayParamNull VALUES (?)', [[1, null]]); + }) + .then(() => { + return conn.query('INSERT INTO arrayParamNull VALUES (?)', [[2, 'a']]); + }) + .then(() => { + return conn.query('SELECT * FROM arrayParamNull'); + }) .then((rows) => { assert.deepEqual(rows, [ { @@ -89,7 +120,50 @@ describe('basic query', () => { conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); + }) + .catch(done); + }); + + it('array parameter with null value with parenthesis', function (done) { + base + .createConnection({ arrayParenthesis: true }) + .then((conn) => { + conn + .query('DROP TABLE IF EXISTS arrayParamNullParen') + .then(() => { + return conn.query('CREATE TABLE arrayParamNullParen (id int, val varchar(10))'); + }) + .then(() => { + return conn.query('INSERT INTO arrayParamNullParen VALUES ?', [[1, null]]); + }) + .then(() => { + return conn.query('INSERT INTO arrayParamNullParen VALUES ?', [[2, 'a']]); + }) + .then(() => { + return conn.query('SELECT * FROM arrayParamNullParen'); + }) + .then((rows) => { + assert.deepEqual(rows, [ + { + id: 1, + val: null + }, + { + id: 2, + val: 'a' + } + ]); + conn.end(); + done(); + }) + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); @@ -99,16 +173,26 @@ describe('basic query', () => { base .createConnection({ permitSetMultiParamEntries: true }) .then((conn) => { - conn.query('CREATE TEMPORARY TABLE setTable(id int, val varchar(128))'); - conn.query('INSERT INTO setTable SET ?', jsonValue); conn - .query('select * from setTable') + .query('DROP TABLE IF EXISTS setTable') + .then(() => { + return conn.query('CREATE TABLE setTable(id int, val varchar(128))'); + }) + .then(() => { + return conn.query('INSERT INTO setTable SET ?', jsonValue); + }) + .then(() => { + return conn.query('select * from setTable'); + }) .then((res) => { assert.deepEqual(res[0], jsonValue); conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); @@ -191,24 +275,34 @@ describe('basic query', () => { base .createConnection() .then((conn) => { - conn.query( - "set @@SQL_MODE = 'ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'" - ); - conn.query('create TEMPORARY table h (c1 varchar(5))'); conn - .query("insert into h values ('123456')") + .query( + "set @@SQL_MODE = 'ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'" + ) + .then(() => { + return conn.query('DROP TABLE IF EXISTS h'); + }) + .then(() => { + return conn.query('create table h (c1 varchar(5))'); + }) + .then(() => { + return conn.query("insert into h values ('123456')"); + }) .then((res) => { assert.equal(res.warningStatus, 1); conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); it('255 columns', (done) => { - let table = 'CREATE TEMPORARY TABLE myTable('; + let table = 'CREATE TABLE myTable('; let insert = 'INSERT INTO myTable VALUES ('; let expRes = {}; for (let i = 0; i < 255; i++) { @@ -226,26 +320,43 @@ describe('basic query', () => { base .createConnection() .then((conn) => { - conn.query(table); - conn.query(insert); conn - .query('SELECT * FROM myTable') + .query('DROP TABLE IF EXISTS myTable') + .then(() => { + return conn.query(table); + }) + .then(() => { + return conn.query(insert); + }) + .then(() => { + return conn.query('SELECT * FROM myTable'); + }) .then((res) => { assert.deepEqual(res[0], expRes); conn.end(); done(); }) - .catch(done); + .catch((err) => { + conn.end(); + done(err); + }); }) .catch(done); }); it('escape validation', function (done) { if (!base.utf8Collation()) this.skip(); - shareConn.query('CREATE TEMPORARY TABLE tt1 (id int, tt varchar(256)) CHARSET utf8mb4'); - shareConn.query('INSERT INTO tt1 VALUES (?,?)', [1, 'jack\nkमस्']); shareConn - .query('SELECT * FROM tt1') + .query('DROP TABLE IF EXISTS tt1') + .then(() => { + return shareConn.query('CREATE TABLE tt1 (id int, tt varchar(256)) CHARSET utf8mb4'); + }) + .then(() => { + return shareConn.query('INSERT INTO tt1 VALUES (?,?)', [1, 'jack\nkमस्']); + }) + .then(() => { + return shareConn.query('SELECT * FROM tt1'); + }) .then((res) => { assert.equal(res[0].tt, 'jack\nkमस्'); done(); diff --git a/test/integration/test-socket.js b/test/integration/test-socket.js index ec602382..f89c17ab 100644 --- a/test/integration/test-socket.js +++ b/test/integration/test-socket.js @@ -7,7 +7,7 @@ const Conf = require('../conf'); describe('test socket', () => { it('named pipe', function (done) { if (process.platform !== 'win32') this.skip(); - if (process.env.MUST_USE_TCPIP) this.skip(); + if (process.env.MUST_USE_TCPIP || process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (Conf.baseConfig.host !== 'localhost' && Conf.baseConfig.host !== 'mariadb.example.com') this.skip(); const test = this; diff --git a/test/integration/test-ssl.js b/test/integration/test-ssl.js index b0734d25..ab4ecbc3 100644 --- a/test/integration/test-ssl.js +++ b/test/integration/test-ssl.js @@ -12,149 +12,137 @@ describe('ssl', function () { let clientCert = null; let clientKeystore = null; let sslEnable = false; + let sslPort = Conf.baseConfig.port; before(function (done) { - if (process.env.MAXSCALE_VERSION) { - done(); - } else { - if ( - tls.DEFAULT_MIN_VERSION === 'TLSv1.2' && - ((process.platform === 'win32' && - shareConn.info.isMariaDB() && - !shareConn.info.hasMinVersion(10, 4, 0)) || - (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(8, 0, 0))) - ) { - //TLSv1.2 is supported on windows only since MariaDB 10.4 - //TLSv1.2 is supported in MySQL only since 8.0 (unix/windows) - //so if testing with Node.js 12, force possible TLS1.1 - if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 0)) { - //MySQL 5.5 and MySQL 5.6 needs TLSv1 - tls.DEFAULT_MIN_VERSION = 'TLSv1'; - } else { - tls.DEFAULT_MIN_VERSION = 'TLSv1.1'; - } + if (process.env.TEST_SSL_PORT) sslPort = parseInt(process.env.TEST_SSL_PORT); + if ( + tls.DEFAULT_MIN_VERSION === 'TLSv1.2' && + ((process.platform === 'win32' && + shareConn.info.isMariaDB() && + !shareConn.info.hasMinVersion(10, 4, 0)) || + (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(8, 0, 0))) + ) { + //TLSv1.2 is supported on windows only since MariaDB 10.4 + //TLSv1.2 is supported in MySQL only since 8.0 (unix/windows) + //so if testing with Node.js 12, force possible TLS1.1 + if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 0)) { + //MySQL 5.5 and MySQL 5.6 needs TLSv1 + tls.DEFAULT_MIN_VERSION = 'TLSv1'; + } else { + tls.DEFAULT_MIN_VERSION = 'TLSv1.1'; } + } - let serverCaFile = - Conf.baseConfig.ssl && Conf.baseConfig.ssl.ca ? null : process.env.TEST_SSL_CA_FILE; - let clientKeyFileName = process.env.TEST_SSL_CLIENT_KEY_FILE; - let clientCertFileName = process.env.TEST_SSL_CLIENT_CERT_FILE; - let clientKeystoreFileName = process.env.TEST_SSL_CLIENT_KEYSTORE_FILE; - - if ( - !serverCaFile && - (Conf.baseConfig.host === 'localhost' || Conf.baseConfig.host === 'mariadb.example.com') - ) { - try { - if (fs.existsSync('../ssl')) { - serverCaFile = '../ssl/server.crt'; - clientKeyFileName = '../ssl/client.key'; - clientCertFileName = '../ssl/client.crt'; - clientKeystoreFileName = '../ssl/fullclient-keystore.p12'; - } - } catch (err) { - console.error(err); + let serverCaFile = + Conf.baseConfig.ssl && Conf.baseConfig.ssl.ca ? null : process.env.TEST_SSL_CA_FILE; + let clientKeyFileName = process.env.TEST_SSL_CLIENT_KEY_FILE; + let clientCertFileName = process.env.TEST_SSL_CLIENT_CERT_FILE; + let clientKeystoreFileName = process.env.TEST_SSL_CLIENT_KEYSTORE_FILE; + + if ( + !serverCaFile && + (Conf.baseConfig.host === 'localhost' || Conf.baseConfig.host === 'mariadb.example.com') + ) { + try { + if (fs.existsSync('../../ssl')) { + serverCaFile = '../../ssl/server.crt'; + clientKeyFileName = '../../ssl/client.key'; + clientCertFileName = '../../ssl/client.crt'; + clientKeystoreFileName = '../../ssl/fullclient-keystore.p12'; } + } catch (err) { + console.error(err); } + } - if (serverCaFile) ca = [fs.readFileSync(serverCaFile, 'utf8')]; - if (clientKeyFileName) clientKey = [fs.readFileSync(clientKeyFileName, 'utf8')]; - if (clientCertFileName) clientCert = [fs.readFileSync(clientCertFileName, 'utf8')]; - if (clientKeystoreFileName) clientKeystore = [fs.readFileSync(clientKeystoreFileName)]; - - shareConn.query("DROP USER 'sslTestUser'@'%'").catch((err) => {}); - shareConn.query("DROP USER 'X509testUser'@'%'").catch((err) => {}); + if (serverCaFile) ca = [fs.readFileSync(serverCaFile, 'utf8')]; + if (clientKeyFileName) clientKey = [fs.readFileSync(clientKeyFileName, 'utf8')]; + if (clientCertFileName) clientCert = [fs.readFileSync(clientCertFileName, 'utf8')]; + if (clientKeystoreFileName) clientKeystore = [fs.readFileSync(clientKeystoreFileName)]; - shareConn - .query( + shareConn + .query("DROP USER IF EXISTS 'sslTestUser'@'%'") + .then(() => { + return shareConn.query("DROP USER IF EXISTS 'X509testUser'@'%'"); + }) + .then(() => { + return shareConn.query( "CREATE USER 'sslTestUser'@'%' IDENTIFIED BY 'ytoKS@ç%ùed5' " + ((shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 2, 0)) || (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 7, 0)) ? ' REQUIRE SSL' : '') - ) - .then(() => { - return shareConn.query( - "GRANT SELECT ON *.* TO 'sslTestUser'@'%' " + - ((shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 2, 0)) || - (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 0)) - ? ' REQUIRE SSL' - : '') - ); - }) - .then(() => { + ); + }) + .then(() => { + return shareConn.query( + "GRANT SELECT ON *.* TO 'sslTestUser'@'%' " + + ((shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 2, 0)) || + (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 0)) + ? ' REQUIRE SSL' + : '') + ); + }) + .then(() => { + return shareConn.query( + "CREATE USER 'X509testUser'@'%' IDENTIFIED BY 'éà@d684SQpl¨^' " + + ((shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 2, 0)) || + (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 7, 0)) + ? ' REQUIRE X509' + : '') + ); + }) + .then(() => { + return shareConn.query( + "GRANT SELECT ON *.* TO 'X509testUser'@'%' " + + ((shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 2, 0)) || + (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 0)) + ? ' REQUIRE X509' + : '') + ); + }) + .then(() => { + if (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(8)) { return shareConn.query( - "CREATE USER 'X509testUser'@'%' IDENTIFIED BY 'éà@d684SQpl¨^' " + - ((shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 2, 0)) || - (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(5, 7, 0)) - ? ' REQUIRE X509' - : '') + "ALTER USER 'sslTestUser'@'%' IDENTIFIED WITH 'mysql_native_password' BY 'ytoKS@ç%ùed5'" ); - }) - .then(() => { + } + return shareConn.query("SET PASSWORD FOR 'sslTestUser'@'%' = PASSWORD('ytoKS@ç%ùed5')"); + }) + .then(() => { + if (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(8)) { return shareConn.query( - "GRANT SELECT ON *.* TO 'X509testUser'@'%' " + - ((shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(10, 2, 0)) || - (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 0)) - ? ' REQUIRE X509' - : '') + "ALTER USER 'X509testUser'@'%' IDENTIFIED WITH 'mysql_native_password' BY 'éà@d684SQpl¨^'" ); - }) - .then(() => { - if (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(8)) { - return shareConn.query( - "ALTER USER 'sslTestUser'@'%' IDENTIFIED WITH 'mysql_native_password' BY 'ytoKS@ç%ùed5'" - ); - } - return shareConn.query("SET PASSWORD FOR 'sslTestUser'@'%' = PASSWORD('ytoKS@ç%ùed5')"); - }) - .then(() => { - if (!shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(8)) { - return shareConn.query( - "ALTER USER 'X509testUser'@'%' IDENTIFIED WITH 'mysql_native_password' BY 'éà@d684SQpl¨^'" - ); - } - return shareConn.query("SET PASSWORD FOR 'X509testUser'@'%' = PASSWORD('éà@d684SQpl¨^')"); - }) - .then(() => { - return shareConn.query("SHOW VARIABLES LIKE 'have_ssl'"); - }) - .then((rows) => { - if (rows[0].Value === 'YES') { - sslEnable = true; - done(); - } else { - //ssl is not enable on database, skipping test. - shareConn - .query("SHOW VARIABLES LIKE '%ssl%'") - .then((rows) => { - // console.log("ssl is not enable on database, skipping test :"); - // for (let i = 0; i < rows.length; i++) { - // console.log(rows[0]["Variable_name"] + " = " + rows[0]["Value"]); - // } - done(); - }) - .catch(done); - } - }) - .catch(done); - } - }); - - after(function (done) { - if (process.env.MAXSCALE_VERSION) { - done(); - } else { - shareConn - .query("DROP USER 'sslTestUser'@'%'") - .then(() => { - return shareConn.query("DROP USER 'X509testUser'@'%'"); - }) - .then(() => { + } + return shareConn.query("SET PASSWORD FOR 'X509testUser'@'%' = PASSWORD('éà@d684SQpl¨^')"); + }) + .then(() => { + return shareConn.query('FLUSH PRIVILEGES'); + }) + .then(() => { + return shareConn.query("SHOW VARIABLES LIKE 'have_ssl'"); + }) + .then((rows) => { + if (rows[0].Value === 'YES') { + sslEnable = true; done(); - }) - .catch(done); - } + } else { + //ssl is not enable on database, skipping test. + shareConn + .query("SHOW VARIABLES LIKE '%ssl%'") + .then((rows) => { + // console.log("ssl is not enable on database, skipping test :"); + // for (let i = 0; i < rows.length; i++) { + // console.log(rows[0]["Variable_name"] + " = " + rows[0]["Value"]); + // } + done(); + }) + .catch(done); + } + }) + .catch(done); }); it('signed certificate error ', function (done) { @@ -163,9 +151,11 @@ describe('ssl', function () { .createConnection({ user: 'sslTestUser', password: 'ytoKS@ç%ùed5', - ssl: true + ssl: true, + port: sslPort }) - .then(() => { + .then((conn) => { + conn.end(); done(new Error('Must have thrown an exception !')); }) .catch((err) => { @@ -177,7 +167,7 @@ describe('ssl', function () { it('signed certificate forcing', function (done) { if (!sslEnable) this.skip(); base - .createConnection({ ssl: { rejectUnauthorized: false } }) + .createConnection({ ssl: { rejectUnauthorized: false }, port: sslPort }) .then((conn) => { conn.end(); done(); @@ -192,13 +182,17 @@ describe('ssl', function () { .createConnection({ user: 'sslTestUser', password: 'ytoKS@ç%ùed5', - ssl: { rejectUnauthorized: false } + ssl: { rejectUnauthorized: false }, + port: sslPort }) .then((conn) => { conn.end(); done(); }) - .catch(done); + .catch((err) => { + console.log(err); + done(err); + }); }); it('SSLv3 disable', function (done) { @@ -208,9 +202,11 @@ describe('ssl', function () { ssl: { rejectUnauthorized: false, secureProtocol: 'SSLv3_client_method' - } + }, + port: sslPort }) - .then(() => { + .then((conn) => { + conn.end(); done(new Error('Must have thrown an exception !')); }) .catch((err) => { @@ -223,9 +219,11 @@ describe('ssl', function () { if (!sslEnable) this.skip(); base .createConnection({ - ssl: { rejectUnauthorized: false, secureProtocol: 'SSLv2_method' } + ssl: { rejectUnauthorized: false, secureProtocol: 'SSLv2_method' }, + port: sslPort }) - .then(() => { + .then((conn) => { + conn.end(); done(new Error('Must have thrown an exception !')); }) .catch((err) => { @@ -246,7 +244,8 @@ describe('ssl', function () { } base .createConnection({ - ssl: { rejectUnauthorized: false, secureProtocol: 'TLSv1_method' } + ssl: { rejectUnauthorized: false, secureProtocol: 'TLSv1_method' }, + port: sslPort }) .then((conn) => { checkProtocol(conn, 'TLSv1'); @@ -268,7 +267,8 @@ describe('ssl', function () { } base .createConnection({ - ssl: { rejectUnauthorized: false, secureProtocol: 'TLSv1_1_method' } + ssl: { rejectUnauthorized: false, secureProtocol: 'TLSv1_1_method' }, + port: sslPort }) .then((conn) => { checkProtocol(conn, 'TLSv1.1'); @@ -296,7 +296,8 @@ describe('ssl', function () { secureProtocol: 'TLSv1_1_method', ciphers: 'DHE-RSA-AES256-SHA:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256' - } + }, + port: sslPort }) .then((conn) => { checkProtocol(conn, 'TLSv1.1'); @@ -324,9 +325,11 @@ describe('ssl', function () { rejectUnauthorized: false, secureProtocol: 'TLSv1_1_method', ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256' - } + }, + port: sslPort }) - .then(() => { + .then((conn) => { + conn.end(); done(new Error('Must have thrown an exception !')); }) .catch((err) => { @@ -351,9 +354,11 @@ describe('ssl', function () { rejectUnauthorized: false, secureProtocol: 'TLSv1_1_method', ciphers: 'ECDHE-ECDSA-AES256-STRANGE' - } + }, + port: sslPort }) - .then(() => { + .then((conn) => { + conn.end(); done(new Error('Must have thrown an exception !')); }) .catch((err) => { @@ -373,7 +378,8 @@ describe('ssl', function () { base .createConnection({ - ssl: { rejectUnauthorized: false, secureProtocol: 'TLSv1_2_method' } + ssl: { rejectUnauthorized: false, secureProtocol: 'TLSv1_2_method' }, + port: sslPort }) .then((conn) => { checkProtocol(conn, 'TLSv1.2'); @@ -384,6 +390,7 @@ describe('ssl', function () { }); it('TLSv1.2 with cipher working', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!sslEnable) this.skip(); //MariaDB server doesn't permit TLSv1.2 on windows //MySQL community version doesn't support TLSv1.2 @@ -399,7 +406,8 @@ describe('ssl', function () { secureProtocol: 'TLSv1_2_method', ciphers: 'DHE-RSA-AES256-SHA:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256' - } + }, + port: sslPort }) .then((conn) => { checkProtocol(conn, 'TLSv1.2'); @@ -422,7 +430,8 @@ describe('ssl', function () { checkServerIdentity: (servername, cert) => { return; } - } + }, + port: sslPort }) .then((conn) => { conn.end(); @@ -465,7 +474,7 @@ describe('ssl', function () { if (!shareConn.info.isMariaDB() && !shareConn.info.hasMinVersion(5, 7, 10)) this.skip(); base - .createConnection({ host: 'mariadb.example.com', ssl: { ca: ca } }) + .createConnection({ host: 'mariadb.example.com', ssl: { ca: ca }, port: sslPort }) .then((conn) => { const isWin = process.platform === 'win32'; let expectedProtocol = ['TLSv1.2', 'TLSv1.3']; @@ -492,10 +501,16 @@ describe('ssl', function () { user: 'X509testUser', password: 'éà@d684SQpl¨^', host: 'mariadb.example.com', - ssl: { ca: ca } + ssl: { ca: ca }, + port: sslPort }) - .then(() => { - done(new Error('Must have thrown an exception !')); + .then((conn) => { + conn.end(); + if (!process.env.MAXSCALE_TEST_DISABLE) { + done(new Error('Must have thrown an exception !')); + } else { + done(); + } }) .catch((err) => { done(); @@ -517,7 +532,8 @@ describe('ssl', function () { ca: ca, cert: clientCert, key: clientKey - } + }, + port: sslPort }) .then((conn) => { conn.end(); @@ -541,7 +557,8 @@ describe('ssl', function () { ca: ca, pfx: clientKeystore, passphrase: 'kspass' - } + }, + port: sslPort }) .then((conn) => { conn.end(); @@ -551,12 +568,16 @@ describe('ssl', function () { }); it('ssl change user', function (done) { + if (process.env.MAXSCALE_TEST_DISABLE) this.skip(); if (!shareConn.info.isMariaDB()) this.skip(); if (!sslEnable) this.skip(); let currUser; let conn; base - .createConnection({ ssl: { rejectUnauthorized: false } }) + .createConnection({ + ssl: { rejectUnauthorized: false }, + port: sslPort + }) .then((con) => { conn = con; conn.query("DROP USER IF EXISTS ChangeUser@'%'").catch((err) => {}); diff --git a/test/integration/test-streaming.js b/test/integration/test-streaming.js index 0c2695a0..9f718224 100644 --- a/test/integration/test-streaming.js +++ b/test/integration/test-streaming.js @@ -17,9 +17,12 @@ describe('streaming', () => { before(function (done) { this.timeout(20000); shareConn - .query( - 'CREATE TEMPORARY TABLE Streaming (id int NOT NULL AUTO_INCREMENT, b longblob, c varchar(10), d longblob, e varchar(10), PRIMARY KEY (id))' - ) + .query('DROP TABLE IF EXISTS Streaming') + .then(() => { + return shareConn.query( + 'CREATE TABLE Streaming (id int NOT NULL AUTO_INCREMENT, b longblob, c varchar(10), d longblob, e varchar(10), PRIMARY KEY (id))' + ); + }) .then(() => { return shareConn.query('SELECT @@max_allowed_packet as t'); }) @@ -39,10 +42,13 @@ describe('streaming', () => { it('Streaming url content', function (done) { this.timeout(30000); shareConn - .query( - 'CREATE TEMPORARY TABLE StreamingContent (id int NOT NULL AUTO_INCREMENT, b longblob, c' + - ' varchar(10), PRIMARY KEY (id))' - ) + .query('DROP TABLE IF EXISTS StreamingContent') + .then(() => { + return shareConn.query( + 'CREATE TABLE StreamingContent (id int NOT NULL AUTO_INCREMENT, b longblob, c' + + ' varchar(10), PRIMARY KEY (id))' + ); + }) .then(() => { const https = require('https'); https.get( @@ -139,7 +145,7 @@ describe('streaming', () => { this.timeout(20000); const r = fs.createReadStream(halfFileName); - let createTable = 'CREATE TEMPORARY TABLE Streaming2 (b longblob'; + let createTable = 'CREATE TABLE Streaming2 (b longblob'; let insertSql = 'insert into Streaming2 values(?'; const params = [r]; const max = 200; @@ -152,7 +158,10 @@ describe('streaming', () => { insertSql += ')'; shareConn - .query(createTable) + .query('DROP TABLE IF EXISTS Streaming2') + .then(() => { + return shareConn.query(createTable); + }) .then(() => { return shareConn.query(insertSql, params); }) diff --git a/test/integration/test-transaction.js b/test/integration/test-transaction.js index 0179a986..7b6f69b5 100644 --- a/test/integration/test-transaction.js +++ b/test/integration/test-transaction.js @@ -7,7 +7,10 @@ const { assert } = require('chai'); describe('transaction', () => { before((done) => { shareConn - .query('CREATE TEMPORARY TABLE testTransaction (v varchar(10))') + .query('DROP TABLE IF EXISTS testTransaction') + .then(() => { + return shareConn.query('CREATE TABLE testTransaction (v varchar(10))'); + }) .then(() => { done(); }) @@ -48,35 +51,44 @@ describe('transaction', () => { it('transaction rollback with callback', (done) => { const conn = base.createCallbackConnection(); conn.connect(function (err) { - if (err) return done(err); - conn.query('CREATE TEMPORARY TABLE testTransaction2 (v varchar(10))', (err) => { - if (err) return done(err); - conn.rollback((err) => { - if (err) return done(err); - conn.query('SET autocommit=0', (err) => { - if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); - assert.equal(conn.info.status & ServerStatus.STATUS_AUTOCOMMIT, 0); - conn.beginTransaction((err) => { + if (err) { + return done(err); + } else { + conn.query('DROP TABLE IF EXISTS testTransaction2', (err) => { + if (err) { + return done(err); + } else { + conn.query('CREATE TABLE testTransaction2 (v varchar(10))', (err) => { if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); - conn.query("INSERT INTO testTransaction2 values ('test')"); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); conn.rollback((err) => { if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); - conn.query('SELECT count(*) as nb FROM testTransaction2', (err, rows) => { + conn.query('SET autocommit=0', (err) => { if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); - assert.equal(rows[0].nb, 0); - conn.end(); - done(); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); + assert.equal(conn.info.status & ServerStatus.STATUS_AUTOCOMMIT, 0); + conn.beginTransaction((err) => { + if (err) return done(err); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); + conn.query("INSERT INTO testTransaction2 values ('test')"); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); + conn.rollback((err) => { + if (err) return done(err); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); + conn.query('SELECT count(*) as nb FROM testTransaction2', (err, rows) => { + if (err) return done(err); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); + assert.equal(rows[0].nb, 0); + conn.end(); + done(); + }); + }); + }); }); }); }); - }); + } }); - }); + } }); }); @@ -84,24 +96,30 @@ describe('transaction', () => { const conn = base.createCallbackConnection(); conn.connect(function (err) { if (err) return done(err); - conn.query('CREATE TEMPORARY TABLE testTransaction2 (v varchar(10))', (err) => { - if (err) return done(err); - conn.rollback(); - conn.query('SET autocommit=0', (err) => { - if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); - assert.equal(conn.info.status & ServerStatus.STATUS_AUTOCOMMIT, 0); - conn.beginTransaction(); - conn.query("INSERT INTO testTransaction2 values ('test')"); - conn.rollback(); - conn.query('SELECT count(*) as nb FROM testTransaction2', (err, rows) => { + conn.query('DROP TABLE IF EXISTS testTransaction2', (err) => { + if (err) { + return done(err); + } else { + conn.query('CREATE TABLE testTransaction2 (v varchar(10))', (err) => { if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); - assert.equal(rows[0].nb, 0); - conn.end(); - done(); + conn.rollback(); + conn.query('SET autocommit=0', (err) => { + if (err) return done(err); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); + assert.equal(conn.info.status & ServerStatus.STATUS_AUTOCOMMIT, 0); + conn.beginTransaction(); + conn.query("INSERT INTO testTransaction2 values ('test')"); + conn.rollback(); + conn.query('SELECT count(*) as nb FROM testTransaction2', (err, rows) => { + if (err) return done(err); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); + assert.equal(rows[0].nb, 0); + conn.end(); + done(); + }); + }); }); - }); + } }); }); }); @@ -140,43 +158,69 @@ describe('transaction', () => { it('transaction commit error handling', function (done) { let conn; - base - .createConnection() - .then((con) => { - conn = con; - conn.query('SET autocommit=0'); - conn.query('CREATE TEMPORARY TABLE testTransaction (v varchar(10))'); - return conn.query("INSERT INTO testTransaction values ('test')"); - }) - .then(() => { - process.nextTick( - conn.__tests.getSocket().destroy.bind(conn.__tests.getSocket(), new Error('close forced')) - ); - conn - .commit() - .then(() => { - done('must have thrown error !'); - conn.end(); - }) - .catch((err) => { - conn.end(); - done(); - }); - }) - .catch(done); + base.createConnection().then((con) => { + conn = con; + return conn + .query('SET autocommit=0') + .then(() => { + return conn.query('DROP TABLE IF EXISTS testTransaction1'); + }) + .then(() => { + return conn.query('CREATE TABLE testTransaction1 (v varchar(10))'); + }) + .then(() => { + return conn.query("INSERT INTO testTransaction1 values ('test')"); + }) + .then(() => { + process.nextTick( + conn.__tests + .getSocket() + .destroy.bind(conn.__tests.getSocket(), new Error('close forced')) + ); + conn + .commit() + .then(() => { + done('must have thrown error !'); + conn.end(); + }) + .catch((err) => { + conn.end(); + done(); + }); + }) + .catch(done); + }); }); it('transaction commit no callback with error', function (done) { const conn = base.createCallbackConnection(); conn.connect((err) => { - conn.query('SET autocommit=0'); - conn.query('CREATE TEMPORARY TABLE testTransaction (v varchar(10))'); - conn.query("INSERT INTO testTransaction values ('test')", (err) => { - process.nextTick( - conn.__tests.getSocket().destroy.bind(conn.__tests.getSocket(), new Error('close forced')) - ); - conn.commit(); - done(); + conn.query('SET autocommit=0', (err) => { + if (err) { + done(err); + } else { + conn.query('DROP TABLE IF EXISTS testTransaction2', (err) => { + if (err) { + done(err); + } else { + conn.query('CREATE TABLE testTransaction2 (v varchar(10))', (err) => { + if (err) { + done(err); + } else { + conn.query("INSERT INTO testTransaction2 values ('test')", (err) => { + process.nextTick( + conn.__tests + .getSocket() + .destroy.bind(conn.__tests.getSocket(), new Error('close forced')) + ); + conn.commit(); + done(); + }); + } + }); + } + }); + } }); }); }); @@ -184,14 +228,30 @@ describe('transaction', () => { it('transaction commit no callback success', function (done) { const conn = base.createCallbackConnection(); conn.connect((err) => { - conn.query('SET autocommit=0'); - conn.query('CREATE TEMPORARY TABLE testTransaction (v varchar(10))'); - conn.query("INSERT INTO testTransaction values ('test')", (err) => { - conn.commit(); - setTimeout(() => { - conn.end(); - done(); - }, 100); + conn.query('SET autocommit=0', (err) => { + if (err) { + done(err); + } else { + conn.query('DROP TABLE IF EXISTS testTransaction3', (err) => { + if (err) { + done(err); + } else { + conn.query('CREATE TABLE testTransaction3 (v varchar(10))', (err) => { + if (err) { + done(err); + } else { + conn.query("INSERT INTO testTransaction3 values ('test')", (err) => { + conn.commit(); + setTimeout(() => { + conn.end(); + done(); + }, 100); + }); + } + }); + } + }); + } }); }); }); @@ -225,34 +285,40 @@ describe('transaction', () => { const conn = base.createCallbackConnection(); conn.connect((err) => { if (err) return done(err); - conn.query('CREATE TEMPORARY TABLE testTransaction (v varchar(10))', (err) => { - if (err) return done(err); - conn.commit((err) => { - if (err) return done(err); - conn.query('SET autocommit=0', (err) => { + conn.query('DROP TABLE IF EXISTS testTransaction4', (err) => { + if (err) { + return done(err); + } else { + conn.query('CREATE TABLE testTransaction4 (v varchar(10))', (err) => { if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); - assert.equal(conn.info.status & ServerStatus.STATUS_AUTOCOMMIT, 0); - conn.beginTransaction((err) => { + conn.commit((err) => { if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); - conn.query("INSERT INTO testTransaction values ('test')", (err) => { + conn.query('SET autocommit=0', (err) => { if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); - conn.commit((err) => { + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); + assert.equal(conn.info.status & ServerStatus.STATUS_AUTOCOMMIT, 0); + conn.beginTransaction((err) => { if (err) return done(err); - assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); - conn.query('SELECT count(*) as nb FROM testTransaction', (err, rows) => { + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); + conn.query("INSERT INTO testTransaction4 values ('test')", (err) => { if (err) return done(err); assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); - assert.equal(rows[0].nb, 1); - conn.end(done); + conn.commit((err) => { + if (err) return done(err); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 0); + conn.query('SELECT count(*) as nb FROM testTransaction4', (err, rows) => { + if (err) return done(err); + assert.equal(conn.info.status & ServerStatus.STATUS_IN_TRANS, 1); + assert.equal(rows[0].nb, 1); + conn.end(done); + }); + }); }); }); }); }); }); - }); + } }); }); }); diff --git a/test/integration/test-typecast.js b/test/integration/test-typecast.js index bd005802..086f8819 100644 --- a/test/integration/test-typecast.js +++ b/test/integration/test-typecast.js @@ -105,7 +105,10 @@ describe('TypeCast', () => { .createConnection({ typeCast: tinyToBoolean }) .then((conn) => { conn - .query('CREATE TEMPORARY TABLE tinyToBool(b1 TINYINT(1), b2 TINYINT(2))') + .query('DROP TABLE IF EXISTS tinyToBool') + .then(() => { + return conn.query('CREATE TABLE tinyToBool(b1 TINYINT(1), b2 TINYINT(2))'); + }) .then(() => { return conn.query('INSERT INTO tinyToBool VALUES (0,0), (1,1), (2,2), (null,null)'); }) @@ -141,7 +144,10 @@ describe('TypeCast', () => { .createConnection({ typeCast: longCast }) .then((conn) => { conn - .query('CREATE TEMPORARY TABLE stupidCast(b1 TINYINT(1), b2 varchar(3))') + .query('DROP TABLE IF EXISTS stupidCast') + .then(() => { + return conn.query('CREATE TABLE stupidCast(b1 TINYINT(1), b2 varchar(3))'); + }) .then(() => { return conn.query( "INSERT INTO stupidCast VALUES (0,'0.1'), (1,'1.1')," + " (2,'2.2'), (null,null)" @@ -177,7 +183,10 @@ describe('TypeCast', () => { .createConnection({ typeCast: longCast }) .then((conn) => { conn - .query('CREATE TEMPORARY TABLE stupidCast(b1 varchar(100))') + .query('DROP TABLE IF EXISTS stupidCast') + .then(() => { + return conn.query('CREATE TABLE stupidCast(b1 varchar(100))'); + }) .then(() => { return conn.query( "INSERT INTO stupidCast VALUES ('1999-01-31" + @@ -208,7 +217,10 @@ describe('TypeCast', () => { .createConnection({ typeCast: longCast }) .then((conn) => { conn - .query('CREATE TEMPORARY TABLE stupidCast(b1 POINT)') + .query('DROP TABLE IF EXISTS stupidCast') + .then(() => { + return conn.query('CREATE TABLE stupidCast(b1 POINT)'); + }) .then(() => { return conn.query('INSERT INTO stupidCast VALUES (?), (?),(null)', [ { @@ -240,7 +252,9 @@ describe('TypeCast', () => { }, { b1: - shareConn.info.isMariaDB() && shareConn.info.hasMinVersion(10, 5, 2) + shareConn.info.isMariaDB() && + shareConn.info.hasMinVersion(10, 5, 2) && + !process.env.MAXSCALE_TEST_DISABLE ? { type: 'Point' } : null } diff --git a/test/unit/config/test-connection-options.js b/test/unit/config/test-connection-options.js index 8d9c5660..e1fac305 100644 --- a/test/unit/config/test-connection-options.js +++ b/test/unit/config/test-connection-options.js @@ -107,6 +107,7 @@ describe('test connection options', () => { assert.equal(result.database, 'шdb'); assert.equal(result.host, 'example.com'); assert.equal(result.password, 'p@ssword'); + assert.equal(result.keepAliveDelay, 0); assert.equal(result.port, 3307); assert.equal(result.user, 'rootå'); assert.deepEqual(result.connectAttributes, { par1: 'bouh', par2: 'bla' }); @@ -226,13 +227,14 @@ describe('test connection options', () => { it('with options', () => { const result = ConnOptions.parse( - 'mariadb://root:pass@localhost:3307/db?metaAsArray=false&ssl=true&dateStrings=true&collation=latin1_swedish_ci&maxAllowedPacket=1048576&permitSetMultiParamEntries=true' + 'mariadb://root:pass@localhost:3307/db?metaAsArray=false&ssl=true&dateStrings=true&collation=latin1_swedish_ci&maxAllowedPacket=1048576&permitSetMultiParamEntries=true&keepAliveDelay=1000' ); assert.deepEqual(result, { database: 'db', dateStrings: true, host: 'localhost', metaAsArray: false, + keepAliveDelay: 1000, password: 'pass', port: 3307, ssl: true, diff --git a/types/index.d.ts b/types/index.d.ts index 042a40a5..ca366bcd 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -// Type definitions for mariadb 2.0 +// Type definitions for mariadb 2.5 // Project: https://github.com/mariadb-corporation/mariadb-connector-nodejs // Definitions by: Diego Dupin // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped @@ -228,9 +228,12 @@ export interface ConnectionConfig extends UserConnectionConfig, QueryConfig { multipleStatements?: boolean; /** - * object with ssl parameters or a string containing name of ssl profile + * object with ssl parameters or a boolean to enable ssl without setting any other ssl option. + * see + * https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/documentation/connection-options.md#ssl + * for more information */ - ssl?: string | (tls.SecureContextOptions & { rejectUnauthorized?: boolean }); + ssl?: boolean | (tls.SecureContextOptions & { rejectUnauthorized?: boolean }); /** * Compress exchanges with database using gzip. @@ -275,6 +278,51 @@ export interface ConnectionConfig extends UserConnectionConfig, QueryConfig { * Example: sessionVariables:{'idle_transaction_timeout':10000} */ sessionVariables?: any; + + /** + * Indicate if array are included in parenthesis. This option permit compatibility with version < 2.5 + */ + arrayParenthesis?: boolean; + + /** + * indicate if JSON fields for MariaDB server 10.5.2+ results in JSON format (or String if disabled) + */ + autoJsonMap?: boolean; + + /** + * permit to enable socket keep alive, setting delay. 0 means not enabled. Keep in mind that this don't reset server [@@wait_timeout](https://mariadb.com/kb/en/library/server-system-variables/#wait_timeout) (use pool option idleTimeout for that). + * in ms + * (Default: 0) + */ + keepAliveDelay?: number; + + /** + * Indicate path/content to MySQL server RSA public key. + * use requires Node.js v11.6+ + */ + rsaPublicKey?: string; + + /** + * Indicate path/content to MySQL server caching RSA public key. + * use requires Node.js v11.6+ + */ + cachingRsaPublicKey?: string; + + /** + * Indicate that if `rsaPublicKey` or `cachingRsaPublicKey` public key are not provided, if client can ask server to send public key. + * default: false + */ + allowPublicKeyRetrieval?: boolean; + + /** + * Whether resultset should return javascript ES2020 [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) + * for [BIGINT](https://mariadb.com/kb/en/bigint/) data type. + * This ensures having expected value even for value > 2^53 + * (see [safe](documentation/connection-options.md#support-for-big-integer) range). + * + * default false + */ + supportBigInt?: boolean; } export interface PoolConfig extends ConnectionConfig { diff --git a/types/mariadb-tests.ts b/types/mariadb-tests.ts index 9494e417..32f2d0ee 100644 --- a/types/mariadb-tests.ts +++ b/types/mariadb-tests.ts @@ -24,8 +24,30 @@ function createPoolConfig(options?: PoolConfig): mariadb.PoolConfig { ); } +function createPoolConfigWithSSl(options?: PoolConfig): mariadb.PoolConfig { + Object.assign( + { + host: baseConfig.host, + user: baseConfig.user, + password: baseConfig.password, + ssl: true + }, + options + ); + return Object.assign( + { + host: baseConfig.host, + user: baseConfig.user, + password: baseConfig.password, + ssl: { ca: 'fff' } + }, + options + ); +} + function createPool(options?: unknown): mariadb.Pool { - return mariadb.createPool(createPoolConfig(options)); + mariadb.createPool(createPoolConfig(options)); + return mariadb.createPool(createPoolConfigWithSSl(options)); } async function testMisc(): Promise {