From 57620cfcbcf46627860c0e86a7e9f8ebe023bdc8 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Tue, 18 Jul 2023 00:10:24 +0300 Subject: [PATCH 01/65] add mssql dev dependency. --- package-lock.json | 1530 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 + 2 files changed, 1507 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1e2334020..dba37b84b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "kysely", - "version": "0.25.0", + "version": "0.26.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kysely", - "version": "0.25.0", + "version": "0.26.1", "license": "MIT", "devDependencies": { "@types/better-sqlite3": "^7.6.4", @@ -14,6 +14,7 @@ "@types/chai-as-promised": "^7.1.5", "@types/chai-subset": "^1.3.3", "@types/mocha": "^10.0.1", + "@types/mssql": "^8.1.2", "@types/node": "^18.15.11", "@types/pg": "^8.10.2", "@types/pg-cursor": "^2.7.0", @@ -25,6 +26,7 @@ "concurrently": "^8.1.0", "esbuild": "^0.17.19", "mocha": "^10.2.0", + "mssql": "^9.1.1", "mysql2": "^3.3.3", "pg": "^8.11.0", "pg-cursor": "^2.10.0", @@ -39,6 +41,249 @@ "node": ">=14.0.0" } }, + "node_modules/@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "dev": true, + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.3.tgz", + "integrity": "sha512-kleJ1iUTxcO32Y06dH9Pfi9K4U+Tlb111WXEnbt7R/ne+NLRwppZiTGJuTD5VVoxTMK5NTbEtm5t2vcdNCFe2g==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz", + "integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^1.0.4", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.3.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.3.tgz", + "integrity": "sha512-ubkOf2YCnVtq7KqEJQqAI8dDD5rH1M6OP5kW0KO/JQyTaxLA0N0pjFWvvaysCj9eHMNBcuuoZXhhl0ypjod2DA==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "dev": true, + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.11.0.tgz", + "integrity": "sha512-nB4KXl6qAyJmBVLWA7SakT4tzpYZTCk4pvRBeI+Ye0WYSOrlTqlMhc4MSS/8atD3ufeYWdkN380LLoXlUUzThw==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.3.0", + "@azure/logger": "^1.0.0", + "form-data": "^4.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", + "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", + "dev": true, + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz", + "integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-2.1.0.tgz", + "integrity": "sha512-BPDz1sK7Ul9t0l9YKLEa8PHqWU4iCfhGJ+ELJl6c8CP3TpJt2urNCbm0ZHsthmxRsYoMPbz2Dvzj30zXZVmAFw==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.4.0", + "@azure/core-rest-pipeline": "^1.1.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^2.26.0", + "@azure/msal-common": "^7.0.0", + "@azure/msal-node": "^1.10.0", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^8.0.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@azure/keyvault-keys": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.7.1.tgz", + "integrity": "sha512-zfmlZQCw1Yz+aPhgZmWOYBUzaKmfBzR2yceAE4S6hKDl7YZraTguuXmtFbCqjRvpz+pIMKAK25fENay9mFy1hQ==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-http-compat": "^1.3.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "dev": true, + "dependencies": { + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "2.38.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.38.0.tgz", + "integrity": "sha512-gxBh83IumHgEP9uMCm9pJLKLRwICMQTxG9TX3AytdNt3oLUI3tytm/szYD5u5zKJgSkhHvwFSM+NPnM04hYw3w==", + "dev": true, + "dependencies": { + "@azure/msal-common": "13.2.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-browser/node_modules/@azure/msal-common": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.2.0.tgz", + "integrity": "sha512-rnstQ7Zgn3fSTKNQO+/YNV34/QXJs0vni7IA0/3QB1EEyrJg14xyRmTqlw9ta+pdSuT5OJwUP8kI3D/rBwUIBw==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-7.6.0.tgz", + "integrity": "sha512-XqfbglUTVLdkHQ8F9UQJtKseRr3sSnr9ysboxtoswvaMVaEfvyLtMoHv9XdKUfOc0qKGzNgRFd9yRjIWVepl6Q==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.18.0.tgz", + "integrity": "sha512-N6GX1Twxw524e7gaJvj7hKtrPRmZl9qGY7U4pmUdx4XzoWYRFfYk4H1ZjVhQ7pwb5Ks88NNhbXVCagsuYPTEFg==", + "dev": true, + "dependencies": { + "@azure/msal-common": "13.2.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": "10 || 12 || 14 || 16 || 18" + } + }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.2.0.tgz", + "integrity": "sha512-rnstQ7Zgn3fSTKNQO+/YNV34/QXJs0vni7IA0/3QB1EEyrJg14xyRmTqlw9ta+pdSuT5OJwUP8kI3D/rBwUIBw==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", @@ -521,6 +766,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@js-joda/core": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.5.3.tgz", + "integrity": "sha512-7dqNYwG8gCt4hfg5PKgM7xLEcgSBcx/UgC92OMnhMmvAnq11QzDFPrxUkNR/u5kn17WWLZ8beZ4A3Qrz4pZcmQ==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -606,6 +857,21 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "node_modules/@tediousjs/connection-string": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.4.2.tgz", + "integrity": "sha512-1R9UC7Qc5wief2oJL+c1+d7v1/oPBayL85u8L/jV2DzIKput1TZ8ZUjj2nxQaSfzu210zp0oFWUrYUiUs8NhBQ==", + "dev": true + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/@tsd/typescript": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@tsd/typescript/-/typescript-5.0.4.tgz", @@ -679,6 +945,17 @@ "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, + "node_modules/@types/mssql": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-8.1.2.tgz", + "integrity": "sha512-hoDM+mZUClfXu0J1pyVdbhv2Ve0dl0TdagAE3M5rd1slqoVEEHuNObPD+giwtJgyo99CcS58qbF9ektVKdxSfQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tedious": "*", + "tarn": "^3.0.1" + } + }, "node_modules/@types/node": { "version": "18.16.16", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.16.tgz", @@ -727,6 +1004,27 @@ "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", "dev": true }, + "node_modules/@types/tedious": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.9.tgz", + "integrity": "sha512-ipwFvfy9b2m0gjHsIX0D6NAAwGCKokzf5zJqUZHUGt+7uWVlBIy6n2eyMgiKQ8ChLFVxic/zwQUhjLYNzbHDRA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -800,6 +1098,19 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -809,6 +1120,26 @@ "node": ">=8" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -827,6 +1158,24 @@ "node": "*" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -944,6 +1293,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true + }, "node_modules/buffer-writer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", @@ -953,6 +1308,19 @@ "node": ">=4" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1120,6 +1488,27 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1262,6 +1651,40 @@ "node": ">=4.0.0" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -1310,6 +1733,15 @@ "node": ">=8" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1334,33 +1766,138 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "node_modules/es-abstract": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-aggregate-error": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.9.tgz", + "integrity": "sha512-fvnX40sb538wdU6r4s35cq4EY6Lr09Upj40BEVem4LEsuW8XgQep9yD5Q1U2KftokNp1rWODFJ2qwZSsAjFpbg==", "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "function-bind": "^1.1.1", + "functions-have-names": "^1.2.3", + "get-intrinsic": "^1.1.3", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0" }, "engines": { - "node": ">=12" + "node": ">= 0.4" }, - "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", @@ -1420,6 +1957,15 @@ "integrity": "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==", "dev": true }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -1497,6 +2043,29 @@ "flat": "cli.js" } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -1529,6 +2098,33 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -1556,6 +2152,37 @@ "node": "*" } }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -1616,6 +2243,21 @@ "node": "*" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -1636,6 +2278,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -1657,6 +2311,15 @@ "node": ">= 0.4.0" } }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1666,6 +2329,57 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1699,6 +2413,33 @@ "node": ">=10" } }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -1771,6 +2512,20 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/irregular-plurals": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", @@ -1780,12 +2535,38 @@ "node": ">=8" } }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1798,6 +2579,34 @@ "node": ">=8" } }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", @@ -1810,6 +2619,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1840,6 +2679,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1849,6 +2700,21 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -1864,6 +2730,83 @@ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", "dev": true }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -1876,6 +2819,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -1906,6 +2873,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "dev": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1924,6 +2897,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -1936,12 +2915,70 @@ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", "dev": true }, + "node_modules/jsonwebtoken": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", + "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", + "dev": true, + "dependencies": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", "dev": true }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dev": true, + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -2114,6 +3151,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -2251,6 +3309,26 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/mssql": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-9.1.1.tgz", + "integrity": "sha512-m0yTx9xzUtTvJpWJHqknUXUDPRnJXZYOOFNygnNIXn1PBkLsC/rkXQdquObd+M0ZPlBhGC00Jg28zG0wCl7VWg==", + "dev": true, + "dependencies": { + "@tediousjs/connection-string": "^0.4.1", + "commander": "^9.4.0", + "debug": "^4.3.3", + "rfdc": "^1.3.0", + "tarn": "^3.0.2", + "tedious": "^15.0.1" + }, + "bin": { + "mssql": "bin/mssql" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mysql2": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.3.3.tgz", @@ -2309,6 +3387,12 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "dev": true }, + "node_modules/native-duplexpair": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", + "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==", + "dev": true + }, "node_modules/nise": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", @@ -2343,6 +3427,12 @@ "node": ">=10" } }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true + }, "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -2367,6 +3457,42 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -2382,6 +3508,23 @@ "wrappy": "1" } }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2839,6 +3982,15 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -3081,6 +4233,23 @@ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -3117,6 +4286,12 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -3149,6 +4324,30 @@ "tslib": "^2.1.0" } }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3169,6 +4368,20 @@ } ] }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3238,6 +4451,20 @@ "vscode-textmate": "^8.0.0" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -3378,6 +4605,12 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + }, "node_modules/sqlstring": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", @@ -3387,6 +4620,16 @@ "node": ">= 0.6" } }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "dev": true, + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -3410,6 +4653,51 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3526,6 +4814,73 @@ "node": ">=6" } }, + "node_modules/tarn": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", + "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/tedious": { + "version": "15.1.3", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-15.1.3.tgz", + "integrity": "sha512-166EpRm5qknwhEisjZqz/mF7k14fXKJYHRg6XiAXVovd/YkyHJ3SG4Ppy89caPaNFfRr7PVYe+s4dAvKaCMFvw==", + "dev": true, + "dependencies": { + "@azure/identity": "^2.0.4", + "@azure/keyvault-keys": "^4.4.0", + "@js-joda/core": "^5.2.0", + "bl": "^5.0.0", + "es-aggregate-error": "^1.0.8", + "iconv-lite": "^0.6.3", + "js-md4": "^0.3.2", + "jsbi": "^4.3.0", + "native-duplexpair": "^1.0.0", + "node-abort-controller": "^3.0.1", + "punycode": "^2.1.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/tedious/node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tedious/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3616,6 +4971,71 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typedoc": { "version": "0.24.8", "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", @@ -3665,12 +5085,36 @@ "node": ">=14.17" } }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -3693,6 +5137,42 @@ "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.10.tgz", + "integrity": "sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/package.json b/package.json index 84836375c..c305ee7ef 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@types/chai-as-promised": "^7.1.5", "@types/chai-subset": "^1.3.3", "@types/mocha": "^10.0.1", + "@types/mssql": "^8.1.2", "@types/node": "^18.15.11", "@types/pg": "^8.10.2", "@types/pg-cursor": "^2.7.0", @@ -79,6 +80,7 @@ "concurrently": "^8.1.0", "esbuild": "^0.17.19", "mocha": "^10.2.0", + "mssql": "^9.1.1", "mysql2": "^3.3.3", "pg": "^8.11.0", "pg-cursor": "^2.10.0", From c84ea108cc3198afc7dc8f2c84157ff31fbb30f0 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Tue, 18 Jul 2023 23:38:20 +0300 Subject: [PATCH 02/65] add mssql to docker-compose.yml. --- docker-compose.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6b24bd0f8..7ec4dd8fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,26 @@ version: '3' services: + # credit to Knex.js team for the following mssql setup here: + mssql: + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - '21433:1433' + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=KyselyTest0 + healthcheck: + test: /opt/mssql-tools/bin/sqlcmd -S mssql -U sa -P 'KyselyTest0' -Q 'select 1' + waitmssql: + image: mcr.microsoft.com/mssql/server:2017-latest + links: + - mssql + depends_on: + - mssql + entrypoint: + - bash + - -c + # https://docs.microsoft.com/en-us/sql/relational-databases/logs/control-transaction-durability?view=sql-server-ver15#bkmk_DbControl + - 'until /opt/mssql-tools/bin/sqlcmd -S mssql -U sa -P KyselyTest0 -d master -Q "CREATE DATABASE kysely_test; ALTER DATABASE kysely_test SET ALLOW_SNAPSHOT_ISOLATION ON; ALTER DATABASE kysely_test SET DELAYED_DURABILITY = FORCED"; do sleep 5; done' mysql: image: 'mysql/mysql-server' environment: @@ -17,4 +38,4 @@ services: POSTGRES_USER: kysely POSTGRES_HOST_AUTH_METHOD: trust ports: - - '5434:5432' \ No newline at end of file + - '5434:5432' From d4d7ab9c33ff2451518ec7cf2dc04c5eea659d8a Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Wed, 19 Jul 2023 01:41:49 +0300 Subject: [PATCH 03/65] config? @ pg driver. --- src/dialect/postgres/postgres-driver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dialect/postgres/postgres-driver.ts b/src/dialect/postgres/postgres-driver.ts index ae070d1f6..829dc8619 100644 --- a/src/dialect/postgres/postgres-driver.ts +++ b/src/dialect/postgres/postgres-driver.ts @@ -43,7 +43,7 @@ export class PostgresDriver implements Driver { // The driver must take care of calling `onCreateConnection` when a new // connection is created. The `pg` module doesn't provide an async hook // for the connection creation. We need to call the method explicitly. - if (this.#config?.onCreateConnection) { + if (this.#config.onCreateConnection) { await this.#config.onCreateConnection(connection) } } From e51b57d3aac725ec202e0a68f87b4bbc0fa2fbbc Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Wed, 19 Jul 2023 03:38:52 +0300 Subject: [PATCH 04/65] initial dialect implementation. --- src/dialect/mssql/mssql-adapter.ts | 33 +++ src/dialect/mssql/mssql-dialect-config.ts | 26 +++ src/dialect/mssql/mssql-dialect.ts | 35 +++ src/dialect/mssql/mssql-driver.ts | 263 ++++++++++++++++++++++ src/dialect/mssql/mssql-introspector.ts | 32 +++ src/dialect/mssql/mssql-query-compiler.ts | 3 + src/index.ts | 7 + src/util/object-utils.ts | 2 +- 8 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 src/dialect/mssql/mssql-adapter.ts create mode 100644 src/dialect/mssql/mssql-dialect-config.ts create mode 100644 src/dialect/mssql/mssql-dialect.ts create mode 100644 src/dialect/mssql/mssql-driver.ts create mode 100644 src/dialect/mssql/mssql-introspector.ts create mode 100644 src/dialect/mssql/mssql-query-compiler.ts diff --git a/src/dialect/mssql/mssql-adapter.ts b/src/dialect/mssql/mssql-adapter.ts new file mode 100644 index 000000000..2d186ef6f --- /dev/null +++ b/src/dialect/mssql/mssql-adapter.ts @@ -0,0 +1,33 @@ +import { Kysely } from '../../kysely.js' +import { DEFAULT_MIGRATION_TABLE } from '../../migration/migrator.js' +import { sql } from '../../raw-builder/sql.js' +import { DialectAdapterBase } from '../dialect-adapter-base.js' + +export class MssqlAdapter extends DialectAdapterBase { + get supportsTransactionalDdl(): boolean { + return true + } + + get supportsReturning(): boolean { + // mssql supports returning with the output clause + return true + } + + async acquireMigrationLock(db: Kysely): Promise { + // Acquire a transaction-level exclusive lock on the migrations table. + // https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-ver16 + const result = await sql`exec sp_getapplock @DbPrincipal = ${sql.lit( + 'dbo' + )}, @Resource = ${sql.lit(DEFAULT_MIGRATION_TABLE)}, @LockMode = ${sql.lit( + 'Exclusive' + )}`.execute(db) + + console.log('sp_getapplock result', result) + } + + async releaseMigrationLock(): Promise { + // Nothing to do here. `sp_getapplock` is automatically released at the + // end of the transaction and since `supportsTransactionalDdl` true, we know + // the `db` instance passed to acquireMigrationLock is actually a transaction. + } +} diff --git a/src/dialect/mssql/mssql-dialect-config.ts b/src/dialect/mssql/mssql-dialect-config.ts new file mode 100644 index 000000000..ec6d0481b --- /dev/null +++ b/src/dialect/mssql/mssql-dialect-config.ts @@ -0,0 +1,26 @@ +import { ConnectionPool } from 'mssql' +import { DatabaseConnection } from '../../driver/database-connection.js' + +export interface MssqlDialectConfig { + /** + * A mssql Pool instance or a function that returns one. + * + * If a function is provided, it's called once when the first query is executed. + * + * https://github.com/sidorares/node-mysql2#using-connection-pools + */ + pool: ConnectionPool | (() => Promise) + + /** + * Called once for each created connection. + */ + onCreateConnection?: (connection: DatabaseConnection) => Promise +} + +export interface MssqlPool { + // TODO: ... +} + +export interface MssqlPoolConnection { + // TODO: ... +} diff --git a/src/dialect/mssql/mssql-dialect.ts b/src/dialect/mssql/mssql-dialect.ts new file mode 100644 index 000000000..26f92e284 --- /dev/null +++ b/src/dialect/mssql/mssql-dialect.ts @@ -0,0 +1,35 @@ +import { Driver } from '../../driver/driver.js' +import { Kysely } from '../../kysely.js' +import { QueryCompiler } from '../../query-compiler/query-compiler.js' +import { DatabaseIntrospector } from '../database-introspector.js' +import { DialectAdapter } from '../dialect-adapter.js' +import { Dialect } from '../dialect.js' +import { MssqlAdapter } from './mssql-adapter.js' +import { MssqlDialectConfig } from './mssql-dialect-config.js' +import { MssqlDriver } from './mssql-driver.js' +import { MssqlIntrospector } from './mssql-introspector.js' +import { MssqlQueryCompiler } from './mssql-query-compiler.js' + +export class MssqlDialect implements Dialect { + readonly #config: MssqlDialectConfig + + constructor(config: MssqlDialectConfig) { + this.#config = config + } + + createDriver(): Driver { + return new MssqlDriver(this.#config) + } + + createQueryCompiler(): QueryCompiler { + return new MssqlQueryCompiler() + } + + createAdapter(): DialectAdapter { + return new MssqlAdapter() + } + + createIntrospector(db: Kysely): DatabaseIntrospector { + return new MssqlIntrospector(db) + } +} diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts new file mode 100644 index 000000000..df43bd68e --- /dev/null +++ b/src/dialect/mssql/mssql-driver.ts @@ -0,0 +1,263 @@ +import { Connection, ConnectionPool, Request } from 'mssql' +import { Request as TediousRequest, TYPES } from 'tedious' +import { + DatabaseConnection, + QueryResult, +} from '../../driver/database-connection.js' +import { Driver, TransactionSettings } from '../../driver/driver.js' +import { + freeze, + isBigInt, + isBoolean, + isBuffer, + isDate, + isFunction, + isNull, + isNumber, + isString, + isUndefined, +} from '../../util/object-utils.js' +import { MssqlDialectConfig } from './mssql-dialect-config.js' +import { CompiledQuery } from '../../query-compiler/compiled-query.js' +import { extendStackTrace } from '../../util/stack-trace-utils.js' + +const PRIVATE_RELEASE_METHOD = Symbol() + +export class MssqlDriver implements Driver { + readonly #config: MssqlDialectConfig + readonly #connections = new WeakMap() + #pool?: ConnectionPool + + constructor(config: MssqlDialectConfig) { + this.#config = freeze({ ...config }) + } + + async init(): Promise { + this.#pool = isFunction(this.#config.pool) + ? await this.#config.pool() + : this.#config.pool + } + + async acquireConnection(): Promise { + const pool = await this.#pool!.connect() + const rawConnection = await pool.pool.acquire().promise + + let connection = this.#connections.get(rawConnection) + + if (!connection) { + connection = new MssqlConnection(rawConnection, pool) + this.#connections.set(rawConnection, connection) + + if (this.#config.onCreateConnection) { + await this.#config.onCreateConnection(connection) + } + } + + return connection + } + + async beginTransaction( + connection: DatabaseConnection, + settings: TransactionSettings + ): Promise { + // TODO: ... + throw new Error('Not implemented') + } + + async commitTransaction(connection: DatabaseConnection): Promise { + // TODO: ... + throw new Error('Not implemented') + } + + async rollbackTransaction(connection: DatabaseConnection): Promise { + // TODO: ... + throw new Error('Not implemented') + } + + async releaseConnection(connection: MssqlConnection): Promise { + connection[PRIVATE_RELEASE_METHOD]() + } + + async destroy(): Promise { + if (this.#pool) { + const pool = this.#pool + this.#pool = undefined + await pool.close() + } + } +} + +class MssqlConnection implements DatabaseConnection { + readonly #pool: ConnectionPool + readonly #rawConnection: Connection + + constructor(rawConnection: Connection, pool: ConnectionPool) { + this.#pool = pool + this.#rawConnection = rawConnection + } + + async executeQuery(compiledQuery: CompiledQuery): Promise> { + try { + const { rowCount, rows } = await new Promise<{ + rows: O[] + rowCount: number + }>((resolve, reject) => + this.#rawConnection.execSql( + this.#createTediousRequest(compiledQuery, reject, resolve) + ) + ) + + console.log('rows', JSON.stringify(rows)) + console.log('rowCount', rowCount) + + return { + numAffectedRows: BigInt(rowCount), + rows, + } + } catch (err) { + throw extendStackTrace(err, new Error()) + } + } + + async *streamQuery( + compiledQuery: CompiledQuery, + chunkSize: number + ): AsyncIterableIterator> { + if (!Number.isInteger(chunkSize) || chunkSize <= 0) { + throw new Error('chunkSize must be a positive integer') + } + + const request = this.#createMssqlRequest(compiledQuery) + request.stream = true + + const cursor = new MssqlCursor(request) + + try { + request.query(compiledQuery.sql) + + while (true) { + const rows = await cursor.read(chunkSize) + + if (rows.length === 0) { + break + } + + yield { + rows: rows, + } + } + } finally { + request.cancel() + } + } + + #createTediousRequest( + compiledQuery: CompiledQuery, + reject: (reason?: any) => void, + resolve: (value: any) => void + ): TediousRequest { + const request = new TediousRequest( + compiledQuery.sql, + (err, rowCount, rows) => { + if (err) { + reject(err) + } else { + resolve({ rowCount, rows }) + } + } + ) + + compiledQuery.parameters.forEach((parameter, index) => + request.addParameter( + String(index + 1), + this.#getTediousDataType(parameter), + parameter + ) + ) + + return request + } + + #createMssqlRequest(compiledQuery: CompiledQuery): Request { + return compiledQuery.parameters.reduce( + (request: Request, param, index) => + request.input(String(index + 1), param), + this.#pool.request() + ) + } + + #getTediousDataType(value: unknown): any { + if (isNull(value) || isUndefined(value) || isString(value)) { + return TYPES.NVarChar + } + + if (isBigInt(value) || (isNumber(value) && value % 1 === 0)) { + if (value < -2147483648 || value > 2147483647) { + return TYPES.BigInt + } else { + return TYPES.Int + } + } + + if (isNumber(value)) { + return TYPES.Float + } + + if (isBoolean(value)) { + return TYPES.Bit + } + + if (isDate(value)) { + return TYPES.DateTime + } + + if (isBuffer(value)) { + return TYPES.VarBinary + } + + return TYPES.NVarChar + } + + [PRIVATE_RELEASE_METHOD](): void { + this.#pool.pool.release(this.#rawConnection) + } +} + +class MssqlCursor { + readonly #request: Request + readonly #chunk: O[] = [] + + constructor(request: Request) { + this.#request = request + } + + async read(chunkSize: number): Promise { + if (this.#chunk.length >= chunkSize) { + return this.#chunk.splice(0, chunkSize) + } + + return new Promise((resolve, reject) => { + const rowListener = (row: O) => { + this.#chunk.push(row) + + if (this.#chunk.length >= chunkSize) { + this.#request.pause() + this.#request.off('row', rowListener) + resolve(this.#chunk.splice(0, chunkSize)) + } + } + + this.#request.on('row', rowListener) + + this.#request.once('error', reject) + + this.#request.once('done', () => { + if (this.#chunk.length < chunkSize) { + resolve(this.#chunk) + } + }) + + this.#request.resume() + }) + } +} diff --git a/src/dialect/mssql/mssql-introspector.ts b/src/dialect/mssql/mssql-introspector.ts new file mode 100644 index 000000000..96a202bd2 --- /dev/null +++ b/src/dialect/mssql/mssql-introspector.ts @@ -0,0 +1,32 @@ +import { Kysely } from '../../kysely.js' +import { + DatabaseIntrospector, + DatabaseMetadata, + DatabaseMetadataOptions, + SchemaMetadata, + TableMetadata, +} from '../database-introspector.js' + +export class MssqlIntrospector implements DatabaseIntrospector { + readonly #db: Kysely + + constructor(db: Kysely) { + this.#db = db + } + + async getSchemas(): Promise { + throw new Error('Not implemented') + } + + async getTables( + options?: DatabaseMetadataOptions | undefined + ): Promise { + throw new Error('Not implemented') + } + + async getMetadata( + options?: DatabaseMetadataOptions | undefined + ): Promise { + throw new Error('Not implemented') + } +} diff --git a/src/dialect/mssql/mssql-query-compiler.ts b/src/dialect/mssql/mssql-query-compiler.ts new file mode 100644 index 000000000..930b8c8d9 --- /dev/null +++ b/src/dialect/mssql/mssql-query-compiler.ts @@ -0,0 +1,3 @@ +import { DefaultQueryCompiler } from '../../query-compiler/default-query-compiler.js' + +export class MssqlQueryCompiler extends DefaultQueryCompiler {} diff --git a/src/index.ts b/src/index.ts index 0cc1d911e..c9c3bd74e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -88,6 +88,13 @@ export * from './dialect/sqlite/sqlite-query-compiler.js' export * from './dialect/sqlite/sqlite-introspector.js' export * from './dialect/sqlite/sqlite-adapter.js' +export * from './dialect/mssql/mssql-adapter.js' +export * from './dialect/mssql/mssql-dialect-config.js' +export * from './dialect/mssql/mssql-dialect.js' +export * from './dialect/mssql/mssql-driver.js' +export * from './dialect/mssql/mssql-introspector.js' +export * from './dialect/mssql/mssql-query-compiler.js' + export * from './query-compiler/default-query-compiler.js' export * from './query-compiler/query-compiler.js' diff --git a/src/util/object-utils.ts b/src/util/object-utils.ts index f26983652..b81fcc82e 100644 --- a/src/util/object-utils.ts +++ b/src/util/object-utils.ts @@ -34,7 +34,7 @@ export function isDate(obj: unknown): obj is Date { return obj instanceof Date } -export function isBigInt(obj: unknown): obj is BigInt { +export function isBigInt(obj: unknown): obj is bigint { return typeof obj === 'bigint' } From a6dfbbc8dc47291104851874bb084a71498b7f96 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Wed, 19 Jul 2023 04:22:55 +0300 Subject: [PATCH 05/65] fix browser tests by getting tedious stuff in config. --- src/dialect/mssql/mssql-dialect-config.ts | 8 +++++ src/dialect/mssql/mssql-driver.ts | 36 +++++++++++++++-------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/dialect/mssql/mssql-dialect-config.ts b/src/dialect/mssql/mssql-dialect-config.ts index ec6d0481b..4f955698b 100644 --- a/src/dialect/mssql/mssql-dialect-config.ts +++ b/src/dialect/mssql/mssql-dialect-config.ts @@ -1,4 +1,5 @@ import { ConnectionPool } from 'mssql' +import { Request, TYPES } from 'tedious' import { DatabaseConnection } from '../../driver/database-connection.js' export interface MssqlDialectConfig { @@ -11,12 +12,19 @@ export interface MssqlDialectConfig { */ pool: ConnectionPool | (() => Promise) + tedious: Tedious + /** * Called once for each created connection. */ onCreateConnection?: (connection: DatabaseConnection) => Promise } +export interface Tedious { + Request: typeof Request + TYPES: typeof TYPES +} + export interface MssqlPool { // TODO: ... } diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index df43bd68e..9caaa33a8 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -1,5 +1,5 @@ import { Connection, ConnectionPool, Request } from 'mssql' -import { Request as TediousRequest, TYPES } from 'tedious' +import type { Request as TediousRequest } from 'tedious' import { DatabaseConnection, QueryResult, @@ -17,7 +17,7 @@ import { isString, isUndefined, } from '../../util/object-utils.js' -import { MssqlDialectConfig } from './mssql-dialect-config.js' +import { MssqlDialectConfig, Tedious } from './mssql-dialect-config.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' import { extendStackTrace } from '../../util/stack-trace-utils.js' @@ -45,7 +45,11 @@ export class MssqlDriver implements Driver { let connection = this.#connections.get(rawConnection) if (!connection) { - connection = new MssqlConnection(rawConnection, pool) + connection = new MssqlConnection( + rawConnection, + pool, + this.#config.tedious + ) this.#connections.set(rawConnection, connection) if (this.#config.onCreateConnection) { @@ -90,10 +94,16 @@ export class MssqlDriver implements Driver { class MssqlConnection implements DatabaseConnection { readonly #pool: ConnectionPool readonly #rawConnection: Connection + readonly #tedious: Tedious - constructor(rawConnection: Connection, pool: ConnectionPool) { + constructor( + rawConnection: Connection, + pool: ConnectionPool, + tedious: Tedious + ) { this.#pool = pool this.#rawConnection = rawConnection + this.#tedious = tedious } async executeQuery(compiledQuery: CompiledQuery): Promise> { @@ -156,7 +166,7 @@ class MssqlConnection implements DatabaseConnection { reject: (reason?: any) => void, resolve: (value: any) => void ): TediousRequest { - const request = new TediousRequest( + const request = new this.#tedious.Request( compiledQuery.sql, (err, rowCount, rows) => { if (err) { @@ -188,34 +198,34 @@ class MssqlConnection implements DatabaseConnection { #getTediousDataType(value: unknown): any { if (isNull(value) || isUndefined(value) || isString(value)) { - return TYPES.NVarChar + return this.#tedious.TYPES.NVarChar } if (isBigInt(value) || (isNumber(value) && value % 1 === 0)) { if (value < -2147483648 || value > 2147483647) { - return TYPES.BigInt + return this.#tedious.TYPES.BigInt } else { - return TYPES.Int + return this.#tedious.TYPES.Int } } if (isNumber(value)) { - return TYPES.Float + return this.#tedious.TYPES.Float } if (isBoolean(value)) { - return TYPES.Bit + return this.#tedious.TYPES.Bit } if (isDate(value)) { - return TYPES.DateTime + return this.#tedious.TYPES.DateTime } if (isBuffer(value)) { - return TYPES.VarBinary + return this.#tedious.TYPES.VarBinary } - return TYPES.NVarChar + return this.#tedious.TYPES.NVarChar } [PRIVATE_RELEASE_METHOD](): void { From f9adb7f7c6bfe4364759867b5b488d05e721ff68 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 22 Jul 2023 04:32:40 +0300 Subject: [PATCH 06/65] introduce mssql to test-setup. --- test/node/src/test-setup.ts | 94 ++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 12 deletions(-) diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 87cbc6e52..a209a6149 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -2,9 +2,12 @@ import * as chai from 'chai' import * as chaiAsPromised from 'chai-as-promised' import * as chaiSubset from 'chai-subset' import * as Cursor from 'pg-cursor' -import { Pool } from 'pg' +import { Pool, PoolConfig } from 'pg' import { createPool } from 'mysql2' import * as Database from 'better-sqlite3' +import * as Tedious from 'tedious' +import { PoolOptions } from 'mysql2' +import { ConnectionPool, config } from 'mssql' chai.use(chaiSubset) chai.use(chaiAsPromised) @@ -31,6 +34,7 @@ import { sql, ColumnType, InsertObject, + MssqlDialect, } from '../../../' export interface Person { @@ -77,13 +81,18 @@ export interface TestContext { db: Kysely } -export type BuiltInDialect = 'postgres' | 'mysql' | 'sqlite' +export type BuiltInDialect = 'postgres' | 'mysql' | 'mssql' | 'sqlite' export type PerDialect = Record export const DIALECTS: BuiltInDialect[] = ( ['postgres', 'mysql', 'sqlite'] as const ).filter((d) => !process.env.DIALECT || d === process.env.DIALECT) +// temporary, to slowly introduce mssql tests file by file. +export const DIALECTS_WITH_MSSQL: BuiltInDialect[] = ( + [...DIALECTS, 'mssql'] as const +).filter((d) => !process.env.DIALECT || d === process.env.DIALECT) + const TEST_INIT_TIMEOUT = 5 * 60 * 1000 // This can be used as a placeholder for testSql when a query is not // supported on some dialect. @@ -108,7 +117,7 @@ export const DIALECT_CONFIGS = { user: 'kysely', port: 5434, max: POOL_SIZE, - }, + } satisfies PoolConfig, mysql: { database: 'kysely_test', @@ -121,7 +130,24 @@ export const DIALECT_CONFIGS = { bigNumberStrings: true, connectionLimit: POOL_SIZE, - }, + } satisfies PoolOptions, + + mssql: { + server: 'localhost', + user: 'sa', + password: 'KyselyTest0', + // parseJSON: true, + options: { + port: 21433, + database: 'kysely_test', + trustedConnection: true, + trustServerCertificate: true, + useUTC: true, + }, + pool: { + max: POOL_SIZE, + }, + } satisfies config, sqlite: { databasePath: ':memory:', @@ -144,6 +170,13 @@ export const DB_CONFIGS: PerDialect = { plugins: PLUGINS, }, + mssql: { + dialect: new MssqlDialect({ + pool: async () => new ConnectionPool(DIALECT_CONFIGS.mssql), + tedious: Tedious, + }), + }, + sqlite: { dialect: new SqliteDialect({ database: async () => new Database(DIALECT_CONFIGS.sqlite.databasePath), @@ -182,7 +215,7 @@ export async function insertPersons( const { pets, ...person } = insertPerson const personId = await insert( - ctx.dialect, + ctx, ctx.db.insertInto('person').values({ ...person }) ) @@ -285,11 +318,21 @@ export function createTableWithId( if (dialect === 'postgres') { return builder.addColumn('id', 'serial', (col) => col.primaryKey()) - } else { + } + + if (dialect === 'mssql') { return builder.addColumn('id', 'integer', (col) => - col.autoIncrement().primaryKey() + col + .notNull() + // TODO: change to method when its implemented + .modifyFront(sql`identity(1,1)`) + .primaryKey() ) } + + return builder.addColumn('id', 'integer', (col) => + col.autoIncrement().primaryKey() + ) } async function connect(config: KyselyConfig): Promise> { @@ -334,7 +377,7 @@ async function insertPetForPerson( const { toys, ...pet } = insertPet const petId = await insert( - ctx.dialect, + ctx, ctx.db.insertInto('pet').values({ ...pet, owner_id: personId }) ) @@ -355,16 +398,43 @@ async function insertToysForPet( } async function insert( - dialect: BuiltInDialect, + ctx: TestContext, qb: InsertQueryBuilder ): Promise { + const { dialect } = ctx + if (dialect === 'postgres' || dialect === 'sqlite') { const { id } = await qb.returning('id').executeTakeFirstOrThrow() + return id - } else { - const { insertId } = await qb.executeTakeFirst() - return Number(insertId) } + + if (dialect === 'mssql') { + // TODO: use insert into "table" (...) output inserted.id values (...) when its implemented + return await ctx.db.connection().execute(async (db) => { + await qb.executeTakeFirstOrThrow() + + const { query } = qb.compile() + + const table = + query.kind === 'InsertQueryNode' && + [query.into.table.schema?.name, query.into.table.identifier.name] + .filter(Boolean) + .join('.') + + const { + rows: [{ id }], + } = await sql<{ id: number }>`select IDENT_CURRENT(${sql.lit( + table + )}) as id`.execute(db) + + return Number(id) + }) + } + + const { insertId } = await qb.executeTakeFirstOrThrow() + + return Number(insertId) } function createNoopTransformerPlugin(): KyselyPlugin { From 945fe298a26910fd46923611e68b29574795ebff Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 22 Jul 2023 04:34:59 +0300 Subject: [PATCH 07/65] get dialect to a working state. --- src/dialect/mssql/mssql-driver.ts | 53 ++++++++++++++++------- src/dialect/mssql/mssql-query-compiler.ts | 6 ++- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index 9caaa33a8..9f3046528 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -1,5 +1,5 @@ import { Connection, ConnectionPool, Request } from 'mssql' -import type { Request as TediousRequest } from 'tedious' +import type { ColumnValue, Request as TediousRequest } from 'tedious' import { DatabaseConnection, QueryResult, @@ -117,9 +117,6 @@ class MssqlConnection implements DatabaseConnection { ) ) - console.log('rows', JSON.stringify(rows)) - console.log('rowCount', rowCount) - return { numAffectedRows: BigInt(rowCount), rows, @@ -166,24 +163,48 @@ class MssqlConnection implements DatabaseConnection { reject: (reason?: any) => void, resolve: (value: any) => void ): TediousRequest { - const request = new this.#tedious.Request( - compiledQuery.sql, - (err, rowCount, rows) => { - if (err) { - reject(err) - } else { - resolve({ rowCount, rows }) - } + const { parameters, sql } = compiledQuery + + let promisedRowCount: number | undefined + const rows: Record[] = [] + + const request = new this.#tedious.Request(sql, (err, rowCount) => { + if (err) { + reject(err) + } else { + promisedRowCount = rowCount } - ) + }) + + for (let i = 0; i < parameters.length; i++) { + const parameter = parameters[i] - compiledQuery.parameters.forEach((parameter, index) => request.addParameter( - String(index + 1), + String(i + 1), this.#getTediousDataType(parameter), parameter ) - ) + } + + const rowListener = (columns: ColumnValue[]) => { + const row: Record = {} + + for (const column of columns) { + row[column.metadata.colName] = column.value + } + + rows.push(row) + } + + request.on('row', rowListener) + + request.once('requestCompleted', () => { + request.off('row', rowListener) + resolve({ + rows, + rowCount: promisedRowCount!, + }) + }) return request } diff --git a/src/dialect/mssql/mssql-query-compiler.ts b/src/dialect/mssql/mssql-query-compiler.ts index 930b8c8d9..0b38215df 100644 --- a/src/dialect/mssql/mssql-query-compiler.ts +++ b/src/dialect/mssql/mssql-query-compiler.ts @@ -1,3 +1,7 @@ import { DefaultQueryCompiler } from '../../query-compiler/default-query-compiler.js' -export class MssqlQueryCompiler extends DefaultQueryCompiler {} +export class MssqlQueryCompiler extends DefaultQueryCompiler { + protected override getCurrentParameterPlaceholder(): string { + return `@${this.numParameters}` + } +} From fd01e7784531ac255b4311fff1f9abce4052ded1 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 22 Jul 2023 04:36:08 +0300 Subject: [PATCH 08/65] introduce mssql to insert test suite. --- test/node/src/insert.test.ts | 85 ++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/test/node/src/insert.test.ts b/test/node/src/insert.test.ts index 4af4cb4fb..793283f17 100644 --- a/test/node/src/insert.test.ts +++ b/test/node/src/insert.test.ts @@ -1,4 +1,10 @@ -import { AliasedRawBuilder, InsertResult, Kysely, sql } from '../../../' +import { + AliasedRawBuilder, + InsertResult, + Kysely, + SelectQueryBuilder, + sql, +} from '../../../' import { DIALECTS, @@ -12,10 +18,12 @@ import { Database, NOT_SUPPORTED, insertDefaultDataSet, + DIALECTS_WITH_MSSQL, + BuiltInDialect, } from './test-setup.js' -for (const dialect of DIALECTS) { - describe(`${dialect}: insert`, () => { +for (const dialect of DIALECTS_WITH_MSSQL) { + describe.only(`${dialect}: insert`, () => { let ctx: TestContext before(async function () { @@ -50,6 +58,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', parameters: ['Foo', 'Barson', 'other'], }, + mssql: { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (@1, @2, @3)', + parameters: ['Foo', 'Barson', 'other'], + }, sqlite: { sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?)', parameters: ['Foo', 'Barson', 'other'], @@ -60,7 +72,7 @@ for (const dialect of DIALECTS) { expect(result).to.be.instanceOf(InsertResult) expect(result.numInsertedOrUpdatedRows).to.equal(1n) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { expect(result.insertId).to.be.undefined } else { expect(result.insertId).to.be.a('bigint') @@ -93,6 +105,10 @@ for (const dialect of DIALECTS) { sql: "insert into `person` (`first_name`, `last_name`, `gender`) values ((select max(name) as `max_name` from `pet`), concat('Bar', 'son'), ?)", parameters: ['other'], }, + mssql: { + sql: `insert into "person" ("first_name", "last_name", "gender") values ((select max(name) as "max_name" from "pet"), concat('Bar', 'son'), @1)`, + parameters: ['other'], + }, sqlite: { sql: `insert into "person" ("first_name", "last_name", "gender") values ((select max(name) as "max_name" from "pet"), 'Bar' || 'son', ?)`, parameters: ['other'], @@ -127,6 +143,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` (`first_name`, `gender`) values ((select `first_name` from `person` where `last_name` = ? limit ?), ?)', parameters: ['Aniston', 1, 'female'], }, + mssql: { + sql: `insert into "person" ("first_name", "gender") values ((select "first_name" from "person" where "last_name" = @1 limit @2), @3)`, + parameters: ['Aniston', 1, 'female'], + }, sqlite: { sql: `insert into "person" ("first_name", "gender") values ((select "first_name" from "person" where "last_name" = ? limit ?), ?)`, parameters: ['Aniston', 1, 'female'], @@ -160,6 +180,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` (`first_name`, `gender`) select `name`, ? as `gender` from `pet`', parameters: ['other'], }, + mssql: { + sql: 'insert into "person" ("first_name", "gender") select "name", @1 as "gender" from "pet"', + parameters: ['other'], + }, sqlite: { sql: 'insert into "person" ("first_name", "gender") select "name", ? as "gender" from "pet"', parameters: ['other'], @@ -192,6 +216,8 @@ for (const dialect of DIALECTS) { ]) }) + // TODO: revisit this when mssql has output clause support as it also supports values expression + // https://database.guide/values-clause-in-sql-server/#:~:text=In%20SQL%20Server%2C%20VALUES%20is,statement%20or%20the%20FROM%20clause. if (dialect === 'postgres') { it('should insert the result of a values expression', async () => { const query = ctx.db @@ -218,6 +244,7 @@ for (const dialect of DIALECTS) { parameters: [1, 'foo', 2, 'bar'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -248,6 +275,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', parameters: ['Foo', 'Barson', 'other'], }, + mssql: { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (@1, @2, @3)', + parameters: ['Foo', 'Barson', 'other'], + }, sqlite: { sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?)', parameters: ['Foo', 'Barson', 'other'], @@ -259,7 +290,7 @@ for (const dialect of DIALECTS) { expect(result).to.be.instanceOf(InsertResult) expect(result.numInsertedOrUpdatedRows).to.equal(1n) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { expect(result.insertId).to.be.undefined } else { expect(result.insertId).to.be.a('bigint') @@ -286,6 +317,7 @@ for (const dialect of DIALECTS) { ], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -297,7 +329,7 @@ for (const dialect of DIALECTS) { }) } - if (dialect !== 'mysql') { + if (dialect === 'postgres' || dialect === 'sqlite') { it('should insert one row and ignore conflicts using `on conflict do nothing`', async () => { const [{ id, ...existingPet }] = await ctx.db .selectFrom('pet') @@ -319,6 +351,7 @@ for (const dialect of DIALECTS) { existingPet.species, ], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'insert into "pet" ("name", "owner_id", "species") values (?, ?, ?) on conflict ("name") do nothing', parameters: [ @@ -367,6 +400,7 @@ for (const dialect of DIALECTS) { ], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -402,6 +436,7 @@ for (const dialect of DIALECTS) { ], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -424,7 +459,7 @@ for (const dialect of DIALECTS) { }) } - if (dialect !== 'mysql') { + if (dialect === 'postgres' || dialect === 'sqlite') { it('should update instead of insert on conflict when using `on conflict do update`', async () => { const [{ id, ...existingPet }] = await ctx.db .selectFrom('pet') @@ -450,6 +485,7 @@ for (const dialect of DIALECTS) { ], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'insert into "pet" ("name", "owner_id", "species") values (?, ?, ?) on conflict ("name") do update set "species" = ?', parameters: [ @@ -512,6 +548,7 @@ for (const dialect of DIALECTS) { ], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -558,6 +595,7 @@ for (const dialect of DIALECTS) { ], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -594,6 +632,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?), (?, ?, ?)', parameters: ['Foo', 'Bar', 'other', 'Baz', 'Spam', 'other'], }, + mssql: { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (@1, @2, @3), (@4, @5, @6)', + parameters: ['Foo', 'Bar', 'other', 'Baz', 'Spam', 'other'], + }, sqlite: { sql: 'insert into "person" ("first_name", "last_name", "gender") values (?, ?, ?), (?, ?, ?)', parameters: ['Foo', 'Bar', 'other', 'Baz', 'Spam', 'other'], @@ -605,7 +647,7 @@ for (const dialect of DIALECTS) { expect(result).to.be.instanceOf(InsertResult) expect(result.numInsertedOrUpdatedRows).to.equal(2n) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { expect(result.insertId).to.be.undefined } else { expect(result.insertId).to.be.a('bigint') @@ -615,7 +657,7 @@ for (const dialect of DIALECTS) { .selectFrom('person') .selectAll() .orderBy('id', 'desc') - .limit(2) + .$call(limit(2, dialect)) .execute() expect(inserted).to.containSubset([ @@ -649,6 +691,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` (`first_name`, `gender`, `last_name`, `middle_name`) values (?, ?, default, default), (?, ?, ?, ?)', parameters: ['Foo', 'other', 'Baz', 'other', 'Spam', 'Bo'], }, + mssql: { + sql: 'insert into "person" ("first_name", "gender", "last_name", "middle_name") values (@1, @2, default, default), (@3, @4, @5, @6)', + parameters: ['Foo', 'other', 'Baz', 'other', 'Spam', 'Bo'], + }, sqlite: { sql: 'insert into "person" ("first_name", "gender", "last_name", "middle_name") values (?, ?, null, null), (?, ?, ?, ?)', parameters: ['Foo', 'other', 'Baz', 'other', 'Spam', 'Bo'], @@ -683,6 +729,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` (`first_name`, `last_name`, `middle_name`, `gender`) values (?, ?, ?, ?), (?, default, default, ?)', parameters: ['Foo', 'Spam', 'Bo', 'other', 'Baz', 'other'], }, + mssql: { + sql: 'insert into "person" ("first_name", "last_name", "middle_name", "gender") values (@1, @2, @3, @4), (@5, default, default, @6)', + parameters: ['Foo', 'Spam', 'Bo', 'other', 'Baz', 'other'], + }, sqlite: { sql: 'insert into "person" ("first_name", "last_name", "middle_name", "gender") values (?, ?, ?, ?), (?, null, null, ?)', parameters: ['Foo', 'Spam', 'Bo', 'other', 'Baz', 'other'], @@ -717,6 +767,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` (`first_name`, `middle_name`, `gender`, `last_name`) values (?, ?, ?, default), (?, default, ?, ?)', parameters: ['Foo', 'Bo', 'other', 'Baz', 'other', 'Spam'], }, + mssql: { + sql: 'insert into "person" ("first_name", "middle_name", "gender", "last_name") values (@1, @2, @3, default), (@4, default, @5, @6)', + parameters: ['Foo', 'Bo', 'other', 'Baz', 'other', 'Spam'], + }, sqlite: { sql: 'insert into "person" ("first_name", "middle_name", "gender", "last_name") values (?, ?, ?, null), (?, null, ?, ?)', parameters: ['Foo', 'Bo', 'other', 'Baz', 'other', 'Spam'], @@ -867,3 +921,16 @@ function values, A extends string>( sql.raw(`${alias}(${keys.join(', ')})`) ) } + +function limit>( + limit: number, + dialect: BuiltInDialect +): (qb: QB) => QB { + return (qb) => { + if (dialect === 'mssql') { + return qb.modifyFront(sql`top ${sql.lit(limit)}`) as QB + } + + return qb.limit(limit) as QB + } +} From b5fbb967c374fa7c58be2cce9c3d34418f5438f9 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 22 Jul 2023 04:50:53 +0300 Subject: [PATCH 09/65] introduce mssql to select test suite. --- test/node/src/select.test.ts | 156 ++++++++++++++++++++++++++--------- 1 file changed, 117 insertions(+), 39 deletions(-) diff --git a/test/node/src/select.test.ts b/test/node/src/select.test.ts index 5f0b15ae1..d8232343d 100644 --- a/test/node/src/select.test.ts +++ b/test/node/src/select.test.ts @@ -2,7 +2,6 @@ import { Kysely, PostgresDialect, sql } from '../../../' import { Pool } from 'pg' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -15,10 +14,11 @@ import { DIALECT_CONFIGS, Database, POOL_SIZE, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { - describe(`${dialect}: select`, () => { +for (const dialect of DIALECTS_WITH_MSSQL) { + describe.only(`${dialect}: select`, () => { let ctx: TestContext before(async function () { @@ -77,6 +77,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select * from "person" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select * from "person" where "first_name" = ?', parameters: ['Jennifer'], @@ -106,6 +110,10 @@ for (const dialect of DIALECTS) { sql: 'select `person`.* from `person` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select "person".* from "person" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select "person".* from "person" where "first_name" = ?', parameters: ['Jennifer'], @@ -120,7 +128,7 @@ for (const dialect of DIALECTS) { ]) }) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { it('should select all columns of a table with a schema', async () => { const query = ctx.db .selectFrom('toy_schema.toy') @@ -132,6 +140,10 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: { + sql: 'select "toy_schema"."toy".* from "toy_schema"."toy"', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) }) @@ -152,6 +164,10 @@ for (const dialect of DIALECTS) { sql: 'select `last_name` from `person` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select "last_name" from "person" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select "last_name" from "person" where "first_name" = ?', parameters: ['Jennifer'], @@ -179,6 +195,10 @@ for (const dialect of DIALECTS) { sql: 'select `last_name` as `ln` from `person` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select "last_name" as "ln" from "person" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select "last_name" as "ln" from "person" where "first_name" = ?', parameters: ['Jennifer'], @@ -206,6 +226,10 @@ for (const dialect of DIALECTS) { sql: 'select `person`.`last_name` from `person` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select "person"."last_name" from "person" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select "person"."last_name" from "person" where "first_name" = ?', parameters: ['Jennifer'], @@ -233,6 +257,10 @@ for (const dialect of DIALECTS) { sql: 'select `person`.`last_name` as `ln` from `person` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select "person"."last_name" as "ln" from "person" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select "person"."last_name" as "ln" from "person" where "first_name" = ?', parameters: ['Jennifer'], @@ -266,6 +294,10 @@ for (const dialect of DIALECTS) { sql: 'select (select `name` from `pet` where `person`.`id` = `pet`.`owner_id`) as `pet_name` from `person` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select (select "name" from "pet" where "person"."id" = "pet"."owner_id") as "pet_name" from "person" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select (select "name" from "pet" where "person"."id" = "pet"."owner_id") as "pet_name" from "person" where "first_name" = ?', parameters: ['Jennifer'], @@ -296,6 +328,10 @@ for (const dialect of DIALECTS) { sql: 'select count(case when `first_name` = ? then 1 end) as `num_jennifers` from `person`', parameters: ['Jennifer'], }, + mssql: { + sql: 'select count(case when "first_name" = @1 then 1 end) as "num_jennifers" from "person"', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select count(case when "first_name" = ? then 1 end) as "num_jennifers" from "person"', parameters: ['Jennifer'], @@ -312,42 +348,46 @@ for (const dialect of DIALECTS) { } }) - if (dialect === 'postgres') { - it('should select the count of jennifers using sum', async () => { - const query = ctx.db - .selectFrom('person') - .select((eb) => - eb.fn - .sum( - eb - .case() - .when('first_name', '=', 'Jennifer') - .then(sql.lit(1)) - .else(sql.lit(0)) - .end() - ) - .as('num_jennifers') - ) + it('should select the count of jennifers using sum', async () => { + const query = ctx.db + .selectFrom('person') + .select((eb) => + eb.fn + .sum( + eb + .case() + .when('first_name', '=', 'Jennifer') + .then(sql.lit(1)) + .else(sql.lit(0)) + .end() + ) + .as('num_jennifers') + ) - testSql(query, dialect, { - postgres: { - sql: 'select sum(case when "first_name" = $1 then 1 else 0 end) as "num_jennifers" from "person"', - parameters: ['Jennifer'], - }, - mysql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, - }) + testSql(query, dialect, { + postgres: { + sql: 'select sum(case when "first_name" = $1 then 1 else 0 end) as "num_jennifers" from "person"', + parameters: ['Jennifer'], + }, + mysql: NOT_SUPPORTED, + mssql: { + sql: 'select sum(case when "first_name" = @1 then 1 else 0 end) as "num_jennifers" from "person"', + parameters: ['Jennifer'], + }, + sqlite: NOT_SUPPORTED, + }) - const counts = await query.execute() + const counts = await query.execute() - expect(counts).to.have.length(1) - expect(counts[0]).to.eql({ num_jennifers: '1' }) + expect(counts).to.have.length(1) + expect(counts[0]).to.eql({ + num_jennifers: dialect === 'postgres' ? '1' : 1, }) - } + }) - // Raw exrpessions are of course supported on all dialects, but we use an - // expression that's only valid on postgres. - if (dialect === 'postgres') { + // Raw expressions are of course supported on all dialects, but we use an + // expression that's only valid on postgres or mssql. + if (dialect === 'postgres' || dialect === 'mssql') { it('should select one field using a raw expression', async () => { const query = ctx.db .selectFrom('person') @@ -366,6 +406,10 @@ for (const dialect of DIALECTS) { parameters: ['Muriel', 'Jennifer'], }, mysql: NOT_SUPPORTED, + mssql: { + sql: `select concat("first_name", ' ', cast(@1 as varchar), ' ', "last_name") as "full_name_with_middle_name" from "person" where "first_name" = @2`, + parameters: ['Muriel', 'Jennifer'], + }, sqlite: NOT_SUPPORTED, }) @@ -380,7 +424,7 @@ for (const dialect of DIALECTS) { it('should select multiple fields', async () => { const fullName = - dialect === 'mysql' + dialect === 'mysql' || dialect === 'mssql' ? sql`concat(first_name, ' ', last_name)` : sql`first_name || ' ' || last_name` @@ -410,6 +454,10 @@ for (const dialect of DIALECTS) { sql: "select `first_name`, `last_name` as `ln`, `person`.`gender`, `person`.`first_name` as `fn`, concat(first_name, ' ', last_name) as `full_name`, (select `name` from `pet` where `person`.`id` = `owner_id`) as `pet_name` from `person` where `first_name` = ?", parameters: ['Jennifer'], }, + mssql: { + sql: `select "first_name", "last_name" as "ln", "person"."gender", "person"."first_name" as "fn", concat(first_name, ' ', last_name) as "full_name", (select "name" from "pet" where "person"."id" = "owner_id") as "pet_name" from "person" where "first_name" = @1`, + parameters: ['Jennifer'], + }, sqlite: { sql: `select "first_name", "last_name" as "ln", "person"."gender", "person"."first_name" as "fn", first_name || ' ' || last_name as "full_name", (select "name" from "pet" where "person"."id" = "owner_id") as "pet_name" from "person" where "first_name" = ?`, parameters: ['Jennifer'], @@ -447,6 +495,10 @@ for (const dialect of DIALECTS) { sql: 'select `last_name`, `name` as `pet_name` from `person`, `pet` where `owner_id` = `person`.`id` and `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select "last_name", "name" as "pet_name" from "person", "pet" where "owner_id" = "person"."id" and "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select "last_name", "name" as "pet_name" from "person", "pet" where "owner_id" = "person"."id" and "first_name" = ?', parameters: ['Jennifer'], @@ -479,6 +531,10 @@ for (const dialect of DIALECTS) { sql: 'select `last_name`, `species` as `pet_species`, `one` from `person`, (select `owner_id`, `species` from `pet`) as `p`, (select 1 as one) as `o` where `p`.`owner_id` = `person`.`id` and `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select "last_name", "species" as "pet_species", "one" from "person", (select "owner_id", "species" from "pet") as "p", (select 1 as one) as "o" where "p"."owner_id" = "person"."id" and "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select "last_name", "species" as "pet_species", "one" from "person", (select "owner_id", "species" from "pet") as "p", (select 1 as one) as "o" where "p"."owner_id" = "person"."id" and "first_name" = ?', parameters: ['Jennifer'], @@ -505,6 +561,10 @@ for (const dialect of DIALECTS) { sql: 'select `first_name`, `pet`.`name` as `pet_name`, `toy`.`name` as `toy_name` from `person` inner join `pet` on `owner_id` = `person`.`id` inner join `toy` on `pet_id` = `pet`.`id` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: { + sql: 'select "first_name", "pet"."name" as "pet_name", "toy"."name" as "toy_name" from "person" inner join "pet" on "owner_id" = "person"."id" inner join "toy" on "pet_id" = "pet"."id" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select "first_name", "pet"."name" as "pet_name", "toy"."name" as "toy_name" from "person" inner join "pet" on "owner_id" = "person"."id" inner join "toy" on "pet_id" = "pet"."id" where "first_name" = ?', parameters: ['Jennifer'], @@ -535,6 +595,10 @@ for (const dialect of DIALECTS) { sql: 'select distinct `gender` from `person` order by `gender`', parameters: [], }, + mssql: { + sql: 'select distinct "gender" from "person" order by "gender"', + parameters: [], + }, sqlite: { sql: 'select distinct "gender" from "person" order by "gender"', parameters: [], @@ -547,7 +611,7 @@ for (const dialect of DIALECTS) { expect(persons).to.eql([{ gender: 'female' }, { gender: 'male' }]) }) - if (dialect !== 'sqlite') { + if (dialect === 'postgres' || dialect === 'mysql') { it('should select a row for update', async () => { const query = ctx.db .selectFrom('person') @@ -564,6 +628,7 @@ for (const dialect of DIALECTS) { sql: 'select `last_name` from `person` where `first_name` = ? for update', parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -589,6 +654,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -601,7 +667,7 @@ for (const dialect of DIALECTS) { ]) }) - it('should select with distict on that uses a RawBuilder expression', async () => { + it('should select with distinct on that uses a RawBuilder expression', async () => { const query = ctx.db .selectFrom('person') .select(['first_name', 'last_name']) @@ -613,6 +679,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -646,6 +713,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -673,6 +741,10 @@ for (const dialect of DIALECTS) { sql: 'select max(`person`.`first_name`) as `max_first_name`, min(`person`.`first_name`) as `min_first_name` from `person`', parameters: [], }, + mssql: { + sql: 'select max("person"."first_name") as "max_first_name", min("person"."first_name") as "min_first_name" from "person"', + parameters: [], + }, sqlite: { sql: 'select max("person"."first_name") as "max_first_name", min("person"."first_name") as "min_first_name" from "person"', parameters: [], @@ -702,6 +774,10 @@ for (const dialect of DIALECTS) { sql: 'select distinct `gender` from `person` order by `gender`', parameters: [], }, + mssql: { + sql: 'select distinct "gender" from "person" order by "gender"', + parameters: [], + }, sqlite: { sql: 'select distinct "gender" from "person" order by "gender"', parameters: [], @@ -714,7 +790,7 @@ for (const dialect of DIALECTS) { expect(persons).to.eql([{ gender: 'female' }, { gender: 'male' }]) }) - if (dialect !== 'sqlite') { + if (dialect === 'postgres' || dialect === 'mysql') { it('modifyEnd should add arbitrary SQL to the end of the query', async () => { const query = ctx.db .selectFrom('person') @@ -731,6 +807,7 @@ for (const dialect of DIALECTS) { sql: 'select `last_name` from `person` where `first_name` = ? for update', parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -741,6 +818,7 @@ for (const dialect of DIALECTS) { }) } + // TODO: introduce mssql if (dialect === 'mysql' || dialect === 'postgres') { it('should stream results', async () => { const males: unknown[] = [] From 3b08a91ed504e61983ad7fa2001f968b617c6877 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 22 Jul 2023 04:54:13 +0300 Subject: [PATCH 10/65] add mssql to test scripts. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c305ee7ef..f550f7690 100644 --- a/package.json +++ b/package.json @@ -32,9 +32,10 @@ "clean": "rm -rf dist & rm -rf test/node/dist & rm -rf test/browser/bundle.js & rm -rf helpers", "test": "npm run build && npm run test:node:build && concurrently -n node,typings,esmimports \"npm run test:node\" \"npm run test:typings\" \"npm run test:esmimports\"", "test:node:build": "tsc -p test/node", - "test:node": "concurrently -n postgres,mysql,sqlite \"npm run test:node:postgres\" \"npm run test:node:mysql\" \"npm run test:node:sqlite\"", + "test:node": "concurrently -n postgres,mysql,mssql,sqlite \"npm run test:node:postgres\" \"npm run test:node:mysql\" \"npm run test:node:mssql\" \"npm run test:node:sqlite\"", "test:node:postgres": "DIALECT=postgres npm run test:node:run", "test:node:mysql": "DIALECT=mysql npm run test:node:run", + "test:node:mssql": "DIALECT=mssql npm run test:node:run", "test:node:sqlite": "DIALECT=sqlite npm run test:node:run", "test:node:run": "mocha --reporter min --timeout 15000 test/node/dist/**/*.test.js", "test:browser:build": "rm -rf test/browser/bundle.js && esbuild test/browser/main.ts --bundle --outfile=test/browser/bundle.js", From e4540bf4e79ff1a3763ce1392841b7fa4cb1e7ee Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 22 Jul 2023 05:15:53 +0300 Subject: [PATCH 11/65] make tests pass type-checks. --- test/node/src/aggregate-function.test.ts | 20 +++++ test/node/src/camel-case.test.ts | 5 ++ test/node/src/case.test.ts | 7 ++ test/node/src/clear.test.ts | 10 +++ test/node/src/coalesce.test.ts | 3 + test/node/src/deduplicate-joins.test.ts | 4 + test/node/src/delete.test.ts | 28 +++++++ test/node/src/explain.test.ts | 6 ++ test/node/src/expression.test.ts | 2 + test/node/src/group-by.test.ts | 7 ++ test/node/src/having.test.ts | 4 + test/node/src/insert.test.ts | 2 +- test/node/src/join.test.ts | 15 ++++ test/node/src/json-traversal.test.ts | 20 +++++ test/node/src/order-by.test.ts | 7 ++ test/node/src/raw-query.test.ts | 2 + test/node/src/raw-sql.test.ts | 10 +++ test/node/src/replace.test.ts | 5 ++ test/node/src/sanitize-identifiers.test.ts | 3 + test/node/src/schema.test.ts | 85 ++++++++++++++++++++++ test/node/src/select.test.ts | 14 +++- test/node/src/set-operation.test.ts | 9 +++ test/node/src/update.test.ts | 8 ++ test/node/src/where.test.ts | 25 +++++++ test/node/src/with-schema.test.ts | 16 ++++ test/node/src/with.test.ts | 7 ++ 26 files changed, 319 insertions(+), 5 deletions(-) diff --git a/test/node/src/aggregate-function.test.ts b/test/node/src/aggregate-function.test.ts index ee582cbf6..804aa8e41 100644 --- a/test/node/src/aggregate-function.test.ts +++ b/test/node/src/aggregate-function.test.ts @@ -65,6 +65,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("id") as "${funcName}",`, @@ -109,6 +110,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}(distinct "id") as "${funcName}",`, @@ -153,6 +155,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("id") over() as "${funcName}",`, @@ -201,6 +204,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("id")`, @@ -261,6 +265,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("id")`, @@ -331,6 +336,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("id")`, @@ -376,6 +382,7 @@ for (const dialect of DIALECTS) { ], parameters: [3], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select *`, @@ -414,6 +421,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select *`, @@ -457,6 +465,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("person"."id") as "${funcName}",`, @@ -494,6 +503,7 @@ for (const dialect of DIALECTS) { ], parameters: [3], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select *`, @@ -532,6 +542,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select *`, @@ -573,6 +584,7 @@ for (const dialect of DIALECTS) { parameters: ['female', 'female'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("person"."id")`, @@ -620,6 +632,7 @@ for (const dialect of DIALECTS) { parameters: ['female', 'female'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("person"."id")`, @@ -675,6 +688,7 @@ for (const dialect of DIALECTS) { parameters: ['female', 'female'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("person"."id")`, @@ -724,6 +738,7 @@ for (const dialect of DIALECTS) { parameters: ['female', 'female'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}("person"."id")`, @@ -768,6 +783,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}(*) as "${funcName}",`, @@ -803,6 +819,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -836,6 +853,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}(*) over() as "${funcName}",`, @@ -875,6 +893,7 @@ for (const dialect of DIALECTS) { parameters: ['female', 'female'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select ${funcName}(*)`, @@ -937,6 +956,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'select rank() over() as "rank",', diff --git a/test/node/src/camel-case.test.ts b/test/node/src/camel-case.test.ts index d71ffe4b2..db4745176 100644 --- a/test/node/src/camel-case.test.ts +++ b/test/node/src/camel-case.test.ts @@ -8,6 +8,7 @@ import { testSql, expect, createTableWithId, + NOT_SUPPORTED, } from './test-setup.js' for (const dialect of DIALECTS) { @@ -117,6 +118,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select "camel_person"."first_name"`, @@ -167,6 +169,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select "camel_person"."first_name"`, @@ -203,6 +206,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `camel_person` add column `middle_name` text references `camel_person` (`first_name`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "camel_person" add column "middle_name" text references "camel_person" ("first_name")', parameters: [], @@ -225,6 +229,7 @@ for (const dialect of DIALECTS) { sql: 'delete from `camel_person` as `c` using `camel_person` where `camel_person`.`first_name` = ?', parameters: ['Arnold'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `delete from "camel_person" as "c" using "camel_person" where "camel_person"."first_name" = ?`, parameters: ['Arnold'], diff --git a/test/node/src/case.test.ts b/test/node/src/case.test.ts index b60eed8df..5023bb77a 100644 --- a/test/node/src/case.test.ts +++ b/test/node/src/case.test.ts @@ -1,6 +1,7 @@ import { sql } from '../../..' import { DIALECTS, + NOT_SUPPORTED, TestContext, clearDatabase, destroyTest, @@ -45,6 +46,7 @@ for (const dialect of DIALECTS) { sql: 'select case when `gender` = ? then ? end as `title` from `person`', parameters: ['male', 'Mr.'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select case when "gender" = ? then ? end as "title" from "person"`, parameters: ['male', 'Mr.'], @@ -70,6 +72,7 @@ for (const dialect of DIALECTS) { sql: 'select case `gender` when ? then ? end as `title` from `person`', parameters: ['male', 'Mr.'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select case "gender" when ? then ? end as "title" from "person"`, parameters: ['male', 'Mr.'], @@ -110,6 +113,7 @@ for (const dialect of DIALECTS) { ], parameters: ['male', 'female'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select case when "gender" = ? then 'Mr.'`, @@ -154,6 +158,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select case "gender" when 'male' then 'Mr.'`, @@ -199,6 +204,7 @@ for (const dialect of DIALECTS) { ], parameters: ['male', 'Mr.', 'female', 'Mrs.'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select case when "gender" = ? then ?`, @@ -259,6 +265,7 @@ for (const dialect of DIALECTS) { ], parameters: ['male', 'Mr.', 'female', 'single', 'Ms.', 'Mrs.'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'select case "gender" when ? then ?', diff --git a/test/node/src/clear.test.ts b/test/node/src/clear.test.ts index 4a8ec9671..be18b6633 100644 --- a/test/node/src/clear.test.ts +++ b/test/node/src/clear.test.ts @@ -4,6 +4,7 @@ import { initTest, TestContext, testSql, + NOT_SUPPORTED, } from './test-setup' for (const dialect of DIALECTS) { @@ -34,6 +35,7 @@ for (const dialect of DIALECTS) { sql: 'select `id` from `person`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select "id" from "person"`, parameters: [], @@ -57,6 +59,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person"`, parameters: [], @@ -78,6 +81,7 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` on conflict do nothing', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `insert into "person" on conflict do nothing`, parameters: [], @@ -104,6 +108,7 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` on conflict do update set `gender` = ?', parameters: ['other'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `insert into "person" on conflict do update set "gender" = ?`, parameters: ['other'], @@ -127,6 +132,7 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `gender` = ?', parameters: ['other'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `update "person" set "gender" = ?`, parameters: ['other'], @@ -149,6 +155,7 @@ for (const dialect of DIALECTS) { sql: 'delete from `person`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `delete from "person"`, parameters: [], @@ -172,6 +179,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person"`, parameters: [], @@ -195,6 +203,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person"`, parameters: [], @@ -219,6 +228,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` limit ?', parameters: [1], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person" limit ?`, parameters: [1], diff --git a/test/node/src/coalesce.test.ts b/test/node/src/coalesce.test.ts index fc7cf02f2..15f3f5772 100644 --- a/test/node/src/coalesce.test.ts +++ b/test/node/src/coalesce.test.ts @@ -69,6 +69,7 @@ for (const dialect of DIALECTS) { ], parameters: [1], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -167,6 +168,7 @@ for (const dialect of DIALECTS) { ], parameters: [1, 2, 3, 4, 5, 6, 7, 8], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'select', @@ -230,6 +232,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'select', diff --git a/test/node/src/deduplicate-joins.test.ts b/test/node/src/deduplicate-joins.test.ts index 917631690..b5ce8623b 100644 --- a/test/node/src/deduplicate-joins.test.ts +++ b/test/node/src/deduplicate-joins.test.ts @@ -8,6 +8,7 @@ import { TestContext, testSql, insertDefaultDataSet, + NOT_SUPPORTED, } from './test-setup.js' for (const dialect of DIALECTS) { @@ -46,6 +47,7 @@ for (const dialect of DIALECTS) { sql: 'select from `person` inner join `pet` on `pet`.`owner_id` = `person`.`id`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select from "person" inner join "pet" on "pet"."owner_id" = "person"."id"', parameters: [], @@ -87,6 +89,7 @@ for (const dialect of DIALECTS) { sql: 'select from `person` inner join (select `owner_id`, `id` as `pet_id`, `species` from `pet`) as `p` on `p`.`owner_id` = `person`.`id` and `p`.`species` in (?, ?)', parameters: ['cat', 'hamster'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select from "person" inner join (select "owner_id", "id" as "pet_id", "species" from "pet") as "p" on "p"."owner_id" = "person"."id" and "p"."species" in (?, ?)', parameters: ['cat', 'hamster'], @@ -110,6 +113,7 @@ for (const dialect of DIALECTS) { sql: 'select from `person` inner join `pet` on `pet`.`owner_id` = `person`.`id` inner join `toy` on `toy`.`pet_id` = `pet`.`id`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select from "person" inner join "pet" on "pet"."owner_id" = "person"."id" inner join "toy" on "toy"."pet_id" = "pet"."id"', parameters: [], diff --git a/test/node/src/delete.test.ts b/test/node/src/delete.test.ts index 249fb6801..eac231740 100644 --- a/test/node/src/delete.test.ts +++ b/test/node/src/delete.test.ts @@ -45,6 +45,7 @@ for (const dialect of DIALECTS) { sql: 'delete from `person` where `gender` = ?', parameters: ['female'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'delete from "person" where "gender" = ?', parameters: ['female'], @@ -106,6 +107,7 @@ for (const dialect of DIALECTS) { parameters: [2], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -129,6 +131,7 @@ for (const dialect of DIALECTS) { parameters: ['male'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'delete from "person" where "gender" = ? returning "first_name", "last_name" as "last"', parameters: ['male'], @@ -160,6 +163,7 @@ for (const dialect of DIALECTS) { parameters: ['female'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'delete from "person" where "gender" = ? returning "first_name", "last_name"', parameters: ['female'], @@ -190,6 +194,7 @@ for (const dialect of DIALECTS) { parameters: ['NO_SUCH_SPECIES'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -216,6 +221,7 @@ for (const dialect of DIALECTS) { parameters: [0], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -244,6 +250,7 @@ for (const dialect of DIALECTS) { parameters: ['Bob'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -266,6 +273,7 @@ for (const dialect of DIALECTS) { parameters: ['cat'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -294,6 +302,7 @@ for (const dialect of DIALECTS) { parameters: ['Zoro'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -322,6 +331,7 @@ for (const dialect of DIALECTS) { parameters: ['Luffy'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -350,6 +360,7 @@ for (const dialect of DIALECTS) { parameters: ['Itachi'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -372,6 +383,7 @@ for (const dialect of DIALECTS) { parameters: ['male'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -390,6 +402,7 @@ for (const dialect of DIALECTS) { parameters: ['male'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -418,6 +431,7 @@ for (const dialect of DIALECTS) { parameters: ['Bob'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -444,6 +458,7 @@ for (const dialect of DIALECTS) { ], parameters: ['NO_SUCH_SPECIES'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -468,6 +483,7 @@ for (const dialect of DIALECTS) { ], parameters: ['NO_SUCH_SPECIES'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -493,6 +509,7 @@ for (const dialect of DIALECTS) { sql: 'delete from `person` using `person` inner join `pet` on `pet`.`owner_id` = `person`.`id` left join `toy` on `toy`.`pet_id` = `pet`.`id` where (`pet`.`species` = ? or `toy`.`price` = ?)', parameters: ['NO_SUCH_SPECIES', 0], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -515,6 +532,7 @@ for (const dialect of DIALECTS) { ], parameters: ['NO_SUCH_SPECIES'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -539,6 +557,7 @@ for (const dialect of DIALECTS) { ], parameters: [0], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -563,6 +582,7 @@ for (const dialect of DIALECTS) { ], parameters: [0], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -587,6 +607,7 @@ for (const dialect of DIALECTS) { ], parameters: [911], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -611,6 +632,7 @@ for (const dialect of DIALECTS) { ], parameters: [911], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -637,6 +659,7 @@ for (const dialect of DIALECTS) { ], parameters: [1000], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -663,6 +686,7 @@ for (const dialect of DIALECTS) { ], parameters: [1000], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -689,6 +713,7 @@ for (const dialect of DIALECTS) { ], parameters: [1000], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -715,6 +740,7 @@ for (const dialect of DIALECTS) { ], parameters: [1000], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -741,6 +767,7 @@ for (const dialect of DIALECTS) { ], parameters: [1000], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -767,6 +794,7 @@ for (const dialect of DIALECTS) { ], parameters: [1000], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) diff --git a/test/node/src/explain.test.ts b/test/node/src/explain.test.ts index a37b02362..0e3dfd0f2 100644 --- a/test/node/src/explain.test.ts +++ b/test/node/src/explain.test.ts @@ -46,6 +46,7 @@ for (const dialect of DIALECTS) { { postgres: 'explain select * from "person" limit $1', mysql: 'explain select * from `person` limit ?', + mssql: 'explain select * from "person" limit ?', sqlite: 'explain select * from "person" limit ?', }[dialect] ) @@ -59,6 +60,7 @@ for (const dialect of DIALECTS) { { postgres: 'explain insert into "person" ("gender") values ($1)', mysql: 'explain insert into `person` (`gender`) values (?)', + mssql: 'explain insert into "person" ("gender") values ($1)', sqlite: 'explain insert into "person" ("gender") values (?)', }[dialect] ) @@ -76,6 +78,7 @@ for (const dialect of DIALECTS) { { postgres: 'explain update "person" set "gender" = $1 where "id" = $2', mysql: 'explain update `person` set `gender` = ? where `id` = ?', + mssql: 'explain update "person" set "gender" = $1 where "id" = $2', sqlite: 'explain update "person" set "gender" = ? where "id" = ?', }[dialect] ) @@ -89,6 +92,7 @@ for (const dialect of DIALECTS) { { postgres: 'explain delete from "person" where "id" = $1', mysql: 'explain delete from `person` where `id` = ?', + mssql: 'explain delete from "person" where "id" = $1', sqlite: 'explain delete from "person" where "id" = ?', }[dialect] ) @@ -122,6 +126,7 @@ for (const dialect of DIALECTS) { postgres: 'explain (analyze, format json) select * from "person" where "id" = $1', mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }[dialect] ) @@ -142,6 +147,7 @@ for (const dialect of DIALECTS) { postgres: NOT_SUPPORTED, mysql: 'explain analyze format=tree select * from `person` where `id` = ?', + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }[dialect] ) diff --git a/test/node/src/expression.test.ts b/test/node/src/expression.test.ts index 0f42d38fd..eb8287f5a 100644 --- a/test/node/src/expression.test.ts +++ b/test/node/src/expression.test.ts @@ -6,6 +6,7 @@ import { insertDefaultDataSet, TestContext, testSql, + NOT_SUPPORTED, } from './test-setup.js' for (const dialect of DIALECTS) { @@ -137,6 +138,7 @@ for (const dialect of DIALECTS) { 2000, ], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "person".* from "person" where ((not "first_name" = ? or "id" + ? > ? or "id" in (?, ?, ?) or upper("first_name") = ? or false) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and true and ("id" = ? or "id" = ? or "id" = ? or "id" = ?) and ("id" = ? and "first_name" = ? and "last_name" = ? and "marital_status" = ?) and ("id" = ? or "id" = ?) and ("id" + ?) > ? and ("first_name" = ? and "last_name" = ?) and ("first_name" = "last_name" or "last_name" = "first_name") and true and "id" between ? and ?)', parameters: [ diff --git a/test/node/src/group-by.test.ts b/test/node/src/group-by.test.ts index 13f349be8..1c7ec0d17 100644 --- a/test/node/src/group-by.test.ts +++ b/test/node/src/group-by.test.ts @@ -9,6 +9,7 @@ import { testSql, expect, insertDefaultDataSet, + NOT_SUPPORTED, } from './test-setup.js' for (const dialect of DIALECTS) { @@ -47,6 +48,7 @@ for (const dialect of DIALECTS) { sql: 'select `gender`, max(first_name) as `max_first_name` from `person` group by `gender` order by `gender`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "gender" order by "gender"', parameters: [], @@ -84,6 +86,7 @@ for (const dialect of DIALECTS) { sql: 'select `gender` as `g`, max(first_name) as `max_first_name` from `person` group by `g` order by `g`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "gender" as "g", max(first_name) as "max_first_name" from "person" group by "g" order by "g"', parameters: [], @@ -121,6 +124,7 @@ for (const dialect of DIALECTS) { sql: 'select `gender`, max(first_name) as `max_first_name` from `person` group by `gender`, `id` order by `gender`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "gender", "id" order by "gender"', parameters: [], @@ -146,6 +150,7 @@ for (const dialect of DIALECTS) { sql: 'select `gender`, max(first_name) as `max_first_name` from `person` group by `person`.`gender` order by `gender` asc', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "person"."gender" order by "gender" asc', parameters: [], @@ -171,6 +176,7 @@ for (const dialect of DIALECTS) { sql: 'select `gender`, max(first_name) as `max_first_name` from `person` group by person.gender order by `gender` asc', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by person.gender order by "gender" asc', parameters: [], @@ -211,6 +217,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'select max(first_name) as "max_first_name"', diff --git a/test/node/src/having.test.ts b/test/node/src/having.test.ts index 684926695..f3384e219 100644 --- a/test/node/src/having.test.ts +++ b/test/node/src/having.test.ts @@ -9,6 +9,7 @@ import { TestContext, testSql, expect, + NOT_SUPPORTED, } from './test-setup.js' for (const dialect of DIALECTS) { @@ -87,6 +88,7 @@ for (const dialect of DIALECTS) { ], parameters: [1], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select "first_name", count(pet.id) as "num_pets"`, @@ -146,6 +148,7 @@ for (const dialect of DIALECTS) { ], parameters: [1], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select "person"."id", count("pet"."id") as "num_pets"`, @@ -191,6 +194,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` group by `first_name` having (`id` in (?, ?, ?) or `first_name` < ? or `first_name` = `first_name`) and `first_name` = `first_name` and not exists (select `id` from `pet`)', parameters: [1, 2, 3, 'foo'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person" group by "first_name" having ("id" in (?, ?, ?) or "first_name" < ? or "first_name" = "first_name") and "first_name" = "first_name" and not exists (select "id" from "pet")`, parameters: [1, 2, 3, 'foo'], diff --git a/test/node/src/insert.test.ts b/test/node/src/insert.test.ts index 793283f17..538af2cd0 100644 --- a/test/node/src/insert.test.ts +++ b/test/node/src/insert.test.ts @@ -23,7 +23,7 @@ import { } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { - describe.only(`${dialect}: insert`, () => { + describe(`${dialect}: insert`, () => { let ctx: TestContext before(async function () { diff --git a/test/node/src/join.test.ts b/test/node/src/join.test.ts index c47d2771f..b6ae811d7 100644 --- a/test/node/src/join.test.ts +++ b/test/node/src/join.test.ts @@ -74,6 +74,7 @@ for (const dialect of DIALECTS) { sql: `select * from \`person\` inner join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` order by \`person\`.\`first_name\``, parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person" inner join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, parameters: [], @@ -135,6 +136,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select * from "person"`, @@ -185,6 +187,7 @@ for (const dialect of DIALECTS) { sql: `select \`pet\`.\`name\` as \`pet_name\`, \`toy\`.\`name\` as \`toy_name\` from \`person\` inner join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` inner join \`toy\` on \`toy\`.\`pet_id\` = \`pet\`.\`id\` where \`first_name\` = ?`, parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select "pet"."name" as "pet_name", "toy"."name" as "toy_name" from "person" inner join "pet" on "pet"."owner_id" = "person"."id" inner join "toy" on "toy"."pet_id" = "pet"."id" where "first_name" = ?`, parameters: ['Jennifer'], @@ -250,6 +253,7 @@ for (const dialect of DIALECTS) { ], parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select * from "person"`, @@ -315,6 +319,7 @@ for (const dialect of DIALECTS) { ], parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select * from "person"`, @@ -363,6 +368,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select "pet"."id" from "person"`, @@ -394,6 +400,7 @@ for (const dialect of DIALECTS) { sql: `select * from \`person\` left join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` order by \`person\`.\`first_name\``, parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person" left join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, parameters: [], @@ -436,6 +443,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select * from "person"`, @@ -467,6 +475,7 @@ for (const dialect of DIALECTS) { sql: `select \`pet\`.\`name\` as \`pet_name\`, \`toy\`.\`name\` as \`toy_name\` from \`person\` left join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` left join \`toy\` on \`toy\`.\`pet_id\` = \`pet\`.\`id\` where \`first_name\` = ?`, parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select "pet"."name" as "pet_name", "toy"."name" as "toy_name" from "person" left join "pet" on "pet"."owner_id" = "person"."id" left join "toy" on "toy"."pet_id" = "pet"."id" where "first_name" = ?`, parameters: ['Jennifer'], @@ -525,6 +534,7 @@ for (const dialect of DIALECTS) { ], parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ `select * from "person"`, @@ -567,6 +577,7 @@ for (const dialect of DIALECTS) { sql: 'select `pet`.`id` from `person` left join `pet` on not exists (select `id` from `pet` as `p` where `p`.`id` = `pet`.`id` and `p`.`owner_id` = `person`.`id`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select "pet"."id" from "person" left join "pet" on not exists (select "id" from "pet" as "p" where "p"."id" = "pet"."id" and "p"."owner_id" = "person"."id")`, parameters: [], @@ -595,6 +606,7 @@ for (const dialect of DIALECTS) { sql: `select * from \`person\` right join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` order by \`person\`.\`first_name\``, parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person" right join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, parameters: [], @@ -621,6 +633,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -650,6 +663,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -682,6 +696,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) diff --git a/test/node/src/json-traversal.test.ts b/test/node/src/json-traversal.test.ts index e99770003..4ef53a802 100644 --- a/test/node/src/json-traversal.test.ts +++ b/test/node/src/json-traversal.test.ts @@ -3,6 +3,7 @@ import { JSONColumnType, ParseJSONResultsPlugin, SqlBool, + sql, } from '../../..' import { BuiltInDialect, @@ -55,6 +56,7 @@ for (const dialect of DIALECTS) { parameters: [], sql: "select `website`->'$.url' as `website_url` from `person_metadata`", }, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "website"->>'$.url' as "website_url" from "person_metadata"`, @@ -83,6 +85,7 @@ for (const dialect of DIALECTS) { parameters: [], sql: "select `nicknames`->'$[0]' as `nickname` from `person_metadata`", }, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "nicknames"->>'$[0]' as "nickname" from "person_metadata"`, @@ -115,6 +118,7 @@ for (const dialect of DIALECTS) { parameters: [], sql: "select `profile`->'$.auth.roles' as `roles` from `person_metadata`", }, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "profile"->>'$.auth.roles' as "roles" from "person_metadata"`, @@ -143,6 +147,7 @@ for (const dialect of DIALECTS) { parameters: [], sql: "select `profile`->'$.tags[0]' as `main_tag` from `person_metadata`", }, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "profile"->>'$.tags[0]' as "main_tag" from "person_metadata"`, @@ -175,6 +180,7 @@ for (const dialect of DIALECTS) { parameters: [], sql: "select `experience`->'$[0].establishment' as `establishment` from `person_metadata`", }, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "experience"->>'$[0].establishment' as "establishment" from "person_metadata"`, @@ -207,6 +213,7 @@ for (const dialect of DIALECTS) { parameters: [], sql: "select `schedule`->'$[0][0]' as `january_1st_schedule` from `person_metadata`", }, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "schedule"->>'$[0][0]' as "january_1st_schedule" from "person_metadata"`, @@ -236,6 +243,7 @@ for (const dialect of DIALECTS) { parameters: [], sql: "select `nicknames`->'$[last]' as `nickname` from `person_metadata`", }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -260,6 +268,7 @@ for (const dialect of DIALECTS) { testSql(query, dialect, { postgres: NOT_SUPPORTED, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "nicknames"->>'$[#-1]' as "nickname" from "person_metadata"`, @@ -324,6 +333,7 @@ for (const dialect of DIALECTS) { parameters: [12], sql: "select * from `person_metadata` where `profile`->'$.auth.login_count' = ?", }, + mssql: NOT_SUPPORTED, sqlite: { parameters: [12], sql: `select * from "person_metadata" where "profile"->>'$.auth.login_count' = ?`, @@ -355,6 +365,7 @@ for (const dialect of DIALECTS) { sql: `select "website"->'url' as "website_url" from "person_metadata"`, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "website"->>'url' as "website_url" from "person_metadata"`, @@ -383,6 +394,7 @@ for (const dialect of DIALECTS) { sql: `select "nicknames"->0 as "nickname" from "person_metadata"`, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "nicknames"->>0 as "nickname" from "person_metadata"`, @@ -415,6 +427,7 @@ for (const dialect of DIALECTS) { sql: `select "profile"->'auth'->'roles' as "roles" from "person_metadata"`, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "profile"->'auth'->>'roles' as "roles" from "person_metadata"`, @@ -443,6 +456,7 @@ for (const dialect of DIALECTS) { sql: `select "profile"->'tags'->0 as "main_tag" from "person_metadata"`, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "profile"->'tags'->>0 as "main_tag" from "person_metadata"`, @@ -475,6 +489,7 @@ for (const dialect of DIALECTS) { sql: `select "experience"->0->'establishment' as "establishment" from "person_metadata"`, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "experience"->0->>'establishment' as "establishment" from "person_metadata"`, @@ -507,6 +522,7 @@ for (const dialect of DIALECTS) { sql: `select "schedule"->0->0 as "january_1st_schedule" from "person_metadata"`, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { parameters: [], sql: `select "schedule"->0->>0 as "january_1st_schedule" from "person_metadata"`, @@ -534,6 +550,7 @@ for (const dialect of DIALECTS) { sql: `select "nicknames"->-1 as "nickname" from "person_metadata"`, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -595,6 +612,7 @@ for (const dialect of DIALECTS) { sql: `select * from "person_metadata" where "profile"->'auth'->'login_count' = $1`, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { parameters: [12], sql: `select * from "person_metadata" where "profile"->'auth'->>'login_count' = ?`, @@ -669,6 +687,8 @@ function resolveJSONColumnDataType(dialect: BuiltInDialect) { return 'jsonb' case 'mysql': return 'json' + case 'mssql': + return sql`nvarchar(max)` case 'sqlite': return 'text' } diff --git a/test/node/src/order-by.test.ts b/test/node/src/order-by.test.ts index 36dbb0296..9fac66320 100644 --- a/test/node/src/order-by.test.ts +++ b/test/node/src/order-by.test.ts @@ -47,6 +47,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` order by `first_name`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" order by "first_name"', parameters: [], @@ -79,6 +80,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` order by `first_name`, `last_name` desc', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" order by "first_name", "last_name" desc', parameters: [], @@ -103,6 +105,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` order by `first_name`, `last_name` desc', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" order by "first_name", "last_name" desc', parameters: [], @@ -146,6 +149,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'select "first_name" as "fn",', @@ -193,6 +197,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'select * from "person"', @@ -223,6 +228,7 @@ for (const dialect of DIALECTS) { sql: "select * from `person` order by coalesce(`first_name`, 'foo') asc", parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person" order by coalesce("first_name", 'foo') asc`, parameters: [], @@ -245,6 +251,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) diff --git a/test/node/src/raw-query.test.ts b/test/node/src/raw-query.test.ts index 5113a6bfb..d1c8c3648 100644 --- a/test/node/src/raw-query.test.ts +++ b/test/node/src/raw-query.test.ts @@ -9,6 +9,7 @@ import { expect, insertDefaultDataSet, testSql, + NOT_SUPPORTED, } from './test-setup.js' for (const dialect of DIALECTS) { @@ -117,6 +118,7 @@ for (const dialect of DIALECTS) { sql: 'select first_name from person where gender = ? order by first_name asc, last_name asc', parameters: [gender], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select first_name from person where gender = ? order by first_name asc, last_name asc', parameters: [gender], diff --git a/test/node/src/raw-sql.test.ts b/test/node/src/raw-sql.test.ts index 603ad0411..d421f4891 100644 --- a/test/node/src/raw-sql.test.ts +++ b/test/node/src/raw-sql.test.ts @@ -47,6 +47,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where first_name between ? and ?', parameters: ['A', 'B'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where first_name between ? and ?', parameters: ['A', 'B'], @@ -73,6 +74,7 @@ for (const dialect of DIALECTS) { sql: "select * from `person` where first_name between 'A' and 'B'", parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person" where first_name between 'A' and 'B'`, parameters: [], @@ -97,6 +99,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` between ? and ?', parameters: ['A', 'B'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "first_name" between ? and ?', parameters: ['A', 'B'], @@ -125,6 +128,7 @@ for (const dialect of DIALECTS) { parameters: ['A', 'B'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -147,6 +151,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` between ? and ?', parameters: ['A', 'B'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "first_name" between ? and ?', parameters: ['A', 'B'], @@ -173,6 +178,7 @@ for (const dialect of DIALECTS) { parameters: ['A', 'B'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -194,6 +200,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` as `person`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" as "person"', parameters: [], @@ -215,6 +222,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -239,6 +247,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where first_name in (?, ?)', parameters: names, }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where first_name in (?, ?)', parameters: names, @@ -265,6 +274,7 @@ for (const dialect of DIALECTS) { parameters: names, }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) diff --git a/test/node/src/replace.test.ts b/test/node/src/replace.test.ts index aa13b7c34..ddf22330a 100644 --- a/test/node/src/replace.test.ts +++ b/test/node/src/replace.test.ts @@ -50,6 +50,7 @@ if (DIALECTS.includes('mysql')) { sql: 'replace into `person` (`id`, `first_name`, `last_name`, `gender`) values (?, ?, ?, ?)', parameters: [15, 'Foo', 'Barson', 'other'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -84,6 +85,7 @@ if (DIALECTS.includes('mysql')) { sql: "replace into `person` (`id`, `first_name`, `last_name`, `gender`) values (?, (select max(name) as `max_name` from `pet`), concat('Bar', 'son'), ?)", parameters: [2500, 'other'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -110,6 +112,7 @@ if (DIALECTS.includes('mysql')) { sql: 'replace into `person` (`first_name`, `gender`) select `name`, ? as `gender` from `pet`', parameters: ['other'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -144,6 +147,7 @@ if (DIALECTS.includes('mysql')) { sql: 'replace into `person` (`id`, `gender`) values (?, ?)', parameters: [12, 'male'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -172,6 +176,7 @@ if (DIALECTS.includes('mysql')) { ], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) diff --git a/test/node/src/sanitize-identifiers.test.ts b/test/node/src/sanitize-identifiers.test.ts index c3ef407ee..672fde4c5 100644 --- a/test/node/src/sanitize-identifiers.test.ts +++ b/test/node/src/sanitize-identifiers.test.ts @@ -7,6 +7,7 @@ import { TestContext, Person, testSql, + NOT_SUPPORTED, } from './test-setup.js' for (const dialect of DIALECTS) { @@ -39,6 +40,7 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name"``` = ?', parameters: ['foo', 'bar'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name""`" = ?', parameters: ['foo', 'bar'], @@ -64,6 +66,7 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name""````` = ?', parameters: ['foo', 'bar'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name""""``" = ?', parameters: ['foo', 'bar'], diff --git a/test/node/src/schema.test.ts b/test/node/src/schema.test.ts index 03881241c..ce16e441a 100644 --- a/test/node/src/schema.test.ts +++ b/test/node/src/schema.test.ts @@ -108,6 +108,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -210,6 +211,7 @@ for (const dialect of DIALECTS) { parameters: [], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -277,6 +279,7 @@ for (const dialect of DIALECTS) { .addColumn('s', 'blob') testSql(builder, dialect, { + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'create table "test"', @@ -352,6 +355,7 @@ for (const dialect of DIALECTS) { sql: 'create table `test` (`a` varchar(255), `b` varchar(255), `c` varchar(255), constraint `a_b_unique` unique (`a`, `b`), constraint `b_c_unique` unique (`b`, `c`))', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create table "test" ("a" varchar(255), "b" varchar(255), "c" varchar(255), constraint "a_b_unique" unique ("a", "b"), constraint "b_c_unique" unique ("b", "c"))', parameters: [], @@ -379,6 +383,7 @@ for (const dialect of DIALECTS) { sql: 'create table `test` (`a` integer, `b` integer, `c` integer, constraint `check_a` check (a > 1), constraint `check_b` check (b < c))', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create table "test" ("a" integer, "b" integer, "c" integer, constraint "check_a" check (a > 1), constraint "check_b" check (b < c))', parameters: [], @@ -404,6 +409,7 @@ for (const dialect of DIALECTS) { sql: 'create table `test` (`a` integer, `b` integer, constraint `primary` primary key (`a`, `b`))', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create table "test" ("a" integer, "b" integer, constraint "primary" primary key ("a", "b"))', parameters: [], @@ -439,6 +445,7 @@ for (const dialect of DIALECTS) { sql: 'create table `test` (`a` integer, `b` integer, constraint `foreign_key` foreign key (`a`, `b`) references `test2` (`c`, `d`))', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create table "test" ("a" integer, "b" integer, constraint "foreign_key" foreign key ("a", "b") references "test2" ("c", "d"))', parameters: [], @@ -474,6 +481,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -510,6 +518,7 @@ for (const dialect of DIALECTS) { sql: 'create table `test` (`a` integer, `b` integer, constraint `foreign_key` foreign key (`a`, `b`) references `test2` (`c`, `d`) on update cascade)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create table "test" ("a" integer, "b" integer, constraint "foreign_key" foreign key ("a", "b") references "test2" ("c", "d") on update cascade)', parameters: [], @@ -534,6 +543,7 @@ for (const dialect of DIALECTS) { sql: 'create table if not exists `test` (`id` integer primary key)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create table if not exists "test" ("id" integer primary key)', parameters: [], @@ -558,6 +568,7 @@ for (const dialect of DIALECTS) { sql: 'create temporary table `test` (`id` integer primary key)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create temporary table "test" ("id" integer primary key)', parameters: [], @@ -581,6 +592,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -603,6 +615,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -624,6 +637,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -645,6 +659,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -672,6 +687,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -701,6 +717,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -728,6 +745,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -745,6 +763,7 @@ for (const dialect of DIALECTS) { testSql(builder, dialect, { postgres: NOT_SUPPORTED, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'create table "test"', @@ -790,6 +809,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -824,6 +844,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'create table "test"', @@ -858,6 +879,7 @@ for (const dialect of DIALECTS) { sql: 'drop table `test`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'drop table "test"', parameters: [], @@ -879,6 +901,7 @@ for (const dialect of DIALECTS) { sql: 'drop table if exists `test`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'drop table if exists "test"', parameters: [], @@ -899,6 +922,7 @@ for (const dialect of DIALECTS) { sql: 'drop table `test` cascade', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) }) @@ -914,6 +938,7 @@ for (const dialect of DIALECTS) { sql: 'drop table if exists `test` cascade', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) }) @@ -946,6 +971,7 @@ for (const dialect of DIALECTS) { sql: 'create index `test_first_name_index` on `test` (`first_name`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create index "test_first_name_index" on "test" ("first_name")', parameters: [], @@ -974,6 +1000,7 @@ for (const dialect of DIALECTS) { sql: 'create index if not exists "test_first_name_index" on "test" ("first_name")', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create index if not exists "test_first_name_index" on "test" ("first_name")', parameters: [], @@ -1001,6 +1028,7 @@ for (const dialect of DIALECTS) { sql: 'create unique index `test_first_name_index` on `test` (`first_name`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create unique index "test_first_name_index" on "test" ("first_name")', parameters: [], @@ -1027,6 +1055,7 @@ for (const dialect of DIALECTS) { sql: 'create index `test_first_name_index` on `test` using hash (`first_name`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create index "test_first_name_index" on "test" using hash ("first_name")', parameters: [], @@ -1052,6 +1081,7 @@ for (const dialect of DIALECTS) { sql: 'create index `test_name_index` on `test` (`first_name`, `last_name`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create index "test_name_index" on "test" ("first_name", "last_name")', parameters: [], @@ -1076,6 +1106,7 @@ for (const dialect of DIALECTS) { sql: "create index `test_first_name_index` on `test` ((first_name < 'Sami'))", parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `create index "test_first_name_index" on "test" ((first_name < 'Sami'))`, parameters: [], @@ -1100,6 +1131,7 @@ for (const dialect of DIALECTS) { sql: 'create index `test_descending_first_name_index` on `test` (`first_name` desc)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create index "test_descending_first_name_index" on "test" ("first_name" desc)', parameters: [], @@ -1124,6 +1156,7 @@ for (const dialect of DIALECTS) { sql: 'create index `test_first_name_descending_last_name_index` on `test` (`first_name`, `last_name` desc)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'create index "test_first_name_descending_last_name_index" on "test" ("first_name", "last_name" desc)', parameters: [], @@ -1152,6 +1185,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: `create index "test_partial_index" on "test" ("first_name", "last_name") where ("first_name" = 'Igal' or "age" >= 18)`, parameters: [], @@ -1194,6 +1228,7 @@ for (const dialect of DIALECTS) { sql: 'drop index `test_first_name_index` on `test`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'drop index "test_first_name_index"', parameters: [], @@ -1214,6 +1249,7 @@ for (const dialect of DIALECTS) { sql: 'drop index if exists "test_first_name_index"', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'drop index if exists "test_first_name_index"', parameters: [], @@ -1237,6 +1273,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1255,6 +1292,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1281,6 +1319,7 @@ for (const dialect of DIALECTS) { sql: "create view `dogs` as select * from `pet` where `species` = 'dog'", parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `create view "dogs" as select * from "pet" where "species" = 'dog'`, parameters: [], @@ -1304,6 +1343,7 @@ for (const dialect of DIALECTS) { sql: `create temporary view "dogs" as select * from "pet" where "species" = 'dog'`, parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `create temporary view "dogs" as select * from "pet" where "species" = 'dog'`, parameters: [], @@ -1333,6 +1373,7 @@ for (const dialect of DIALECTS) { sql: "create or replace view `dogs` as select * from `pet` where `species` = 'dog'", parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1350,6 +1391,7 @@ for (const dialect of DIALECTS) { ) testSql(builder, dialect, { + mssql: NOT_SUPPORTED, sqlite: { sql: `create view if not exists "dogs" as select * from "pet" where "species" = 'dog'`, parameters: [], @@ -1377,6 +1419,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1421,6 +1464,7 @@ for (const dialect of DIALECTS) { sql: 'drop view `dogs`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `drop view "dogs"`, parameters: [], @@ -1442,6 +1486,7 @@ for (const dialect of DIALECTS) { sql: 'drop view if exists `dogs`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: `drop view if exists "dogs"`, parameters: [], @@ -1463,6 +1508,7 @@ for (const dialect of DIALECTS) { sql: 'drop view `dogs` cascade', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1480,6 +1526,7 @@ for (const dialect of DIALECTS) { sql: 'drop view if exists `dogs` cascade', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1505,6 +1552,7 @@ for (const dialect of DIALECTS) { sql: 'create schema `pets`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1523,6 +1571,7 @@ for (const dialect of DIALECTS) { sql: 'create schema if not exists `pets`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1554,6 +1603,7 @@ for (const dialect of DIALECTS) { sql: 'drop schema `pets`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1572,6 +1622,7 @@ for (const dialect of DIALECTS) { sql: 'drop schema if exists `pets`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1588,6 +1639,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1606,6 +1658,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1635,6 +1688,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1663,6 +1717,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1678,6 +1733,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1714,6 +1770,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` add column `bool_col` boolean not null', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" add column "bool_col" boolean not null', parameters: [], @@ -1749,6 +1806,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` add column `bool_col` boolean not null unique', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" add column "bool_col" boolean not null unique', parameters: [], @@ -1787,6 +1845,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'alter table "test"', @@ -1815,6 +1874,7 @@ for (const dialect of DIALECTS) { parameters: [], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1832,6 +1892,7 @@ for (const dialect of DIALECTS) { parameters: [], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1858,6 +1919,7 @@ for (const dialect of DIALECTS) { parameters: [], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1884,6 +1946,7 @@ for (const dialect of DIALECTS) { parameters: [], }, postgres: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1908,6 +1971,7 @@ for (const dialect of DIALECTS) { sql: "alter table `test` alter column `varchar_col` set default 'foo'", parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -1935,6 +1999,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` alter column `varchar_col` drop default', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) await builder.execute() @@ -1951,6 +2016,7 @@ for (const dialect of DIALECTS) { sql: 'alter table "test" alter column "varchar_col" type text', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" alter column "varchar_col" type text', parameters: [], @@ -1971,6 +2037,7 @@ for (const dialect of DIALECTS) { sql: 'alter table "test" alter column "varchar_col" type text', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" alter column "varchar_col" type text', parameters: [], @@ -1991,6 +2058,7 @@ for (const dialect of DIALECTS) { sql: 'alter table "test" alter column "varchar_col" set not null', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" alter column "varchar_col" set not null', parameters: [], @@ -2016,6 +2084,7 @@ for (const dialect of DIALECTS) { sql: 'alter table "test" alter column "varchar_col" drop not null', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" alter column "varchar_col" drop not null', parameters: [], @@ -2050,6 +2119,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -2073,6 +2143,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` drop column `varchar_col`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" drop column "varchar_col"', parameters: [], @@ -2111,6 +2182,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'alter table "test"', @@ -2139,6 +2211,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` rename to `test2`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" rename to "test2"', parameters: [], @@ -2160,6 +2233,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -2183,6 +2257,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` rename column `varchar_col` to `text_col`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" rename column "varchar_col" to "text_col"', parameters: [], @@ -2216,6 +2291,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [ 'alter table "test"', @@ -2251,6 +2327,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -2287,6 +2364,7 @@ for (const dialect of DIALECTS) { ], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -2314,6 +2392,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` add constraint `some_constraint` unique (`varchar_col`, `integer_col`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" add constraint "some_constraint" unique ("varchar_col", "integer_col")', parameters: [], @@ -2341,6 +2420,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` add constraint `some_constraint` check (integer_col > 0)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" add constraint "some_constraint" check (integer_col > 0)', parameters: [], @@ -2380,6 +2460,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` add constraint `some_constraint` foreign key (`integer_col`, `varchar_col`) references `test2` (`a`, `b`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" add constraint "some_constraint" foreign key ("integer_col", "varchar_col") references "test2" ("a", "b")', parameters: [], @@ -2417,6 +2498,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` add constraint `some_constraint` foreign key (`integer_col`, `varchar_col`) references `test2` (`a`, `b`) on delete set null on update cascade', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'alter table "test" add constraint "some_constraint" foreign key ("integer_col", "varchar_col") references "test2" ("a", "b") on delete set null on update cascade', parameters: [], @@ -2460,6 +2542,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test` drop constraint `foreign_key_constraint`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -2496,6 +2579,7 @@ for (const dialect of DIALECTS) { sql: 'alter table `test_schema`.`test` add column `second_column` text', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -2528,6 +2612,7 @@ for (const dialect of DIALECTS) { sql: ["alter table `test` add column `abc` integer default '42'"], parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: [`alter table "test" add column "abc" integer default '42'`], parameters: [], diff --git a/test/node/src/select.test.ts b/test/node/src/select.test.ts index d8232343d..37d1819b3 100644 --- a/test/node/src/select.test.ts +++ b/test/node/src/select.test.ts @@ -18,7 +18,7 @@ import { } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { - describe.only(`${dialect}: select`, () => { + describe(`${dialect}: select`, () => { let ctx: TestContext before(async function () { @@ -369,19 +369,25 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'select sum(case when "first_name" = $1 then 1 else 0 end) as "num_jennifers" from "person"', parameters: ['Jennifer'], }, - mysql: NOT_SUPPORTED, + mysql: { + sql: 'select sum(case when `first_name` = ? then 1 else 0 end) as `num_jennifers` from `person`', + parameters: ['Jennifer'], + }, mssql: { sql: 'select sum(case when "first_name" = @1 then 1 else 0 end) as "num_jennifers" from "person"', parameters: ['Jennifer'], }, - sqlite: NOT_SUPPORTED, + sqlite: { + sql: 'select sum(case when "first_name" = ? then 1 else 0 end) as "num_jennifers" from "person"', + parameters: ['Jennifer'], + }, }) const counts = await query.execute() expect(counts).to.have.length(1) expect(counts[0]).to.eql({ - num_jennifers: dialect === 'postgres' ? '1' : 1, + num_jennifers: dialect === 'postgres' || dialect === 'mysql' ? '1' : 1, }) }) diff --git a/test/node/src/set-operation.test.ts b/test/node/src/set-operation.test.ts index 380d31c6a..8e658038c 100644 --- a/test/node/src/set-operation.test.ts +++ b/test/node/src/set-operation.test.ts @@ -46,6 +46,7 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union select `id`, `name` from `pet` order by `name`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" order by "name"', parameters: [], @@ -87,6 +88,7 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union (select `id`, `name` from `pet` union select `id`, `name` from `toy`) order by `name`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -118,6 +120,7 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union all select `id`, `name` from `pet` order by `name`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union all select "id", "name" from "pet" order by "name"', parameters: [], @@ -152,6 +155,7 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union all select `id`, `name` from `pet` union select `id`, `name` from `toy` order by `name`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union all select "id", "name" from "pet" union select "id", "name" from "toy" order by "name"', parameters: [], @@ -175,6 +179,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "id", "first_name" as "name" from "person" intersect select "id", "name" from "pet" order by "name"', parameters: [], @@ -197,6 +202,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "id", "first_name" as "name" from "person" except select "id", "name" from "pet" order by "name"', parameters: [], @@ -222,6 +228,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union all select "id", "name" from "toy" intersect select "id", "name" from "pet" except select "id", "name" from "toy" order by "name"', parameters: [], @@ -246,6 +253,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -265,6 +273,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index 1df421076..f31144087 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -48,6 +48,7 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name` = ? where `gender` = ?', parameters: ['Foo', 'Barson', 'female'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name" = ? where "gender" = ?', parameters: ['Foo', 'Barson', 'female'], @@ -93,6 +94,7 @@ for (const dialect of DIALECTS) { sql: 'update `person` as `p` set `first_name` = ?, `last_name` = ? where `p`.`gender` = ?', parameters: ['Foo', 'Barson', 'female'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" as "p" set "first_name" = ?, "last_name" = ? where "p"."gender" = ?', parameters: ['Foo', 'Barson', 'female'], @@ -143,6 +145,7 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `last_name` = (select `name` from `pet` where `person`.`id` = `owner_id`) where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" set "last_name" = (select "name" from "pet" where "person"."id" = "owner_id") where "first_name" = ?', parameters: ['Jennifer'], @@ -183,6 +186,7 @@ for (const dialect of DIALECTS) { parameters: ['2', 'Jennifer'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -219,6 +223,7 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `last_name` = `first_name` where `first_name` = ?', parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" set "last_name" = "first_name" where "first_name" = ?', parameters: ['Jennifer'], @@ -259,6 +264,7 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name` = ? where `gender` = ?', parameters: ['Foo', 'Barson', 'female'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name" = ? where "gender" = ?', parameters: ['Foo', 'Barson', 'female'], @@ -301,6 +307,7 @@ for (const dialect of DIALECTS) { parameters: ['Barson', 'male'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" set "last_name" = ? where "gender" = ? returning "first_name", "last_name"', parameters: ['Barson', 'male'], @@ -351,6 +358,7 @@ for (const dialect of DIALECTS) { parameters: ['Arnold'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'update "person" set "first_name" = "pet"."name" from "pet" where "pet"."owner_id" = "person"."id" and "person"."first_name" = ? returning "first_name"', parameters: ['Arnold'], diff --git a/test/node/src/where.test.ts b/test/node/src/where.test.ts index b585a6935..45054876c 100644 --- a/test/node/src/where.test.ts +++ b/test/node/src/where.test.ts @@ -9,6 +9,7 @@ import { testSql, expect, insertDefaultDataSet, + NOT_SUPPORTED, } from './test-setup.js' for (const dialect of DIALECTS) { @@ -47,6 +48,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ?', parameters: ['Arnold'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "first_name" = ?', parameters: ['Arnold'], @@ -87,6 +89,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `last_name` is not null', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "last_name" is not null', parameters: [], @@ -118,6 +121,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `last_name` is null', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "last_name" is null', parameters: [], @@ -147,6 +151,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ?', parameters: ['Arnold'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "first_name" = ?', parameters: ['Arnold'], @@ -180,6 +185,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `person`.`first_name` = ?', parameters: ['Arnold'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "person"."first_name" = ?', parameters: ['Arnold'], @@ -209,6 +215,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where person.first_name = ?', parameters: ['Arnold'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where person.first_name = ?', parameters: ['Arnold'], @@ -234,6 +241,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `person`.`first_name` = ?', parameters: ['Arnold'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "person"."first_name" = ?', parameters: ['Arnold'], @@ -259,6 +267,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (first_name is null) is false', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where (first_name is null) is false', parameters: [], @@ -292,6 +301,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (select `pet`.`name` from `pet` where `owner_id` = `person`.`id`) = ?', parameters: ['Catto'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where (select "pet"."name" from "pet" where "owner_id" = "person"."id") = ?', parameters: ['Catto'], @@ -330,6 +340,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (select `pet`.`name` = ? as `is_doggo` from `pet` where `owner_id` = `person`.`id`)', parameters: ['Doggo'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where (select "pet"."name" = ? as "is_doggo" from "pet" where "owner_id" = "person"."id")', parameters: ['Doggo'], @@ -368,6 +379,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where ? = (select `pet`.`name` from `pet` where `owner_id` = `person`.`id`)', parameters: ['Catto'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where ? = (select "pet"."name" from "pet" where "owner_id" = "person"."id")', parameters: ['Catto'], @@ -397,6 +409,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ?', parameters: ['Arnold'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "first_name" = ?', parameters: ['Arnold'], @@ -431,6 +444,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` in (?, ?) order by `first_name` desc', parameters: ['Arnold', 'Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "first_name" in (?, ?) order by "first_name" desc', parameters: ['Arnold', 'Jennifer'], @@ -470,6 +484,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ? and `person`.`last_name` = ?', parameters: ['Arnold', 'Schwarzenegger'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where "first_name" = ? and "person"."last_name" = ?', parameters: ['Arnold', 'Schwarzenegger'], @@ -507,6 +522,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? and upper(`last_name`) = ?)', parameters: ['Jennifer', 'ANISTON'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where ("first_name" = ? and upper("last_name") = ?)', parameters: ['Jennifer', 'ANISTON'], @@ -545,6 +561,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? and upper(`last_name`) = ?)', parameters: ['Jennifer', 'ANISTON'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where ("first_name" = ? and upper("last_name") = ?)', parameters: ['Jennifer', 'ANISTON'], @@ -582,6 +599,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? and `last_name` = upper(`first_name`))', parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where ("first_name" = ? and "last_name" = upper("first_name"))', parameters: ['Jennifer'], @@ -612,6 +630,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? or upper(`last_name`) = ?)', parameters: ['Jennifer', 'ANISTON'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where ("first_name" = ? or upper("last_name") = ?)', parameters: ['Jennifer', 'ANISTON'], @@ -650,6 +669,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? or upper(`last_name`) = ?)', parameters: ['Jennifer', 'ANISTON'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where ("first_name" = ? or upper("last_name") = ?)', parameters: ['Jennifer', 'ANISTON'], @@ -688,6 +708,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where exists (select 1 as `exists` from `pet` where `pet`.`owner_id` = `person`.`id`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where exists (select 1 as "exists" from "pet" where "pet"."owner_id" = "person"."id")', parameters: [], @@ -721,6 +742,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where not exists (select `pet`.`id` from `pet` where `pet`.`owner_id` = `person`.`id`)', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where not exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id")', parameters: [], @@ -753,6 +775,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where case when `first_name` = ? then true else false end', parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where case when "first_name" = ? then true else false end', parameters: ['Jennifer'], @@ -779,6 +802,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where first_name between ? and ? and last_name between ? and ?', parameters: ['Arnold', 'Jennifer', 'A', 'Z'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person" where first_name between ? and ? and last_name between ? and ?', parameters: ['Arnold', 'Jennifer', 'A', 'Z'], @@ -805,6 +829,7 @@ for (const dialect of DIALECTS) { sql: 'select * from `person`, `pet` where `person`.`id` = `pet`.`id`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select * from "person", "pet" where "person"."id" = "pet"."id"', parameters: [], diff --git a/test/node/src/with-schema.test.ts b/test/node/src/with-schema.test.ts index 0ef985959..24fd8566d 100644 --- a/test/node/src/with-schema.test.ts +++ b/test/node/src/with-schema.test.ts @@ -64,6 +64,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -83,6 +84,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -102,6 +104,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -120,6 +123,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -150,6 +154,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -181,6 +186,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -212,6 +218,7 @@ if (DIALECTS.includes('postgres')) { parameters: ['Doggo', 'dog', anyPerson.id], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: { sql: 'insert into "mammals"."pet" ("name", "species", "owner_id") values (?, ?, ?) returning "mammals"."pet"."id"', parameters: ['Doggo', 'dog', anyPerson.id], @@ -233,6 +240,7 @@ if (DIALECTS.includes('postgres')) { parameters: ['Doggo'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -254,6 +262,7 @@ if (DIALECTS.includes('postgres')) { parameters: ['cat', 'Doggo'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -277,6 +286,7 @@ if (DIALECTS.includes('postgres')) { parameters: ['Doggo'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -302,6 +312,7 @@ if (DIALECTS.includes('postgres')) { parameters: ['Doggo'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -322,6 +333,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) }) @@ -349,6 +361,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -385,6 +398,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -413,6 +427,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -446,6 +461,7 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) diff --git a/test/node/src/with.test.ts b/test/node/src/with.test.ts index 5ae1c235c..3032ab7d8 100644 --- a/test/node/src/with.test.ts +++ b/test/node/src/with.test.ts @@ -63,6 +63,7 @@ for (const dialect of DIALECTS) { sql: 'with `jennifer_and_sylvester` as (select `id`, `first_name`, `gender` from `person` where (`first_name` = ? or `first_name` = ?)), `sylvester` as (select * from `jennifer_and_sylvester` where `gender` = ?) select * from `sylvester`', parameters: ['Jennifer', 'Sylvester', 'male'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'with "jennifer_and_sylvester" as (select "id", "first_name", "gender" from "person" where ("first_name" = ? or "first_name" = ?)), "sylvester" as (select * from "jennifer_and_sylvester" where "gender" = ?) select * from "sylvester"', parameters: ['Jennifer', 'Sylvester', 'male'], @@ -102,6 +103,7 @@ for (const dialect of DIALECTS) { sql: 'with `arnold`(`id`, `first_name`) as (select `id`, `first_name` from `person` where `first_name` = ?) select * from `arnold`', parameters: ['Arnold'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'with "arnold"("id", "first_name") as (select "id", "first_name" from "person" where "first_name" = ?) select * from "arnold"', parameters: ['Arnold'], @@ -166,6 +168,7 @@ for (const dialect of DIALECTS) { parameters: ['node1'], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -195,6 +198,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -222,6 +226,7 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -255,6 +260,7 @@ for (const dialect of DIALECTS) { sql: 'with "jennifer" as (select "id", "first_name", "gender" from "person" where "first_name" = $1) insert into "pet" ("owner_id", "name", "species") values ((select "id" from "jennifer"), $2, $3)', parameters: ['Jennifer', 'Catto 2', 'cat'], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'with "jennifer" as (select "id", "first_name", "gender" from "person" where "first_name" = ?) insert into "pet" ("owner_id", "name", "species") values ((select "id" from "jennifer"), ?, ?)', parameters: ['Jennifer', 'Catto 2', 'cat'], @@ -309,6 +315,7 @@ for (const dialect of DIALECTS) { 'Jennifer', ], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, mysql: NOT_SUPPORTED, }) From 57cd0369b3507ce09f6f2eb37b79c947f2a40cfe Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 22 Jul 2023 15:13:02 +0300 Subject: [PATCH 12/65] fix new tests after update branch. --- test/node/src/json.test.ts | 6 ++++++ test/node/src/set-operation.test.ts | 2 ++ 2 files changed, 8 insertions(+) diff --git a/test/node/src/json.test.ts b/test/node/src/json.test.ts index 34fd47632..f3ec67fd2 100644 --- a/test/node/src/json.test.ts +++ b/test/node/src/json.test.ts @@ -53,6 +53,12 @@ const jsonFunctions = { jsonObjectFrom: mysql_jsonObjectFrom, jsonBuildObject: mysql_jsonBuildObject, }, + // TODO: this is fake, to avoid ts errors. + mssql: { + jsonArrayFrom: mysql_jsonArrayFrom, + jsonObjectFrom: mysql_jsonObjectFrom, + jsonBuildObject: mysql_jsonBuildObject, + }, sqlite: { jsonArrayFrom: sqlite_jsonArrayFrom, jsonObjectFrom: sqlite_jsonObjectFrom, diff --git a/test/node/src/set-operation.test.ts b/test/node/src/set-operation.test.ts index 83d0405df..ddeb47e1d 100644 --- a/test/node/src/set-operation.test.ts +++ b/test/node/src/set-operation.test.ts @@ -83,6 +83,7 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union select `id`, `name` from `pet` union select `id`, `species` as `name` from `pet` order by `name`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union select "id", "species" as "name" from "pet" order by "name"', parameters: [], @@ -122,6 +123,7 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union select `id`, `name` from `pet` union select `id`, `species` as `name` from `pet` order by `name`', parameters: [], }, + mssql: NOT_SUPPORTED, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union select "id", "species" as "name" from "pet" order by "name"', parameters: [], From 1b725bca8fe4f291ae2a818d75b815af1b177b00 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 22 Jul 2023 17:34:41 +0300 Subject: [PATCH 13/65] introduce mssql to update test suite. --- test/node/src/insert.test.ts | 14 +-- test/node/src/test-setup.ts | 14 +++ test/node/src/update.test.ts | 211 +++++++++++++++++++++++++++-------- 3 files changed, 181 insertions(+), 58 deletions(-) diff --git a/test/node/src/insert.test.ts b/test/node/src/insert.test.ts index 538af2cd0..0fea6ef98 100644 --- a/test/node/src/insert.test.ts +++ b/test/node/src/insert.test.ts @@ -20,6 +20,7 @@ import { insertDefaultDataSet, DIALECTS_WITH_MSSQL, BuiltInDialect, + limit, } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { @@ -921,16 +922,3 @@ function values, A extends string>( sql.raw(`${alias}(${keys.join(', ')})`) ) } - -function limit>( - limit: number, - dialect: BuiltInDialect -): (qb: QB) => QB { - return (qb) => { - if (dialect === 'mssql') { - return qb.modifyFront(sql`top ${sql.lit(limit)}`) as QB - } - - return qb.limit(limit) as QB - } -} diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index a209a6149..33769f1fb 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -35,6 +35,7 @@ import { ColumnType, InsertObject, MssqlDialect, + SelectQueryBuilder, } from '../../../' export interface Person { @@ -456,3 +457,16 @@ function createNoopTransformerPlugin(): KyselyPlugin { function sleep(millis: number): Promise { return new Promise((resolve) => setTimeout(resolve, millis)) } + +export function limit>( + limit: number, + dialect: BuiltInDialect +): (qb: QB) => QB { + return (qb) => { + if (dialect === 'mssql') { + return qb.modifyFront(sql`top ${sql.lit(limit)}`) as QB + } + + return qb.limit(limit) as QB + } +} diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index f31144087..1a29dc777 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -11,9 +11,11 @@ import { NOT_SUPPORTED, insertDefaultDataSet, DEFAULT_DATA_SET, + DIALECTS_WITH_MSSQL, + limit, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: update`, () => { let ctx: TestContext @@ -48,7 +50,10 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name` = ? where `gender` = ?', parameters: ['Foo', 'Barson', 'female'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'update "person" set "first_name" = @1, "last_name" = @2 where "gender" = @3', + parameters: ['Foo', 'Barson', 'female'], + }, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name" = ? where "gender" = ?', parameters: ['Foo', 'Barson', 'female'], @@ -79,51 +84,94 @@ for (const dialect of DIALECTS) { ]) }) - it('should update one row with table alias', async () => { - const query = ctx.db - .updateTable('person as p') - .set({ first_name: 'Foo', last_name: 'Barson' }) - .where('p.gender', '=', 'female') + // mssql doesn't support table aliases in update clause, but it does support this + // with update alias set ... from table_name as alias + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'sqlite') { + it('should update one row with table alias', async () => { + const query = ctx.db + .updateTable('person as p') + .set({ first_name: 'Foo', last_name: 'Barson' }) + .where('p.gender', '=', 'female') - testSql(query, dialect, { - postgres: { - sql: 'update "person" as "p" set "first_name" = $1, "last_name" = $2 where "p"."gender" = $3', - parameters: ['Foo', 'Barson', 'female'], - }, - mysql: { - sql: 'update `person` as `p` set `first_name` = ?, `last_name` = ? where `p`.`gender` = ?', - parameters: ['Foo', 'Barson', 'female'], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'update "person" as "p" set "first_name" = ?, "last_name" = ? where "p"."gender" = ?', - parameters: ['Foo', 'Barson', 'female'], - }, + testSql(query, dialect, { + postgres: { + sql: 'update "person" as "p" set "first_name" = $1, "last_name" = $2 where "p"."gender" = $3', + parameters: ['Foo', 'Barson', 'female'], + }, + mysql: { + sql: 'update `person` as `p` set `first_name` = ?, `last_name` = ? where `p`.`gender` = ?', + parameters: ['Foo', 'Barson', 'female'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'update "person" as "p" set "first_name" = ?, "last_name" = ? where "p"."gender" = ?', + parameters: ['Foo', 'Barson', 'female'], + }, + }) + + const result = await query.executeTakeFirst() + + expect(result).to.be.instanceOf(UpdateResult) + expect(result.numUpdatedRows).to.equal(1n) + if (dialect === 'mysql') { + expect(result.numChangedRows).to.equal(1n) + } else { + expect(result.numChangedRows).to.undefined + } + + expect( + await ctx.db + .selectFrom('person') + .select(['first_name', 'last_name', 'gender']) + .orderBy('first_name') + .orderBy('last_name') + .execute() + ).to.eql([ + { first_name: 'Arnold', last_name: 'Schwarzenegger', gender: 'male' }, + { first_name: 'Foo', last_name: 'Barson', gender: 'female' }, + { first_name: 'Sylvester', last_name: 'Stallone', gender: 'male' }, + ]) }) + } - const result = await query.executeTakeFirst() + if (dialect === 'mssql') { + it('should update one row with table alias in from clause', async () => { + const query = ctx.db + .updateTable('p' as 'person') + .set({ first_name: 'Foo', last_name: 'Barson' }) + .from('person as p') + .where('p.gender', '=', 'female') - expect(result).to.be.instanceOf(UpdateResult) - expect(result.numUpdatedRows).to.equal(1n) - if (dialect === 'mysql') { - expect(result.numChangedRows).to.equal(1n) - } else { + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + mssql: { + sql: 'update "p" set "first_name" = @1, "last_name" = @2 from "person" as "p" where "p"."gender" = @3', + parameters: ['Foo', 'Barson', 'female'], + }, + sqlite: NOT_SUPPORTED, + }) + + const result = await query.executeTakeFirst() + + expect(result).to.be.instanceOf(UpdateResult) + expect(result.numUpdatedRows).to.equal(1n) expect(result.numChangedRows).to.undefined - } - expect( - await ctx.db - .selectFrom('person') - .select(['first_name', 'last_name', 'gender']) - .orderBy('first_name') - .orderBy('last_name') - .execute() - ).to.eql([ - { first_name: 'Arnold', last_name: 'Schwarzenegger', gender: 'male' }, - { first_name: 'Foo', last_name: 'Barson', gender: 'female' }, - { first_name: 'Sylvester', last_name: 'Stallone', gender: 'male' }, - ]) - }) + expect( + await ctx.db + .selectFrom('person') + .select(['first_name', 'last_name', 'gender']) + .orderBy('first_name') + .orderBy('last_name') + .execute() + ).to.eql([ + { first_name: 'Arnold', last_name: 'Schwarzenegger', gender: 'male' }, + { first_name: 'Foo', last_name: 'Barson', gender: 'female' }, + { first_name: 'Sylvester', last_name: 'Stallone', gender: 'male' }, + ]) + }) + } it('should update one row using a subquery', async () => { const query = ctx.db @@ -145,7 +193,10 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `last_name` = (select `name` from `pet` where `person`.`id` = `owner_id`) where `first_name` = ?', parameters: ['Jennifer'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'update "person" set "last_name" = (select "name" from "pet" where "person"."id" = "owner_id") where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'update "person" set "last_name" = (select "name" from "pet" where "person"."id" = "owner_id") where "first_name" = ?', parameters: ['Jennifer'], @@ -223,7 +274,10 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `last_name` = `first_name` where `first_name` = ?', parameters: ['Jennifer'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'update "person" set "last_name" = "first_name" where "first_name" = @1', + parameters: ['Jennifer'], + }, sqlite: { sql: 'update "person" set "last_name" = "first_name" where "first_name" = ?', parameters: ['Jennifer'], @@ -264,7 +318,10 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name` = ? where `gender` = ?', parameters: ['Foo', 'Barson', 'female'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'update "person" set "first_name" = @1, "last_name" = @2 where "gender" = @3', + parameters: ['Foo', 'Barson', 'female'], + }, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name" = ? where "gender" = ?', parameters: ['Foo', 'Barson', 'female'], @@ -419,7 +476,7 @@ for (const dialect of DIALECTS) { qb .selectFrom('person') .where('first_name', '=', 'Jennifer') - .limit(1) + .$call(limit(1, dialect)) .select('id') ) .updateTable('pet') @@ -427,6 +484,25 @@ for (const dialect of DIALECTS) { owner_id: eb.selectFrom('jennifer_id').select('id'), })) + testSql(query, dialect, { + postgres: { + sql: 'with "jennifer_id" as (select "id" from "person" where "first_name" = $1 limit $2) update "pet" set "owner_id" = (select "id" from "jennifer_id")', + parameters: ['Jennifer', 1], + }, + mysql: { + sql: 'update `pet` set `owner_id` = (select `id` from (select `id` from `person` where `first_name` = ? limit ?) as `jennifer_id`)', + parameters: ['Jennifer', 1], + }, + mssql: { + sql: 'with "jennifer_id" as (select top 1 "id" from "person" where "first_name" = @1) update "pet" set "owner_id" = (select "id" from "jennifer_id")', + parameters: ['Jennifer'], + }, + sqlite: { + sql: 'with "jennifer_id" as (select "id" from "person" where "first_name" = ? limit ?) update "pet" set "owner_id" = (select "id" from "jennifer_id")', + parameters: ['Jennifer', 1], + }, + }) + await query.execute() const jennifer = await ctx.db @@ -455,6 +531,51 @@ for (const dialect of DIALECTS) { name: eb.fn.coalesce('person.first_name', eb.val('')), })) + testSql(query, dialect, { + postgres: { + sql: 'update "pet" as "p" set "name" = coalesce("person"."first_name", $1) from "pet" inner join "person" on "person"."id" = "pet"."owner_id" where "p"."id" = "pet"."id"', + parameters: [''], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await query.execute() + + const pets = await ctx.db + .selectFrom('pet') + .innerJoin('person', 'person.id', 'pet.owner_id') + .select(['pet.name as pet_name', 'person.first_name as person_name']) + .execute() + + expect(pets).to.have.length(3) + for (const pet of pets) { + expect(pet.person_name).to.equal(pet.pet_name) + } + }) + } + + if (dialect === 'mssql') { + it('should update using a from clause and a join', async () => { + const query = ctx.db + .updateTable('p' as 'pet') + .from('pet as p') + .innerJoin('person', 'person.id', 'p.owner_id') + .set((eb) => ({ + name: eb.fn.coalesce('person.first_name', eb.val('')), + })) + + testSql(query, dialect, { + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + mssql: { + sql: 'update "p" set "name" = coalesce("person"."first_name", @1) from "pet" as "p" inner join "person" on "person"."id" = "p"."owner_id"', + parameters: [''], + }, + sqlite: NOT_SUPPORTED, + }) + await query.execute() const pets = await ctx.db From dabc2d10d408718083cdbf3deadb8154d84585a8 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 23 Jul 2023 00:23:05 +0300 Subject: [PATCH 14/65] fix mysql test expectation. --- test/node/src/update.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index 1a29dc777..163a6d1d3 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -490,7 +490,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { parameters: ['Jennifer', 1], }, mysql: { - sql: 'update `pet` set `owner_id` = (select `id` from (select `id` from `person` where `first_name` = ? limit ?) as `jennifer_id`)', + sql: 'with `jennifer_id` as (select `id` from `person` where `first_name` = ? limit ?) update `pet` set `owner_id` = (select `id` from `jennifer_id`)', parameters: ['Jennifer', 1], }, mssql: { From facd91835d2e0319ffe93d3b75756a962a00f19c Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 23 Jul 2023 00:54:13 +0300 Subject: [PATCH 15/65] introduce mssql to delete test suite. --- docker-compose.yml | 7 ++++-- test/node/src/delete.test.ts | 47 +++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7ec4dd8fe..f390151c7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,9 @@ services: ports: - '21433:1433' environment: - - ACCEPT_EULA=Y - - SA_PASSWORD=KyselyTest0 + ACCEPT_EULA: Y + MSSQL_PID: Express + SA_PASSWORD: KyselyTest0 healthcheck: test: /opt/mssql-tools/bin/sqlcmd -S mssql -U sa -P 'KyselyTest0' -Q 'select 1' waitmssql: @@ -16,6 +17,8 @@ services: - mssql depends_on: - mssql + environment: + MSSQL_PID: Express entrypoint: - bash - -c diff --git a/test/node/src/delete.test.ts b/test/node/src/delete.test.ts index eac231740..bd1e5efa2 100644 --- a/test/node/src/delete.test.ts +++ b/test/node/src/delete.test.ts @@ -1,7 +1,6 @@ import { DeleteResult, sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -11,9 +10,10 @@ import { NOT_SUPPORTED, insertDefaultDataSet, DEFAULT_DATA_SET, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: delete`, () => { let ctx: TestContext @@ -45,7 +45,10 @@ for (const dialect of DIALECTS) { sql: 'delete from `person` where `gender` = ?', parameters: ['female'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'delete from "person" where "gender" = @1', + parameters: ['female'], + }, sqlite: { sql: 'delete from "person" where "gender" = ?', parameters: ['female'], @@ -80,6 +83,25 @@ for (const dialect of DIALECTS) { ]) ) + testSql(query, dialect, { + postgres: { + sql: 'delete from "person" where ("first_name" = $1 or "first_name" = $2)', + parameters: ['Jennifer', 'Arnold'], + }, + mysql: { + sql: 'delete from `person` where (`first_name` = ? or `first_name` = ?)', + parameters: ['Jennifer', 'Arnold'], + }, + mssql: { + sql: 'delete from "person" where ("first_name" = @1 or "first_name" = @2)', + parameters: ['Jennifer', 'Arnold'], + }, + sqlite: { + sql: 'delete from "person" where ("first_name" = ? or "first_name" = ?)', + parameters: ['Jennifer', 'Arnold'], + }, + }) + const result = await query.executeTakeFirst() expect(result).to.be.instanceOf(DeleteResult) @@ -91,6 +113,25 @@ for (const dialect of DIALECTS) { .deleteFrom('person') .where('first_name', '=', 'Nobody') + testSql(query, dialect, { + postgres: { + sql: 'delete from "person" where "first_name" = $1', + parameters: ['Nobody'], + }, + mysql: { + sql: 'delete from `person` where `first_name` = ?', + parameters: ['Nobody'], + }, + mssql: { + sql: 'delete from "person" where "first_name" = @1', + parameters: ['Nobody'], + }, + sqlite: { + sql: 'delete from "person" where "first_name" = ?', + parameters: ['Nobody'], + }, + }) + const result = await query.executeTakeFirst() expect(result).to.be.instanceOf(DeleteResult) From 042b3973ec4f2da894c1840eed2985bb11bf9fc8 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 23 Jul 2023 01:57:19 +0300 Subject: [PATCH 16/65] introduce mssql to with test suite. --- test/node/src/with.test.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/node/src/with.test.ts b/test/node/src/with.test.ts index 3032ab7d8..8787c7e7a 100644 --- a/test/node/src/with.test.ts +++ b/test/node/src/with.test.ts @@ -1,7 +1,6 @@ import { sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -10,9 +9,10 @@ import { expect, NOT_SUPPORTED, insertDefaultDataSet, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: with`, () => { let ctx: TestContext @@ -63,7 +63,10 @@ for (const dialect of DIALECTS) { sql: 'with `jennifer_and_sylvester` as (select `id`, `first_name`, `gender` from `person` where (`first_name` = ? or `first_name` = ?)), `sylvester` as (select * from `jennifer_and_sylvester` where `gender` = ?) select * from `sylvester`', parameters: ['Jennifer', 'Sylvester', 'male'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'with "jennifer_and_sylvester" as (select "id", "first_name", "gender" from "person" where ("first_name" = @1 or "first_name" = @2)), "sylvester" as (select * from "jennifer_and_sylvester" where "gender" = @3) select * from "sylvester"', + parameters: ['Jennifer', 'Sylvester', 'male'], + }, sqlite: { sql: 'with "jennifer_and_sylvester" as (select "id", "first_name", "gender" from "person" where ("first_name" = ? or "first_name" = ?)), "sylvester" as (select * from "jennifer_and_sylvester" where "gender" = ?) select * from "sylvester"', parameters: ['Jennifer', 'Sylvester', 'male'], @@ -71,6 +74,7 @@ for (const dialect of DIALECTS) { }) const result = await query.execute() + expect(result).to.have.length(1) expect(Object.keys(result[0]).sort()).to.eql([ 'first_name', @@ -103,7 +107,10 @@ for (const dialect of DIALECTS) { sql: 'with `arnold`(`id`, `first_name`) as (select `id`, `first_name` from `person` where `first_name` = ?) select * from `arnold`', parameters: ['Arnold'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'with "arnold"("id", "first_name") as (select "id", "first_name" from "person" where "first_name" = @1) select * from "arnold"', + parameters: ['Arnold'], + }, sqlite: { sql: 'with "arnold"("id", "first_name") as (select "id", "first_name" from "person" where "first_name" = ?) select * from "arnold"', parameters: ['Arnold'], @@ -239,7 +246,7 @@ for (const dialect of DIALECTS) { }) } - if (dialect !== 'mysql') { + if (dialect === 'postgres' || dialect === 'mssql' || dialect === 'sqlite') { it('should create an insert query with common table expressions', async () => { const query = ctx.db .with('jennifer', (db) => @@ -260,7 +267,10 @@ for (const dialect of DIALECTS) { sql: 'with "jennifer" as (select "id", "first_name", "gender" from "person" where "first_name" = $1) insert into "pet" ("owner_id", "name", "species") values ((select "id" from "jennifer"), $2, $3)', parameters: ['Jennifer', 'Catto 2', 'cat'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'with "jennifer" as (select "id", "first_name", "gender" from "person" where "first_name" = @1) insert into "pet" ("owner_id", "name", "species") values ((select "id" from "jennifer"), @2, @3)', + parameters: ['Jennifer', 'Catto 2', 'cat'], + }, sqlite: { sql: 'with "jennifer" as (select "id", "first_name", "gender" from "person" where "first_name" = ?) insert into "pet" ("owner_id", "name", "species") values ((select "id" from "jennifer"), ?, ?)', parameters: ['Jennifer', 'Catto 2', 'cat'], @@ -272,7 +282,7 @@ for (const dialect of DIALECTS) { }) } - if (dialect !== 'mysql' && dialect !== 'sqlite') { + if (dialect === 'postgres') { it('should create a with query where CTEs are inserts updates and deletes', async () => { const query = ctx.db .with('deleted_arnold', (db) => From 86e220a8d806239f782e5534b997d38332c45f07 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 23 Jul 2023 03:09:25 +0300 Subject: [PATCH 17/65] introduce mssql to with schema test suite. --- test/node/src/test-setup.ts | 2 +- test/node/src/with-schema.test.ts | 215 +++++++++++++++++++----------- 2 files changed, 136 insertions(+), 81 deletions(-) diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 33769f1fb..76c6f0128 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -398,7 +398,7 @@ async function insertToysForPet( .executeTakeFirst() } -async function insert( +export async function insert( ctx: TestContext, qb: InsertQueryBuilder ): Promise { diff --git a/test/node/src/with-schema.test.ts b/test/node/src/with-schema.test.ts index 24fd8566d..43792d116 100644 --- a/test/node/src/with-schema.test.ts +++ b/test/node/src/with-schema.test.ts @@ -6,39 +6,39 @@ import { testSql, NOT_SUPPORTED, createTableWithId, - DIALECTS, + DIALECTS_WITH_MSSQL, + insert, + limit, } from './test-setup.js' -if (DIALECTS.includes('postgres')) { - const dialect = 'postgres' as const - +for (const dialect of DIALECTS_WITH_MSSQL.filter( + (dialect) => dialect === 'postgres' || dialect === 'mssql' +)) { describe(`${dialect}: with schema`, () => { let ctx: TestContext before(async function () { ctx = await initTest(this, dialect) - await dropTables() await createTables() }) beforeEach(async () => { - const person = await ctx.db - .insertInto('person') - .values({ + const personId = await insert( + ctx, + ctx.db.insertInto('person').values({ first_name: 'Foo', last_name: 'Bar', gender: 'other', }) - .returning('id') - .executeTakeFirst() + ) await ctx.db .withSchema('mammals') .insertInto('pet') .values({ name: 'Catto', - owner_id: person!.id, + owner_id: personId, species: 'cat', }) .execute() @@ -64,7 +64,10 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "mammals"."pet"', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -84,7 +87,10 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "mammals"."pet" as "p" left join "mammals"."pet" on "mammals"."pet"."id" = "p"."id"', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -104,7 +110,10 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "mammals"."pet" as "p1" left join "mammals"."pet" as "p2" on "p1"."id" = "p2"."id"', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -123,7 +132,10 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "p"."name" from "mammals"."pet" as "p"', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -154,7 +166,14 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'select "mammals"."pet"."name",', + '(select "name" from "mammals"."pet" as "p" where "p"."id" = "mammals"."pet"."id") as "p_name"', + 'from "mammals"."pet"', + ], + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -169,7 +188,7 @@ if (DIALECTS.includes('postgres')) { 'pet.name', (qb) => qb - .withSchema('public') + .withSchema(dialect === 'postgres' ? 'public' : 'dbo') .selectFrom('person') .select('first_name') .whereRef('pet.owner_id', '=', 'person.id') @@ -186,7 +205,14 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'select "mammals"."pet"."name",', + '(select "first_name" from "dbo"."person" where "mammals"."pet"."owner_id" = "dbo"."person"."id") as "owner_first_name"', + 'from "mammals"."pet"', + ], + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -199,7 +225,7 @@ if (DIALECTS.includes('postgres')) { const [anyPerson] = await ctx.db .selectFrom('person') .selectAll() - .limit(1) + .$call(limit(1, dialect)) .execute() const query = ctx.db @@ -210,7 +236,7 @@ if (DIALECTS.includes('postgres')) { species: 'dog', owner_id: anyPerson.id, }) - .returning('pet.id') + .$call((qb) => (dialect === 'postgres' ? qb.returning('pet.id') : qb)) testSql(query, dialect, { postgres: { @@ -218,12 +244,14 @@ if (DIALECTS.includes('postgres')) { parameters: ['Doggo', 'dog', anyPerson.id], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'insert into "mammals"."pet" ("name", "species", "owner_id") values (?, ?, ?) returning "mammals"."pet"."id"', + mssql: { + sql: 'insert into "mammals"."pet" ("name", "species", "owner_id") values (@1, @2, @3)', parameters: ['Doggo', 'dog', anyPerson.id], }, + sqlite: NOT_SUPPORTED, }) + + await query.execute() }) }) @@ -240,7 +268,10 @@ if (DIALECTS.includes('postgres')) { parameters: ['Doggo'], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'delete from "mammals"."pet" where "mammals"."pet"."name" = @1', + parameters: ['Doggo'], + }, sqlite: NOT_SUPPORTED, }) @@ -262,7 +293,10 @@ if (DIALECTS.includes('postgres')) { parameters: ['cat', 'Doggo'], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'update "mammals"."pet" set "species" = @1 where "mammals"."pet"."name" = @2', + parameters: ['cat', 'Doggo'], + }, sqlite: NOT_SUPPORTED, }) @@ -286,7 +320,10 @@ if (DIALECTS.includes('postgres')) { parameters: ['Doggo'], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'with "doggo" as (select * from "mammals"."pet" where "mammals"."pet"."name" = @1) select * from "doggo"', + parameters: ['Doggo'], + }, sqlite: NOT_SUPPORTED, }) @@ -312,7 +349,10 @@ if (DIALECTS.includes('postgres')) { parameters: ['Doggo'], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'with "doggo" as (select "mammals"."pet"."id" from "mammals"."pet" where "name" = @1) select "mammals"."pet"."id", (select "id" from "doggo") as "doggo_id", * from "mammals"."pet"', + parameters: ['Doggo'], + }, sqlite: NOT_SUPPORTED, }) @@ -321,21 +361,34 @@ if (DIALECTS.includes('postgres')) { }) describe('create table', () => { + afterEach(async () => { + await ctx.db.schema + .withSchema('mammals') + .dropTable('foo') + .ifExists() + .execute() + }) + it('should add schema for references', async () => { const query = ctx.db.schema .withSchema('mammals') .createTable('foo') - .addColumn('bar', 'integer', (col) => col.references('pets.id')) + .addColumn('bar', 'integer', (col) => col.references('pet.id')) testSql(query, dialect, { postgres: { - sql: 'create table "mammals"."foo" ("bar" integer references "mammals"."pets" ("id"))', + sql: 'create table "mammals"."foo" ("bar" integer references "mammals"."pet" ("id"))', parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create table "mammals"."foo" ("bar" integer references "mammals"."pet" ("id"))', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) + + await query.execute() }) }) @@ -361,7 +414,10 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create index "pet_id_index" on "mammals"."pet" ("id")', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -369,42 +425,36 @@ if (DIALECTS.includes('postgres')) { }) }) - describe('drop index', () => { - beforeEach(async () => { - await ctx.db.schema - .withSchema('mammals') - .createIndex('pet_id_index') - .column('id') - .on('pet') - .execute() - }) - - afterEach(async () => { - await ctx.db.schema - .withSchema('mammals') - .dropIndex('pet_id_index') - .ifExists() - .execute() - }) + if (dialect === 'postgres') { + describe('drop index', () => { + beforeEach(async () => { + await ctx.db.schema + .withSchema('mammals') + .createIndex('pet_id_index') + .column('id') + .on('pet') + .execute() + }) - it('should add schema for dropped index', async () => { - const query = ctx.db.schema - .withSchema('mammals') - .dropIndex('pet_id_index') + it('should add schema for dropped index', async () => { + const query = ctx.db.schema + .withSchema('mammals') + .dropIndex('pet_id_index') + + testSql(query, dialect, { + postgres: { + sql: 'drop index "mammals"."pet_id_index"', + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) - testSql(query, dialect, { - postgres: { - sql: 'drop index "mammals"."pet_id_index"', - parameters: [], - }, - mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + await query.execute() }) - - await query.execute() }) - }) + } describe('create view', () => { afterEach(async () => { @@ -419,15 +469,18 @@ if (DIALECTS.includes('postgres')) { const query = ctx.db.schema .withSchema('mammals') .createView('dogs') - .as(ctx.db.selectFrom('pet').where('species', '=', 'dog')) + .as(ctx.db.selectFrom('pet').where('species', '=', 'dog').selectAll()) testSql(query, dialect, { postgres: { - sql: `create view "mammals"."dogs" as select from "mammals"."pet" where "species" = 'dog'`, + sql: `create view "mammals"."dogs" as select * from "mammals"."pet" where "species" = 'dog'`, parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: `create view "mammals"."dogs" as select * from "mammals"."pet" where "species" = 'dog'`, + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -440,15 +493,7 @@ if (DIALECTS.includes('postgres')) { await ctx.db.schema .withSchema('mammals') .createView('dogs') - .as(ctx.db.selectFrom('pet').where('species', '=', 'dog')) - .execute() - }) - - afterEach(async () => { - await ctx.db.schema - .withSchema('mammals') - .dropView('dogs') - .ifExists() + .as(ctx.db.selectFrom('pet').where('species', '=', 'dog').selectAll()) .execute() }) @@ -461,7 +506,10 @@ if (DIALECTS.includes('postgres')) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: `drop view "mammals"."dogs"`, + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -470,7 +518,10 @@ if (DIALECTS.includes('postgres')) { }) async function createTables(): Promise { - await ctx.db.schema.createSchema('mammals').ifNotExists().execute() + await ctx.db.schema + .createSchema('mammals') + .$call((qb) => (dialect === 'postgres' ? qb.ifNotExists() : qb)) + .execute() const table = createTableWithId( ctx.db.schema.withSchema('mammals'), @@ -479,11 +530,15 @@ if (DIALECTS.includes('postgres')) { ) await table - .addColumn('name', 'varchar', (col) => col.unique()) + .addColumn('name', 'varchar(50)', (col) => col.unique()) .addColumn('owner_id', 'integer', (col) => - col.references('public.person.id').onDelete('cascade') + col + .references( + dialect === 'postgres' ? 'public.person.id' : 'dbo.person.id' + ) + .onDelete('cascade') ) - .addColumn('species', 'varchar') + .addColumn('species', 'varchar(50)') .execute() } From f5a381505e9fd461c9faebddb7dfcf2b99e6e606 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 24 Jul 2023 00:23:46 +0300 Subject: [PATCH 18/65] introduce mssql to where test suite. --- test/node/src/where.test.ts | 307 +++++++++++++++++++++++------------- 1 file changed, 194 insertions(+), 113 deletions(-) diff --git a/test/node/src/where.test.ts b/test/node/src/where.test.ts index 45054876c..14cdf7526 100644 --- a/test/node/src/where.test.ts +++ b/test/node/src/where.test.ts @@ -1,7 +1,6 @@ import { sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -9,10 +8,11 @@ import { testSql, expect, insertDefaultDataSet, + DIALECTS_WITH_MSSQL, NOT_SUPPORTED, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: where`, () => { let ctx: TestContext @@ -48,7 +48,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ?', parameters: ['Arnold'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "first_name" = @1', + parameters: ['Arnold'], + }, sqlite: { sql: 'select * from "person" where "first_name" = ?', parameters: ['Arnold'], @@ -89,7 +92,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `last_name` is not null', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "last_name" is not null', + parameters: [], + }, sqlite: { sql: 'select * from "person" where "last_name" is not null', parameters: [], @@ -121,7 +127,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `last_name` is null', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "last_name" is null', + parameters: [], + }, sqlite: { sql: 'select * from "person" where "last_name" is null', parameters: [], @@ -151,7 +160,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ?', parameters: ['Arnold'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "first_name" = @1', + parameters: ['Arnold'], + }, sqlite: { sql: 'select * from "person" where "first_name" = ?', parameters: ['Arnold'], @@ -185,7 +197,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `person`.`first_name` = ?', parameters: ['Arnold'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "person"."first_name" = @1', + parameters: ['Arnold'], + }, sqlite: { sql: 'select * from "person" where "person"."first_name" = ?', parameters: ['Arnold'], @@ -215,7 +230,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where person.first_name = ?', parameters: ['Arnold'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where person.first_name = @1', + parameters: ['Arnold'], + }, sqlite: { sql: 'select * from "person" where person.first_name = ?', parameters: ['Arnold'], @@ -241,7 +259,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `person`.`first_name` = ?', parameters: ['Arnold'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "person"."first_name" = @1', + parameters: ['Arnold'], + }, sqlite: { sql: 'select * from "person" where "person"."first_name" = ?', parameters: ['Arnold'], @@ -252,31 +273,37 @@ for (const dialect of DIALECTS) { expect(person!.first_name).to.equal('Arnold') }) - it('a raw instance and a boolean value', async () => { - const query = ctx.db - .selectFrom('person') - .selectAll() - .where(sql`(first_name is null)`, 'is', false) - - testSql(query, dialect, { - postgres: { - sql: 'select * from "person" where (first_name is null) is false', - parameters: [], - }, - mysql: { - sql: 'select * from `person` where (first_name is null) is false', - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'select * from "person" where (first_name is null) is false', - parameters: [], - }, + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { + it('a raw instance and a boolean value', async () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .where(sql`(first_name is null)`, 'is', false) + + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" where (first_name is null) is false', + parameters: [], + }, + mysql: { + sql: 'select * from `person` where (first_name is null) is false', + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'select * from "person" where (first_name is null) is false', + parameters: [], + }, + }) + + const persons = await query.execute() + expect(persons).to.have.length(3) }) - - const persons = await query.execute() - expect(persons).to.have.length(3) - }) + } it('a subquery and a primitive value', async () => { const query = ctx.db @@ -301,7 +328,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (select `pet`.`name` from `pet` where `owner_id` = `person`.`id`) = ?', parameters: ['Catto'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where (select "pet"."name" from "pet" where "owner_id" = "person"."id") = @1', + parameters: ['Catto'], + }, sqlite: { sql: 'select * from "person" where (select "pet"."name" from "pet" where "owner_id" = "person"."id") = ?', parameters: ['Catto'], @@ -320,44 +350,50 @@ for (const dialect of DIALECTS) { ]) }) - it('a boolean subquery', async () => { - const query = ctx.db - .selectFrom('person') - .selectAll() - .where((eb) => - eb - .selectFrom('pet') - .whereRef('owner_id', '=', 'person.id') - .select((eb) => eb('pet.name', '=', 'Doggo').as('is_doggo')) - ) + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { + it('a boolean subquery', async () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .where((eb) => + eb + .selectFrom('pet') + .whereRef('owner_id', '=', 'person.id') + .select((eb) => eb('pet.name', '=', 'Doggo').as('is_doggo')) + ) - testSql(query, dialect, { - postgres: { - sql: 'select * from "person" where (select "pet"."name" = $1 as "is_doggo" from "pet" where "owner_id" = "person"."id")', - parameters: ['Doggo'], - }, - mysql: { - sql: 'select * from `person` where (select `pet`.`name` = ? as `is_doggo` from `pet` where `owner_id` = `person`.`id`)', - parameters: ['Doggo'], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'select * from "person" where (select "pet"."name" = ? as "is_doggo" from "pet" where "owner_id" = "person"."id")', - parameters: ['Doggo'], - }, + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" where (select "pet"."name" = $1 as "is_doggo" from "pet" where "owner_id" = "person"."id")', + parameters: ['Doggo'], + }, + mysql: { + sql: 'select * from `person` where (select `pet`.`name` = ? as `is_doggo` from `pet` where `owner_id` = `person`.`id`)', + parameters: ['Doggo'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'select * from "person" where (select "pet"."name" = ? as "is_doggo" from "pet" where "owner_id" = "person"."id")', + parameters: ['Doggo'], + }, + }) + + const persons = await query.execute() + expect(persons).to.have.length(1) + expect(persons[0].id).to.be.a('number') + expect(persons).to.containSubset([ + { + first_name: 'Arnold', + last_name: 'Schwarzenegger', + gender: 'male', + }, + ]) }) - - const persons = await query.execute() - expect(persons).to.have.length(1) - expect(persons[0].id).to.be.a('number') - expect(persons).to.containSubset([ - { - first_name: 'Arnold', - last_name: 'Schwarzenegger', - gender: 'male', - }, - ]) - }) + } it('a raw instance and a subquery', async () => { const query = ctx.db @@ -379,7 +415,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where ? = (select `pet`.`name` from `pet` where `owner_id` = `person`.`id`)', parameters: ['Catto'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where @1 = (select "pet"."name" from "pet" where "owner_id" = "person"."id")', + parameters: ['Catto'], + }, sqlite: { sql: 'select * from "person" where ? = (select "pet"."name" from "pet" where "owner_id" = "person"."id")', parameters: ['Catto'], @@ -409,7 +448,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ?', parameters: ['Arnold'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "first_name" = @1', + parameters: ['Arnold'], + }, sqlite: { sql: 'select * from "person" where "first_name" = ?', parameters: ['Arnold'], @@ -444,7 +486,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` in (?, ?) order by `first_name` desc', parameters: ['Arnold', 'Jennifer'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "first_name" in (@1, @2) order by "first_name" desc', + parameters: ['Arnold', 'Jennifer'], + }, sqlite: { sql: 'select * from "person" where "first_name" in (?, ?) order by "first_name" desc', parameters: ['Arnold', 'Jennifer'], @@ -484,7 +529,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` = ? and `person`.`last_name` = ?', parameters: ['Arnold', 'Schwarzenegger'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "first_name" = @1 and "person"."last_name" = @2', + parameters: ['Arnold', 'Schwarzenegger'], + }, sqlite: { sql: 'select * from "person" where "first_name" = ? and "person"."last_name" = ?', parameters: ['Arnold', 'Schwarzenegger'], @@ -522,7 +570,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? and upper(`last_name`) = ?)', parameters: ['Jennifer', 'ANISTON'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where ("first_name" = @1 and upper("last_name") = @2)', + parameters: ['Jennifer', 'ANISTON'], + }, sqlite: { sql: 'select * from "person" where ("first_name" = ? and upper("last_name") = ?)', parameters: ['Jennifer', 'ANISTON'], @@ -561,7 +612,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? and upper(`last_name`) = ?)', parameters: ['Jennifer', 'ANISTON'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where ("first_name" = @1 and upper("last_name") = @2)', + parameters: ['Jennifer', 'ANISTON'], + }, sqlite: { sql: 'select * from "person" where ("first_name" = ? and upper("last_name") = ?)', parameters: ['Jennifer', 'ANISTON'], @@ -599,7 +653,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? and `last_name` = upper(`first_name`))', parameters: ['Jennifer'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where ("first_name" = @1 and "last_name" = upper("first_name"))', + parameters: ['Jennifer'], + }, sqlite: { sql: 'select * from "person" where ("first_name" = ? and "last_name" = upper("first_name"))', parameters: ['Jennifer'], @@ -630,7 +687,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? or upper(`last_name`) = ?)', parameters: ['Jennifer', 'ANISTON'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where ("first_name" = @1 or upper("last_name") = @2)', + parameters: ['Jennifer', 'ANISTON'], + }, sqlite: { sql: 'select * from "person" where ("first_name" = ? or upper("last_name") = ?)', parameters: ['Jennifer', 'ANISTON'], @@ -669,7 +729,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where (`first_name` = ? or upper(`last_name`) = ?)', parameters: ['Jennifer', 'ANISTON'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where ("first_name" = @1 or upper("last_name") = @2)', + parameters: ['Jennifer', 'ANISTON'], + }, sqlite: { sql: 'select * from "person" where ("first_name" = ? or upper("last_name") = ?)', parameters: ['Jennifer', 'ANISTON'], @@ -708,7 +771,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where exists (select 1 as `exists` from `pet` where `pet`.`owner_id` = `person`.`id`)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where exists (select 1 as "exists" from "pet" where "pet"."owner_id" = "person"."id")', + parameters: [], + }, sqlite: { sql: 'select * from "person" where exists (select 1 as "exists" from "pet" where "pet"."owner_id" = "person"."id")', parameters: [], @@ -742,7 +808,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where not exists (select `pet`.`id` from `pet` where `pet`.`owner_id` = `person`.`id`)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where not exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id")', + parameters: [], + }, sqlite: { sql: 'select * from "person" where not exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id")', parameters: [], @@ -753,38 +822,44 @@ for (const dialect of DIALECTS) { expect(persons).to.have.length(0) }) - it('case expression', async () => { - const query = ctx.db - .selectFrom('person') - .selectAll() - .where((eb) => - eb - .case() - .when('first_name', '=', 'Jennifer') - .then(sql.lit(true)) - .else(sql.lit(false)) - .end() - ) + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { + it('case expression', async () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .where((eb) => + eb + .case() + .when('first_name', '=', 'Jennifer') + .then(sql.lit(true)) + .else(sql.lit(false)) + .end() + ) - testSql(query, dialect, { - postgres: { - sql: 'select * from "person" where case when "first_name" = $1 then true else false end', - parameters: ['Jennifer'], - }, - mysql: { - sql: 'select * from `person` where case when `first_name` = ? then true else false end', - parameters: ['Jennifer'], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'select * from "person" where case when "first_name" = ? then true else false end', - parameters: ['Jennifer'], - }, + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" where case when "first_name" = $1 then true else false end', + parameters: ['Jennifer'], + }, + mysql: { + sql: 'select * from `person` where case when `first_name` = ? then true else false end', + parameters: ['Jennifer'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'select * from "person" where case when "first_name" = ? then true else false end', + parameters: ['Jennifer'], + }, + }) + + const persons = await query.execute() + expect(persons).to.have.length(1) }) - - const persons = await query.execute() - expect(persons).to.have.length(1) - }) + } it('single raw instance', async () => { const query = ctx.db @@ -802,7 +877,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where first_name between ? and ? and last_name between ? and ?', parameters: ['Arnold', 'Jennifer', 'A', 'Z'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where first_name between @1 and @2 and last_name between @3 and @4', + parameters: ['Arnold', 'Jennifer', 'A', 'Z'], + }, sqlite: { sql: 'select * from "person" where first_name between ? and ? and last_name between ? and ?', parameters: ['Arnold', 'Jennifer', 'A', 'Z'], @@ -829,7 +907,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person`, `pet` where `person`.`id` = `pet`.`id`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person", "pet" where "person"."id" = "pet"."id"', + parameters: [], + }, sqlite: { sql: 'select * from "person", "pet" where "person"."id" = "pet"."id"', parameters: [], From e41fcef3f32c6307d684bafa3c8321513f237811 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 24 Jul 2023 00:45:13 +0300 Subject: [PATCH 19/65] test mssql vs. selectNoFrom. --- test/node/src/select.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/node/src/select.test.ts b/test/node/src/select.test.ts index 3e9acf800..e64d25d60 100644 --- a/test/node/src/select.test.ts +++ b/test/node/src/select.test.ts @@ -15,6 +15,7 @@ import { Database, POOL_SIZE, DIALECTS_WITH_MSSQL, + limit, } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { @@ -939,7 +940,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { .selectFrom('person') .select('first_name') .orderBy('first_name') - .limit(1) + .$call(limit(1, dialect)) .as('person_first_name'), ]) @@ -952,6 +953,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'select (select 1 as `one`) as `one`, (select `first_name` from `person` order by `first_name` limit ?) as `person_first_name`', parameters: [1], }, + mssql: { + sql: `select (select 1 as "one") as "one", (select top 1 "first_name" from "person" order by "first_name") as "person_first_name"`, + parameters: [], + }, sqlite: { sql: 'select (select 1 as "one") as "one", (select "first_name" from "person" order by "first_name" limit ?) as "person_first_name"', parameters: [1], From e72124ca927c6ec0733c50cc564f0aebc6c9b5a8 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 24 Jul 2023 01:29:22 +0300 Subject: [PATCH 20/65] introduce mssql to set operation test suite. --- test/node/src/set-operation.test.ts | 53 ++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/test/node/src/set-operation.test.ts b/test/node/src/set-operation.test.ts index ddeb47e1d..fe4da4d79 100644 --- a/test/node/src/set-operation.test.ts +++ b/test/node/src/set-operation.test.ts @@ -1,5 +1,4 @@ import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -8,9 +7,10 @@ import { expect, insertDefaultDataSet, NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: set operations`, () => { let ctx: TestContext @@ -46,7 +46,10 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union select `id`, `name` from `pet` order by `name`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" order by "name"', + parameters: [], + }, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" order by "name"', parameters: [], @@ -83,7 +86,10 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union select `id`, `name` from `pet` union select `id`, `species` as `name` from `pet` order by `name`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union select "id", "species" as "name" from "pet" order by "name"', + parameters: [], + }, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union select "id", "species" as "name" from "pet" order by "name"', parameters: [], @@ -123,7 +129,10 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union select `id`, `name` from `pet` union select `id`, `species` as `name` from `pet` order by `name`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union select "id", "species" as "name" from "pet" order by "name"', + parameters: [], + }, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union select "id", "species" as "name" from "pet" order by "name"', parameters: [], @@ -144,7 +153,7 @@ for (const dialect of DIALECTS) { ]) }) - if (dialect === 'postgres' || dialect === 'mysql') { + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { it('should combine three select queries using union and an expression builder', async () => { const query = ctx.db .selectFrom('person') @@ -168,7 +177,10 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union (select `id`, `name` from `pet` union select `id`, `name` from `toy`) order by `name`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" union (select "id", "name" from "pet" union select "id", "name" from "toy") order by "name"', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -200,7 +212,10 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union all select `id`, `name` from `pet` order by `name`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" union all select "id", "name" from "pet" order by "name"', + parameters: [], + }, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union all select "id", "name" from "pet" order by "name"', parameters: [], @@ -235,7 +250,10 @@ for (const dialect of DIALECTS) { sql: 'select `id`, `first_name` as `name` from `person` union all select `id`, `name` from `pet` union select `id`, `name` from `toy` order by `name`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" union all select "id", "name" from "pet" union select "id", "name" from "toy" order by "name"', + parameters: [], + }, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union all select "id", "name" from "pet" union select "id", "name" from "toy" order by "name"', parameters: [], @@ -245,7 +263,7 @@ for (const dialect of DIALECTS) { await query.execute() }) - if (dialect === 'postgres' || dialect === 'sqlite') { + if (dialect === 'postgres' || dialect === 'mssql' || dialect === 'sqlite') { it('should combine two select queries using intersect', async () => { const query = ctx.db .selectFrom('person') @@ -259,7 +277,10 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" intersect select "id", "name" from "pet" order by "name"', + parameters: [], + }, sqlite: { sql: 'select "id", "first_name" as "name" from "person" intersect select "id", "name" from "pet" order by "name"', parameters: [], @@ -282,7 +303,10 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" except select "id", "name" from "pet" order by "name"', + parameters: [], + }, sqlite: { sql: 'select "id", "first_name" as "name" from "person" except select "id", "name" from "pet" order by "name"', parameters: [], @@ -308,7 +332,10 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union all select "id", "name" from "toy" intersect select "id", "name" from "pet" except select "id", "name" from "toy" order by "name"', + parameters: [], + }, sqlite: { sql: 'select "id", "first_name" as "name" from "person" union select "id", "name" from "pet" union all select "id", "name" from "toy" intersect select "id", "name" from "pet" except select "id", "name" from "toy" order by "name"', parameters: [], From ccfa1f49cce64190b3ba293e2cbc795bd445d118 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 24 Jul 2023 03:14:18 +0300 Subject: [PATCH 21/65] fix error rejections. --- src/dialect/mssql/mssql-driver.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index 9f3046528..80b380a86 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -170,7 +170,11 @@ class MssqlConnection implements DatabaseConnection { const request = new this.#tedious.Request(sql, (err, rowCount) => { if (err) { - reject(err) + if (err instanceof AggregateError) { + reject(err.errors) + } else { + reject(err) + } } else { promisedRowCount = rowCount } From 150845766da82fdc20a0b2a6298268a79729d6f6 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 24 Jul 2023 03:14:39 +0300 Subject: [PATCH 22/65] introduce mssql to schema module test suite. --- test/node/src/schema.test.ts | 100 +++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/test/node/src/schema.test.ts b/test/node/src/schema.test.ts index ce16e441a..c9e094b8b 100644 --- a/test/node/src/schema.test.ts +++ b/test/node/src/schema.test.ts @@ -1,7 +1,6 @@ import { ColumnMetadata, sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, expect, @@ -9,9 +8,10 @@ import { NOT_SUPPORTED, TestContext, testSql, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: schema`, () => { let ctx: TestContext @@ -241,7 +241,89 @@ for (const dialect of DIALECTS) { name: 'k', }) }) - } else { + } else if (dialect === 'mssql') { + it('should create a table with all data types', async () => { + const builder = ctx.db.schema + .createTable('test') + .addColumn('a', 'integer', (col) => + col + .notNull() + .modifyFront(sql`identity(1,1)`) + .primaryKey() + ) + .addColumn('b', 'integer', (col) => + col + .references('test.a') + .onDelete('no action') + .onUpdate('no action') + .check(sql`b < 10`) + ) + .addColumn('c', 'varchar') + .addColumn('d', 'varchar(10)') + .addColumn('e', 'bigint', (col) => col.unique().notNull()) + .addColumn('f', 'double precision') + .addColumn('g', 'real') + .addColumn('h', 'text') + .addColumn('i', sql`varchar(123)`) + .addColumn('j', 'numeric(6, 2)') + .addColumn('k', 'decimal(8, 4)') + .addColumn('l', sql`bit`, (col) => col.notNull().defaultTo(0)) + .addColumn('m', 'date') + .addColumn('n', 'datetime', (col) => + col.defaultTo(sql`current_timestamp`) + ) + .addColumn('o', sql`uniqueidentifier`, (col) => + col.notNull().defaultTo(sql`newid()`) + ) + .addColumn('p', sql`smallint`) + .addColumn('q', sql`int`) + .addColumn('r', 'double precision', (col) => col.notNull()) + .addColumn('s', 'time(6)') + .addColumn('t', 'timestamp', (col) => col.notNull()) + .addColumn('u', sql`datetime2`) + .addColumn('v', 'char(4)') + .addColumn('w', 'char') + .addColumn('x', 'binary') + + testSql(builder, dialect, { + mssql: { + sql: [ + 'create table "test"', + '("a" integer identity(1,1) not null primary key,', + '"b" integer references "test" ("a") on delete no action on update no action check (b < 10),', + '"c" varchar,', + '"d" varchar(10),', + '"e" bigint not null unique,', + '"f" double precision,', + '"g" real,', + '"h" text,', + '"i" varchar(123),', + '"j" numeric(6, 2),', + '"k" decimal(8, 4),', + '"l" bit default 0 not null,', + '"m" date,', + '"n" datetime default current_timestamp,', + '"o" uniqueidentifier default newid() not null,', + '"p" smallint,', + '"q" int,', + '"r" double precision not null,', + '"s" time(6),', + '"t" timestamp not null,', + '"u" datetime2,', + '"v" char(4),', + '"w" char,', + '"x" binary)', + ], + parameters: [], + }, + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await builder.execute() + }) + } else if (dialect === 'sqlite') { it('should create a table with all data types', async () => { const builder = ctx.db.schema .createTable('test') @@ -335,6 +417,13 @@ for (const dialect of DIALECTS) { name: 'l', }) }) + } else { + throw new Error(`Unknown dialect: ${dialect}`) + } + + // TODO: ... remove and continue + if (dialect === 'mssql') { + return } it('should create a table with a unique constraints', async () => { @@ -859,6 +948,11 @@ for (const dialect of DIALECTS) { }) }) + // TODO: ... remove and continue + if (dialect === 'mssql') { + return + } + describe('drop table', () => { beforeEach(async () => { await ctx.db.schema From c5ab5dfc81022b4e44bb7de392eb52245fc0e2a1 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 29 Jul 2023 17:55:53 +0300 Subject: [PATCH 23/65] remove mssql, add tedious and tarn. --- package-lock.json | 143 +++++++++++++++++++++++----------------------- package.json | 3 +- 2 files changed, 74 insertions(+), 72 deletions(-) diff --git a/package-lock.json b/package-lock.json index dba37b84b..cccbbce9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,13 +26,14 @@ "concurrently": "^8.1.0", "esbuild": "^0.17.19", "mocha": "^10.2.0", - "mssql": "^9.1.1", "mysql2": "^3.3.3", "pg": "^8.11.0", "pg-cursor": "^2.10.0", "playwright": "^1.34.3", "prettier": "^2.8.8", "sinon": "^15.1.0", + "tarn": "^3.0.2", + "tedious": "^16.4.0", "tsd": "^0.28.1", "typedoc": "^0.24.8", "typescript": "^5.1.6" @@ -99,9 +100,9 @@ } }, "node_modules/@azure/core-lro": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.3.tgz", - "integrity": "sha512-ubkOf2YCnVtq7KqEJQqAI8dDD5rH1M6OP5kW0KO/JQyTaxLA0N0pjFWvvaysCj9eHMNBcuuoZXhhl0ypjod2DA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", + "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", "dev": true, "dependencies": { "@azure/abort-controller": "^1.0.0", @@ -857,12 +858,6 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, - "node_modules/@tediousjs/connection-string": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.4.2.tgz", - "integrity": "sha512-1R9UC7Qc5wief2oJL+c1+d7v1/oPBayL85u8L/jV2DzIKput1TZ8ZUjj2nxQaSfzu210zp0oFWUrYUiUs8NhBQ==", - "dev": true - }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -1013,6 +1008,18 @@ "@types/node": "*" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1500,15 +1507,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || >=14" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1957,6 +1955,15 @@ "integrity": "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==", "dev": true }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -2789,16 +2796,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -3309,26 +3312,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "node_modules/mssql": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-9.1.1.tgz", - "integrity": "sha512-m0yTx9xzUtTvJpWJHqknUXUDPRnJXZYOOFNygnNIXn1PBkLsC/rkXQdquObd+M0ZPlBhGC00Jg28zG0wCl7VWg==", - "dev": true, - "dependencies": { - "@tediousjs/connection-string": "^0.4.1", - "commander": "^9.4.0", - "debug": "^4.3.3", - "rfdc": "^1.3.0", - "tarn": "^3.0.2", - "tedious": "^15.0.1" - }, - "bin": { - "mssql": "bin/mssql" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/mysql2": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.3.3.tgz", @@ -3972,6 +3955,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -4286,12 +4278,6 @@ "node": ">=0.10.0" } }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4824,37 +4810,37 @@ } }, "node_modules/tedious": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/tedious/-/tedious-15.1.3.tgz", - "integrity": "sha512-166EpRm5qknwhEisjZqz/mF7k14fXKJYHRg6XiAXVovd/YkyHJ3SG4Ppy89caPaNFfRr7PVYe+s4dAvKaCMFvw==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-16.4.0.tgz", + "integrity": "sha512-WUWtO18n43GnKI367lVEtmbBxAaTIpTONuZ87sTEMMUcQ9gy5D9H6TCHBKNz/6yYIKnCfjE9wgAc2dR4qiDiaA==", "dev": true, "dependencies": { "@azure/identity": "^2.0.4", "@azure/keyvault-keys": "^4.4.0", - "@js-joda/core": "^5.2.0", - "bl": "^5.0.0", - "es-aggregate-error": "^1.0.8", + "@js-joda/core": "^5.5.3", + "bl": "^6.0.3", + "es-aggregate-error": "^1.0.9", "iconv-lite": "^0.6.3", "js-md4": "^0.3.2", "jsbi": "^4.3.0", "native-duplexpair": "^1.0.0", - "node-abort-controller": "^3.0.1", - "punycode": "^2.1.0", + "node-abort-controller": "^3.1.1", + "punycode": "^2.3.0", "sprintf-js": "^1.1.2" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/tedious/node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.3.tgz", + "integrity": "sha512-ZmReEQkPP4zOjCHVzGpXYLvf95/HnvwsNZ1sh2dhoy6OxqX9Sl3JF7UmoKXlXE40AjldnWlsSxvqDiDrgSCJDA==", "dev": true, "dependencies": { "buffer": "^6.0.3", "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "readable-stream": "^4.2.0" } }, "node_modules/tedious/node_modules/buffer": { @@ -4881,6 +4867,22 @@ "ieee754": "^1.2.1" } }, + "node_modules/tedious/node_modules/readable-stream": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", + "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5154,17 +5156,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.10.tgz", - "integrity": "sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" diff --git a/package.json b/package.json index b11b16a57..b11c0671b 100644 --- a/package.json +++ b/package.json @@ -86,13 +86,14 @@ "concurrently": "^8.1.0", "esbuild": "^0.17.19", "mocha": "^10.2.0", - "mssql": "^9.1.1", "mysql2": "^3.3.3", "pg": "^8.11.0", "pg-cursor": "^2.10.0", "playwright": "^1.34.3", "prettier": "^2.8.8", "sinon": "^15.1.0", + "tarn": "^3.0.2", + "tedious": "^16.4.0", "tsd": "^0.28.1", "typedoc": "^0.24.8", "typescript": "^5.1.6" From bf5f93e35700ad53f0ccaedfce2a966f77711b31 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 29 Jul 2023 23:41:51 +0300 Subject: [PATCH 24/65] initial pool implementation using tarn. --- src/dialect/mssql/mssql-dialect-config.ts | 38 +-- src/dialect/mssql/mssql-driver.ts | 282 +++++++++++++--------- src/driver/driver.ts | 1 + test/node/src/test-setup.ts | 35 ++- 4 files changed, 207 insertions(+), 149 deletions(-) diff --git a/src/dialect/mssql/mssql-dialect-config.ts b/src/dialect/mssql/mssql-dialect-config.ts index 4f955698b..5e2bddc66 100644 --- a/src/dialect/mssql/mssql-dialect-config.ts +++ b/src/dialect/mssql/mssql-dialect-config.ts @@ -1,34 +1,22 @@ -import { ConnectionPool } from 'mssql' -import { Request, TYPES } from 'tedious' -import { DatabaseConnection } from '../../driver/database-connection.js' +import { Pool } from 'tarn' +import { Connection, ISOLATION_LEVEL, Request, TYPES } from 'tedious' export interface MssqlDialectConfig { - /** - * A mssql Pool instance or a function that returns one. - * - * If a function is provided, it's called once when the first query is executed. - * - * https://github.com/sidorares/node-mysql2#using-connection-pools - */ - pool: ConnectionPool | (() => Promise) - - tedious: Tedious + connectionFactory: () => Connection | Promise + Tarn: Tarn + Tedious: Tedious +} - /** - * Called once for each created connection. - */ - onCreateConnection?: (connection: DatabaseConnection) => Promise +export interface Tarn { + Pool: typeof Pool + options: Omit< + ConstructorParameters[0], + 'create' | 'destroy' | 'validate' + > } export interface Tedious { Request: typeof Request + ISOLATION_LEVEL: typeof ISOLATION_LEVEL TYPES: typeof TYPES } - -export interface MssqlPool { - // TODO: ... -} - -export interface MssqlPoolConnection { - // TODO: ... -} diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index 80b380a86..93dbd917c 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -1,17 +1,20 @@ -import { Connection, ConnectionPool, Request } from 'mssql' -import type { ColumnValue, Request as TediousRequest } from 'tedious' +import { Pool } from 'tarn' +import { ColumnValue, Connection, Request } from 'tedious' import { DatabaseConnection, QueryResult, } from '../../driver/database-connection.js' -import { Driver, TransactionSettings } from '../../driver/driver.js' +import { + Driver, + IsolationLevel, + TransactionSettings, +} from '../../driver/driver.js' import { freeze, isBigInt, isBoolean, isBuffer, isDate, - isFunction, isNull, isNumber, isString, @@ -22,97 +25,120 @@ import { CompiledQuery } from '../../query-compiler/compiled-query.js' import { extendStackTrace } from '../../util/stack-trace-utils.js' const PRIVATE_RELEASE_METHOD = Symbol() +const PRIVATE_DESTROY_METHOD = Symbol() export class MssqlDriver implements Driver { readonly #config: MssqlDialectConfig - readonly #connections = new WeakMap() - #pool?: ConnectionPool + readonly #pool: Pool constructor(config: MssqlDialectConfig) { this.#config = freeze({ ...config }) + + this.#pool = new this.#config.Tarn.Pool({ + ...this.#config.Tarn.options, + create: async () => { + const connection = await this.#config.connectionFactory() + + await new Promise((resolve, reject) => + connection.connect((error) => { + if (error) reject(error) + else resolve(undefined) + }) + ) + + return new MssqlConnection(connection, this.#config.Tedious) + }, + destroy: async (connection) => { + await connection[PRIVATE_DESTROY_METHOD]() + }, + // @ts-ignore + validate: async (connection) => { + try { + await connection.executeQuery(CompiledQuery.raw('select 1')) + + return true + } catch (err) { + return false + } + }, + }) } async init(): Promise { - this.#pool = isFunction(this.#config.pool) - ? await this.#config.pool() - : this.#config.pool + // noop } async acquireConnection(): Promise { - const pool = await this.#pool!.connect() - const rawConnection = await pool.pool.acquire().promise - - let connection = this.#connections.get(rawConnection) - - if (!connection) { - connection = new MssqlConnection( - rawConnection, - pool, - this.#config.tedious - ) - this.#connections.set(rawConnection, connection) - - if (this.#config.onCreateConnection) { - await this.#config.onCreateConnection(connection) - } - } - - return connection + return await this.#pool.acquire().promise } async beginTransaction( - connection: DatabaseConnection, + connection: MssqlConnection, settings: TransactionSettings ): Promise { - // TODO: ... - throw new Error('Not implemented') + await connection.beginTransaction(settings) } - async commitTransaction(connection: DatabaseConnection): Promise { - // TODO: ... - throw new Error('Not implemented') + async commitTransaction(connection: MssqlConnection): Promise { + await connection.commitTransaction() } - async rollbackTransaction(connection: DatabaseConnection): Promise { - // TODO: ... - throw new Error('Not implemented') + async rollbackTransaction(connection: MssqlConnection): Promise { + await connection.rollbackTransaction() } async releaseConnection(connection: MssqlConnection): Promise { - connection[PRIVATE_RELEASE_METHOD]() + await connection[PRIVATE_RELEASE_METHOD]() + this.#pool.release(connection) } async destroy(): Promise { - if (this.#pool) { - const pool = this.#pool - this.#pool = undefined - await pool.close() - } + await this.#pool.destroy() } } class MssqlConnection implements DatabaseConnection { - readonly #pool: ConnectionPool - readonly #rawConnection: Connection + readonly #connection: Connection readonly #tedious: Tedious - constructor( - rawConnection: Connection, - pool: ConnectionPool, - tedious: Tedious - ) { - this.#pool = pool - this.#rawConnection = rawConnection + constructor(connection: Connection, tedious: Tedious) { + this.#connection = connection this.#tedious = tedious } + async beginTransaction(settings: TransactionSettings): Promise { + const { isolationLevel } = settings + + await new Promise((resolve, reject) => + this.#connection.beginTransaction( + (error) => { + if (error) reject(error) + else resolve(undefined) + }, + isolationLevel ? Date.now().toString() : undefined, + isolationLevel + ? this.#getTediousIsolationLevel(isolationLevel) + : undefined + ) + ) + } + + async commitTransaction(): Promise { + await new Promise((resolve, reject) => + this.#connection.commitTransaction((error) => { + if (error) reject(error) + else resolve(undefined) + }) + ) + } + async executeQuery(compiledQuery: CompiledQuery): Promise> { try { const { rowCount, rows } = await new Promise<{ rows: O[] rowCount: number }>((resolve, reject) => - this.#rawConnection.execSql( + this.#connection.execSql( this.#createTediousRequest(compiledQuery, reject, resolve) ) ) @@ -126,6 +152,15 @@ class MssqlConnection implements DatabaseConnection { } } + async rollbackTransaction(): Promise { + await new Promise((resolve, reject) => + this.#connection.rollbackTransaction((error) => { + if (error) reject(error) + else resolve(undefined) + }) + ) + } + async *streamQuery( compiledQuery: CompiledQuery, chunkSize: number @@ -134,35 +169,35 @@ class MssqlConnection implements DatabaseConnection { throw new Error('chunkSize must be a positive integer') } - const request = this.#createMssqlRequest(compiledQuery) - request.stream = true + // const request = this.#createMssqlRequest(this.#request, compiledQuery) + // request.stream = true - const cursor = new MssqlCursor(request) + // const cursor = new MssqlCursor(request) - try { - request.query(compiledQuery.sql) + // try { + // request.query(compiledQuery.sql) - while (true) { - const rows = await cursor.read(chunkSize) + // while (true) { + // const rows = await cursor.read(chunkSize) - if (rows.length === 0) { - break - } + // if (rows.length === 0) { + // break + // } - yield { - rows: rows, - } - } - } finally { - request.cancel() - } + // yield { + // rows: rows, + // } + // } + // } finally { + // request.cancel() + // } } #createTediousRequest( compiledQuery: CompiledQuery, reject: (reason?: any) => void, resolve: (value: any) => void - ): TediousRequest { + ): Request { const { parameters, sql } = compiledQuery let promisedRowCount: number | undefined @@ -213,14 +248,6 @@ class MssqlConnection implements DatabaseConnection { return request } - #createMssqlRequest(compiledQuery: CompiledQuery): Request { - return compiledQuery.parameters.reduce( - (request: Request, param, index) => - request.input(String(index + 1), param), - this.#pool.request() - ) - } - #getTediousDataType(value: unknown): any { if (isNull(value) || isUndefined(value) || isString(value)) { return this.#tedious.TYPES.NVarChar @@ -253,46 +280,79 @@ class MssqlConnection implements DatabaseConnection { return this.#tedious.TYPES.NVarChar } - [PRIVATE_RELEASE_METHOD](): void { - this.#pool.pool.release(this.#rawConnection) + #getTediousIsolationLevel(isolationLevel: IsolationLevel) { + const { ISOLATION_LEVEL } = this.#tedious + + const mapper: Record< + IsolationLevel, + (typeof ISOLATION_LEVEL)[keyof typeof ISOLATION_LEVEL] + > = { + [isolationLevel]: ISOLATION_LEVEL.NO_CHANGE, + 'read committed': ISOLATION_LEVEL.READ_COMMITTED, + 'read uncommitted': ISOLATION_LEVEL.READ_UNCOMMITTED, + 'repeatable read': ISOLATION_LEVEL.REPEATABLE_READ, + serializable: ISOLATION_LEVEL.SERIALIZABLE, + snapshot: ISOLATION_LEVEL.SNAPSHOT, + } + + return mapper[isolationLevel] || undefined } -} -class MssqlCursor { - readonly #request: Request - readonly #chunk: O[] = [] + [PRIVATE_RELEASE_METHOD](): Promise { + return new Promise((resolve, reject) => { + this.#connection.reset((error) => { + if (error) reject(error) + else resolve(undefined) + }) + }) + } - constructor(request: Request) { - this.#request = request + [PRIVATE_DESTROY_METHOD](): Promise { + return new Promise((resolve) => { + this.#connection.once('end', () => { + resolve(undefined) + }) + + this.#connection.close() + }) } +} - async read(chunkSize: number): Promise { - if (this.#chunk.length >= chunkSize) { - return this.#chunk.splice(0, chunkSize) - } +// class MssqlCursor { +// readonly #request: Request +// readonly #chunk: O[] = [] - return new Promise((resolve, reject) => { - const rowListener = (row: O) => { - this.#chunk.push(row) +// constructor(request: Request) { +// this.#request = request +// } - if (this.#chunk.length >= chunkSize) { - this.#request.pause() - this.#request.off('row', rowListener) - resolve(this.#chunk.splice(0, chunkSize)) - } - } +// async read(chunkSize: number): Promise { +// if (this.#chunk.length >= chunkSize) { +// return this.#chunk.splice(0, chunkSize) +// } - this.#request.on('row', rowListener) +// return new Promise((resolve, reject) => { +// const rowListener = (row: O) => { +// this.#chunk.push(row) - this.#request.once('error', reject) +// if (this.#chunk.length >= chunkSize) { +// this.#request.pause() +// this.#request.off('row', rowListener) +// resolve(this.#chunk.splice(0, chunkSize)) +// } +// } - this.#request.once('done', () => { - if (this.#chunk.length < chunkSize) { - resolve(this.#chunk) - } - }) +// this.#request.on('row', rowListener) - this.#request.resume() - }) - } -} +// this.#request.once('error', reject) + +// this.#request.once('done', () => { +// if (this.#chunk.length < chunkSize) { +// resolve(this.#chunk) +// } +// }) + +// this.#request.resume() +// }) +// } +// } diff --git a/src/driver/driver.ts b/src/driver/driver.ts index c5f9d9ed6..9d4267726 100644 --- a/src/driver/driver.ts +++ b/src/driver/driver.ts @@ -57,6 +57,7 @@ export const TRANSACTION_ISOLATION_LEVELS = [ 'read committed', 'repeatable read', 'serializable', + 'snapshot', ] as const export type IsolationLevel = ArrayItemType diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 76c6f0128..56d3c14ca 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -5,9 +5,9 @@ import * as Cursor from 'pg-cursor' import { Pool, PoolConfig } from 'pg' import { createPool } from 'mysql2' import * as Database from 'better-sqlite3' +import * as Tarn from 'tarn' import * as Tedious from 'tedious' import { PoolOptions } from 'mysql2' -import { ConnectionPool, config } from 'mssql' chai.use(chaiSubset) chai.use(chaiAsPromised) @@ -134,21 +134,22 @@ export const DIALECT_CONFIGS = { } satisfies PoolOptions, mssql: { - server: 'localhost', - user: 'sa', - password: 'KyselyTest0', - // parseJSON: true, + authentication: { + options: { + password: 'KyselyTest0', + userName: 'sa', + }, + type: 'default', + }, options: { - port: 21433, + connectTimeout: 3000, database: 'kysely_test', - trustedConnection: true, + port: 21433, trustServerCertificate: true, useUTC: true, }, - pool: { - max: POOL_SIZE, - }, - } satisfies config, + server: 'localhost', + } satisfies Tedious.ConnectionConfig, sqlite: { databasePath: ':memory:', @@ -173,9 +174,17 @@ export const DB_CONFIGS: PerDialect = { mssql: { dialect: new MssqlDialect({ - pool: async () => new ConnectionPool(DIALECT_CONFIGS.mssql), - tedious: Tedious, + connectionFactory: () => new Tedious.Connection(DIALECT_CONFIGS.mssql), + Tarn: { + options: { + max: POOL_SIZE, + min: 0, + }, + ...Tarn, + }, + Tedious, }), + plugins: PLUGINS, }, sqlite: { From 32bda8e00bfca325c27e12e01d5978d4c18a067f Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 29 Jul 2023 23:48:04 +0300 Subject: [PATCH 25/65] fix failing where tests. --- test/node/src/where.test.ts | 177 +++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 84 deletions(-) diff --git a/test/node/src/where.test.ts b/test/node/src/where.test.ts index c412df1b1..963fe57fe 100644 --- a/test/node/src/where.test.ts +++ b/test/node/src/where.test.ts @@ -513,96 +513,104 @@ for (const dialect of DIALECTS_WITH_MSSQL) { ]) }) - it('a `where in` query with tuples', async () => { - const query = ctx.db - .selectFrom('person') - .selectAll() - .where((eb) => - eb(eb.refTuple('first_name', 'last_name'), 'in', [ - eb.tuple('Jennifer', 'Aniston'), - eb.tuple('Sylvester', 'Stallone'), - ]) - ) - .orderBy('first_name asc') + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { + it('a `where in` query with tuples', async () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .where((eb) => + eb(eb.refTuple('first_name', 'last_name'), 'in', [ + eb.tuple('Jennifer', 'Aniston'), + eb.tuple('Sylvester', 'Stallone'), + ]) + ) + .orderBy('first_name asc') - testSql(query, dialect, { - postgres: { - sql: 'select * from "person" where ("first_name", "last_name") in (($1, $2), ($3, $4)) order by "first_name" asc', - parameters: ['Jennifer', 'Aniston', 'Sylvester', 'Stallone'], - }, - mysql: { - sql: 'select * from `person` where (`first_name`, `last_name`) in ((?, ?), (?, ?)) order by `first_name` asc', - parameters: ['Jennifer', 'Aniston', 'Sylvester', 'Stallone'], - }, - sqlite: { - sql: 'select * from "person" where ("first_name", "last_name") in ((?, ?), (?, ?)) order by "first_name" asc', - parameters: ['Jennifer', 'Aniston', 'Sylvester', 'Stallone'], - }, - }) + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" where ("first_name", "last_name") in (($1, $2), ($3, $4)) order by "first_name" asc', + parameters: ['Jennifer', 'Aniston', 'Sylvester', 'Stallone'], + }, + mysql: { + sql: 'select * from `person` where (`first_name`, `last_name`) in ((?, ?), (?, ?)) order by `first_name` asc', + parameters: ['Jennifer', 'Aniston', 'Sylvester', 'Stallone'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'select * from "person" where ("first_name", "last_name") in ((?, ?), (?, ?)) order by "first_name" asc', + parameters: ['Jennifer', 'Aniston', 'Sylvester', 'Stallone'], + }, + }) - const persons = await query.execute() - expect(persons).to.have.length(2) - expect(persons).to.containSubset([ - { - first_name: 'Jennifer', - last_name: 'Aniston', - gender: 'female', - }, - { - first_name: 'Sylvester', - last_name: 'Stallone', - gender: 'male', - }, - ]) - }) + const persons = await query.execute() + expect(persons).to.have.length(2) + expect(persons).to.containSubset([ + { + first_name: 'Jennifer', + last_name: 'Aniston', + gender: 'female', + }, + { + first_name: 'Sylvester', + last_name: 'Stallone', + gender: 'male', + }, + ]) + }) - it('a `where in` query with tuples and a subquery', async () => { - const query = ctx.db - .selectFrom('person') - .selectAll() - .where((eb) => - eb( - eb.refTuple('first_name', 'last_name'), - 'in', - eb - .selectFrom('person as p2') - .select(['p2.first_name', 'p2.last_name']) - .where('first_name', 'in', ['Arnold', 'Sylvester']) - .$asTuple('first_name', 'last_name') + it('a `where in` query with tuples and a subquery', async () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .where((eb) => + eb( + eb.refTuple('first_name', 'last_name'), + 'in', + eb + .selectFrom('person as p2') + .select(['p2.first_name', 'p2.last_name']) + .where('first_name', 'in', ['Arnold', 'Sylvester']) + .$asTuple('first_name', 'last_name') + ) ) - ) - .orderBy('first_name asc') + .orderBy('first_name asc') - testSql(query, dialect, { - postgres: { - sql: 'select * from "person" where ("first_name", "last_name") in (select "p2"."first_name", "p2"."last_name" from "person" as "p2" where "first_name" in ($1, $2)) order by "first_name" asc', - parameters: ['Arnold', 'Sylvester'], - }, - mysql: { - sql: 'select * from `person` where (`first_name`, `last_name`) in (select `p2`.`first_name`, `p2`.`last_name` from `person` as `p2` where `first_name` in (?, ?)) order by `first_name` asc', - parameters: ['Arnold', 'Sylvester'], - }, - sqlite: { - sql: 'select * from "person" where ("first_name", "last_name") in (select "p2"."first_name", "p2"."last_name" from "person" as "p2" where "first_name" in (?, ?)) order by "first_name" asc', - parameters: ['Arnold', 'Sylvester'], - }, - }) + testSql(query, dialect, { + postgres: { + sql: 'select * from "person" where ("first_name", "last_name") in (select "p2"."first_name", "p2"."last_name" from "person" as "p2" where "first_name" in ($1, $2)) order by "first_name" asc', + parameters: ['Arnold', 'Sylvester'], + }, + mysql: { + sql: 'select * from `person` where (`first_name`, `last_name`) in (select `p2`.`first_name`, `p2`.`last_name` from `person` as `p2` where `first_name` in (?, ?)) order by `first_name` asc', + parameters: ['Arnold', 'Sylvester'], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'select * from "person" where ("first_name", "last_name") in (select "p2"."first_name", "p2"."last_name" from "person" as "p2" where "first_name" in (?, ?)) order by "first_name" asc', + parameters: ['Arnold', 'Sylvester'], + }, + }) - const persons = await query.execute() - expect(persons).to.have.length(2) - expect(persons).to.containSubset([ - { - first_name: 'Arnold', - last_name: 'Schwarzenegger', - gender: 'male', - }, - { - first_name: 'Sylvester', - last_name: 'Stallone', - gender: 'male', - }, - ]) - }) + const persons = await query.execute() + expect(persons).to.have.length(2) + expect(persons).to.containSubset([ + { + first_name: 'Arnold', + last_name: 'Schwarzenegger', + gender: 'male', + }, + { + first_name: 'Sylvester', + last_name: 'Stallone', + gender: 'male', + }, + ]) + }) + } it('two where expressions', async () => { const query = ctx.db @@ -1020,6 +1028,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'select `first_name` from `person` where ? = any((select `name` from `pet` where `pet`.`owner_id` = `person`.`id`))', parameters: ['Cat Stevens'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) From ae171189d1a10dfc68c662bb8c4d2887095b382f Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 29 Jul 2023 23:49:26 +0300 Subject: [PATCH 26/65] fix compilation errors at select tests. --- test/node/src/select.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/node/src/select.test.ts b/test/node/src/select.test.ts index dd009225f..5b895205d 100644 --- a/test/node/src/select.test.ts +++ b/test/node/src/select.test.ts @@ -662,6 +662,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'select `last_name` from `person` where `first_name` = ? for update skip locked', parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -688,6 +689,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'select `last_name` from `person` where `first_name` = ? for update skip locked', parameters: ['Jennifer'], }, + mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) From a8f4a3c6f0cb1bcfef254343105c08a295621e72 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 29 Jul 2023 23:52:43 +0300 Subject: [PATCH 27/65] introduce mssql to schema module test suite, pt. 2. --- test/node/src/schema.test.ts | 528 ++++++++++++++++++++++++----------- 1 file changed, 361 insertions(+), 167 deletions(-) diff --git a/test/node/src/schema.test.ts b/test/node/src/schema.test.ts index c9e094b8b..eb1dff5e4 100644 --- a/test/node/src/schema.test.ts +++ b/test/node/src/schema.test.ts @@ -1,3 +1,4 @@ +import { fail } from 'assert' import { ColumnMetadata, sql } from '../../../' import { @@ -284,6 +285,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { .addColumn('v', 'char(4)') .addColumn('w', 'char') .addColumn('x', 'binary') + .addColumn('y', sql``, (col) => col.modifyEnd(sql`as (a + f)`)) testSql(builder, dialect, { mssql: { @@ -312,7 +314,8 @@ for (const dialect of DIALECTS_WITH_MSSQL) { '"u" datetime2,', '"v" char(4),', '"w" char,', - '"x" binary)', + '"x" binary,', + '"y" as (a + f))', ], parameters: [], }, @@ -421,11 +424,6 @@ for (const dialect of DIALECTS_WITH_MSSQL) { throw new Error(`Unknown dialect: ${dialect}`) } - // TODO: ... remove and continue - if (dialect === 'mssql') { - return - } - it('should create a table with a unique constraints', async () => { const builder = ctx.db.schema .createTable('test') @@ -444,7 +442,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create table `test` (`a` varchar(255), `b` varchar(255), `c` varchar(255), constraint `a_b_unique` unique (`a`, `b`), constraint `b_c_unique` unique (`b`, `c`))', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create table "test" ("a" varchar(255), "b" varchar(255), "c" varchar(255), constraint "a_b_unique" unique ("a", "b"), constraint "b_c_unique" unique ("b", "c"))', + parameters: [], + }, sqlite: { sql: 'create table "test" ("a" varchar(255), "b" varchar(255), "c" varchar(255), constraint "a_b_unique" unique ("a", "b"), constraint "b_c_unique" unique ("b", "c"))', parameters: [], @@ -472,7 +473,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create table `test` (`a` integer, `b` integer, `c` integer, constraint `check_a` check (a > 1), constraint `check_b` check (b < c))', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create table "test" ("a" integer, "b" integer, "c" integer, constraint "check_a" check (a > 1), constraint "check_b" check (b < c))', + parameters: [], + }, sqlite: { sql: 'create table "test" ("a" integer, "b" integer, "c" integer, constraint "check_a" check (a > 1), constraint "check_b" check (b < c))', parameters: [], @@ -498,7 +502,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create table `test` (`a` integer, `b` integer, constraint `primary` primary key (`a`, `b`))', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create table "test" ("a" integer, "b" integer, constraint "primary" primary key ("a", "b"))', + parameters: [], + }, sqlite: { sql: 'create table "test" ("a" integer, "b" integer, constraint "primary" primary key ("a", "b"))', parameters: [], @@ -534,7 +541,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create table `test` (`a` integer, `b` integer, constraint `foreign_key` foreign key (`a`, `b`) references `test2` (`c`, `d`))', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create table "test" ("a" integer, "b" integer, constraint "foreign_key" foreign key ("a", "b") references "test2" ("c", "d"))', + parameters: [], + }, sqlite: { sql: 'create table "test" ("a" integer, "b" integer, constraint "foreign_key" foreign key ("a", "b") references "test2" ("c", "d"))', parameters: [], @@ -544,7 +554,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { it('should support schemas in foreign key target table', async () => { await ctx.db.schema .createTable('test2') @@ -560,7 +570,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { .addForeignKeyConstraint( 'foreign_key', ['a', 'b'], - 'public.test2', + dialect === 'postgres' ? 'public.test2' : 'dbo.test2', ['c', 'd'] ) @@ -570,7 +580,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create table "test" ("a" integer, "b" integer, constraint "foreign_key" foreign key ("a", "b") references "dbo"."test2" ("c", "d"))', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -607,7 +620,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create table `test` (`a` integer, `b` integer, constraint `foreign_key` foreign key (`a`, `b`) references `test2` (`c`, `d`) on update cascade)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create table "test" ("a" integer, "b" integer, constraint "foreign_key" foreign key ("a", "b") references "test2" ("c", "d") on update cascade)', + parameters: [], + }, sqlite: { sql: 'create table "test" ("a" integer, "b" integer, constraint "foreign_key" foreign key ("a", "b") references "test2" ("c", "d") on update cascade)', parameters: [], @@ -617,58 +633,96 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - it("should create a table if it doesn't already exist", async () => { - const builder = ctx.db.schema - .createTable('test') - .ifNotExists() - .addColumn('id', 'integer', (col) => col.primaryKey()) + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { + it("should create a table if it doesn't already exist", async () => { + const builder = ctx.db.schema + .createTable('test') + .ifNotExists() + .addColumn('id', 'integer', (col) => col.primaryKey()) - testSql(builder, dialect, { - postgres: { - sql: 'create table if not exists "test" ("id" integer primary key)', - parameters: [], - }, - mysql: { - sql: 'create table if not exists `test` (`id` integer primary key)', - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'create table if not exists "test" ("id" integer primary key)', - parameters: [], - }, + testSql(builder, dialect, { + postgres: { + sql: 'create table if not exists "test" ("id" integer primary key)', + parameters: [], + }, + mysql: { + sql: 'create table if not exists `test` (`id` integer primary key)', + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'create table if not exists "test" ("id" integer primary key)', + parameters: [], + }, + }) + + await builder.execute() }) - await builder.execute() - }) + it('should create a temporary table', async () => { + const builder = ctx.db.schema + .createTable('test') + .temporary() + .addColumn('id', 'integer', (col) => col.primaryKey()) - it('should create a temporary table', async () => { - const builder = ctx.db.schema - .createTable('test') - .temporary() - .addColumn('id', 'integer', (col) => col.primaryKey()) + testSql(builder, dialect, { + postgres: { + sql: 'create temporary table "test" ("id" integer primary key)', + parameters: [], + }, + mysql: { + sql: 'create temporary table `test` (`id` integer primary key)', + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'create temporary table "test" ("id" integer primary key)', + parameters: [], + }, + }) - testSql(builder, dialect, { - postgres: { - sql: 'create temporary table "test" ("id" integer primary key)', - parameters: [], - }, - mysql: { - sql: 'create temporary table `test` (`id` integer primary key)', - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'create temporary table "test" ("id" integer primary key)', - parameters: [], - }, + await builder.execute() }) + } - await builder.execute() - }) + if (dialect === 'mssql') { + it('should create a temporary table', async () => { + await ctx.db.connection().execute(async (conn) => { + const builder = conn.schema + .createTable('##test') + .addColumn('id', 'integer', (col) => col.primaryKey()) + + testSql(builder, dialect, { + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + mssql: { + sql: 'create table "##test" ("id" integer primary key)', + parameters: [], + }, + sqlite: NOT_SUPPORTED, + }) + + await builder.execute() + + await sql`select * from "##test"`.execute(conn) + }) + + try { + await sql`select * from "##test"`.execute(ctx.db) + + fail() // table is not gone! + } catch (err) { + // it works! + } + }) + } if (dialect === 'postgres') { - it('should create a temporary table witn on commit statement', async () => { + it('should create a temporary table with on commit statement', async () => { const builder = ctx.db.schema .createTable('test') .temporary() @@ -689,22 +743,27 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) } - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { + const schema = dialect === 'postgres' ? 'public' : 'dbo' + it('should create a table in specific schema', async () => { const builder = ctx.db.schema - .createTable('public.test') - .addColumn('id', 'serial', (col) => col.primaryKey()) - .addColumn('foreign_key', 'integer', (col) => - col.references('public.test.id') + .createTable(`${schema}.test`) + .addColumn('id', 'varchar(32)', (col) => col.primaryKey()) + .addColumn('foreign_key', 'varchar(32)', (col) => + col.references(`${schema}.test.id`) ) testSql(builder, dialect, { postgres: { - sql: 'create table "public"."test" ("id" serial primary key, "foreign_key" integer references "public"."test" ("id"))', + sql: 'create table "public"."test" ("id" varchar(32) primary key, "foreign_key" varchar(32) references "public"."test" ("id"))', parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create table "dbo"."test" ("id" varchar(32) primary key, "foreign_key" varchar(32) references "dbo"."test" ("id"))', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -933,7 +992,14 @@ for (const dialect of DIALECTS_WITH_MSSQL) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'create table "test"', + '("id" integer not null,', + `"call_me" varchar(10) default 'maybe')`, + ], + parameters: [], + }, sqlite: { sql: [ 'create table "test"', @@ -948,11 +1014,6 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) }) - // TODO: ... remove and continue - if (dialect === 'mssql') { - return - } - describe('drop table', () => { beforeEach(async () => { await ctx.db.schema @@ -973,7 +1034,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop table `test`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'drop table "test"', + parameters: [], + }, sqlite: { sql: 'drop table "test"', parameters: [], @@ -995,7 +1059,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop table if exists `test`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'drop table if exists "test"', + parameters: [], + }, sqlite: { sql: 'drop table if exists "test"', parameters: [], @@ -1004,7 +1071,8 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - if (dialect !== 'sqlite') { + + if (dialect == 'postgres' || dialect === 'mysql' || dialect === 'mssql') { it('should drop a table cascade', async () => { const builder = ctx.db.schema.dropTable('test').cascade() testSql(builder, dialect, { @@ -1016,7 +1084,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop table `test` cascade', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'drop table "test" cascade', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) }) @@ -1032,7 +1103,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop table if exists `test` cascade', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'drop table if exists "test" cascade', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) }) @@ -1065,7 +1139,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create index `test_first_name_index` on `test` (`first_name`)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create index "test_first_name_index" on "test" ("first_name")', + parameters: [], + }, sqlite: { sql: 'create index "test_first_name_index" on "test" ("first_name")', parameters: [], @@ -1075,7 +1152,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - if (dialect !== 'mysql') { + if (dialect === 'postgres' || dialect === 'sqlite') { it('should create an index if not exists', async () => { await ctx.db.schema .createIndex('test_first_name_index') @@ -1094,12 +1171,12 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create index if not exists "test_first_name_index" on "test" ("first_name")', parameters: [], }, + mysql: NOT_SUPPORTED, mssql: NOT_SUPPORTED, sqlite: { sql: 'create index if not exists "test_first_name_index" on "test" ("first_name")', parameters: [], }, - mysql: NOT_SUPPORTED, }) await builder.execute() @@ -1122,7 +1199,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create unique index `test_first_name_index` on `test` (`first_name`)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create unique index "test_first_name_index" on "test" ("first_name")', + parameters: [], + }, sqlite: { sql: 'create unique index "test_first_name_index" on "test" ("first_name")', parameters: [], @@ -1145,15 +1225,9 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create index "test_first_name_index" on "test" using hash ("first_name")', parameters: [], }, - mysql: { - sql: 'create index `test_first_name_index` on `test` using hash (`first_name`)', - parameters: [], - }, + mysql: NOT_SUPPORTED, mssql: NOT_SUPPORTED, - sqlite: { - sql: 'create index "test_first_name_index" on "test" using hash ("first_name")', - parameters: [], - }, + sqlite: NOT_SUPPORTED, }) await builder.execute() @@ -1175,7 +1249,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create index `test_name_index` on `test` (`first_name`, `last_name`)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create index "test_name_index" on "test" ("first_name", "last_name")', + parameters: [], + }, sqlite: { sql: 'create index "test_name_index" on "test" ("first_name", "last_name")', parameters: [], @@ -1185,30 +1262,36 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - it('should create an index for an expression', async () => { - const builder = ctx.db.schema - .createIndex('test_first_name_index') - .on('test') - .expression(sql`(first_name < 'Sami')`) + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { + it('should create an index for an expression', async () => { + const builder = ctx.db.schema + .createIndex('test_first_name_index') + .on('test') + .expression(sql`(first_name < 'Sami')`) - testSql(builder, dialect, { - postgres: { - sql: `create index "test_first_name_index" on "test" ((first_name < 'Sami'))`, - parameters: [], - }, - mysql: { - sql: "create index `test_first_name_index` on `test` ((first_name < 'Sami'))", - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: `create index "test_first_name_index" on "test" ((first_name < 'Sami'))`, - parameters: [], - }, - }) + testSql(builder, dialect, { + postgres: { + sql: `create index "test_first_name_index" on "test" ((first_name < 'Sami'))`, + parameters: [], + }, + mysql: { + sql: "create index `test_first_name_index` on `test` ((first_name < 'Sami'))", + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: `create index "test_first_name_index" on "test" ((first_name < 'Sami'))`, + parameters: [], + }, + }) - await builder.execute() - }) + await builder.execute() + }) + } it('should create a sorted index, single column', async () => { const builder = ctx.db.schema @@ -1225,7 +1308,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create index `test_descending_first_name_index` on `test` (`first_name` desc)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create index "test_descending_first_name_index" on "test" ("first_name" desc)', + parameters: [], + }, sqlite: { sql: 'create index "test_descending_first_name_index" on "test" ("first_name" desc)', parameters: [], @@ -1250,7 +1336,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create index `test_first_name_descending_last_name_index` on `test` (`first_name`, `last_name` desc)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'create index "test_first_name_descending_last_name_index" on "test" ("first_name", "last_name" desc)', + parameters: [], + }, sqlite: { sql: 'create index "test_first_name_descending_last_name_index" on "test" ("first_name", "last_name" desc)', parameters: [], @@ -1260,8 +1349,71 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - if (dialect !== 'mysql') { - it('should create a partial index', async () => { + if ( + dialect === 'postgres' || + dialect === 'mssql' || + dialect === 'sqlite' + ) { + it('should create a partial index, single column', async () => { + const builder = ctx.db.schema + .createIndex('test_partial_index') + .on('test') + .column('first_name') + .where('first_name', '=', 'Sami') + + testSql(builder, dialect, { + postgres: { + sql: `create index "test_partial_index" on "test" ("first_name") where "first_name" = 'Sami'`, + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: { + sql: `create index "test_partial_index" on "test" ("first_name") where "first_name" = 'Sami'`, + parameters: [], + }, + sqlite: { + sql: `create index "test_partial_index" on "test" ("first_name") where "first_name" = 'Sami'`, + parameters: [], + }, + }) + + await builder.execute() + }) + + it('should create a partial index, multi-column, and', async () => { + const builder = ctx.db.schema + .createIndex('test_partial_index') + .on('test') + .columns(['first_name', 'last_name']) + .where((eb) => + eb.and([ + eb('first_name', '=', 'Igal'), + eb(sql.ref('age'), '>=', 18), + ]) + ) + + testSql(builder, dialect, { + postgres: { + sql: `create index "test_partial_index" on "test" ("first_name", "last_name") where ("first_name" = 'Igal' and "age" >= 18)`, + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: { + sql: `create index "test_partial_index" on "test" ("first_name", "last_name") where ("first_name" = 'Igal' and "age" >= 18)`, + parameters: [], + }, + sqlite: { + sql: `create index "test_partial_index" on "test" ("first_name", "last_name") where ("first_name" = 'Igal' and "age" >= 18)`, + parameters: [], + }, + }) + + await builder.execute() + }) + } + + if (dialect === 'postgres' || dialect === 'sqlite') { + it('should create a partial index, multi-column, or', async () => { const builder = ctx.db.schema .createIndex('test_partial_index') .on('test') @@ -1309,7 +1461,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { it('should drop an index', async () => { let builder = ctx.db.schema.dropIndex('test_first_name_index') - if (dialect === 'mysql') { + if (dialect === 'mysql' || dialect === 'mssql') { builder = builder.on('test') } @@ -1322,7 +1474,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop index `test_first_name_index` on `test`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'drop index "test_first_name_index" on "test"', + parameters: [], + }, sqlite: { sql: 'drop index "test_first_name_index"', parameters: [], @@ -1332,23 +1487,34 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - if (dialect !== 'mysql') { + if ( + dialect === 'postgres' || + dialect === 'mssql' || + dialect === 'sqlite' + ) { it('should drop an index if it exists', async () => { let builder = ctx.db.schema .dropIndex('test_first_name_index') .ifExists() + if (dialect === 'mssql') { + builder = builder.on('test') + } + testSql(builder, dialect, { postgres: { sql: 'drop index if exists "test_first_name_index"', parameters: [], }, - mssql: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, + mssql: { + sql: 'drop index if exists "test_first_name_index" on "test"', + parameters: [], + }, sqlite: { sql: 'drop index if exists "test_first_name_index"', parameters: [], }, - mysql: NOT_SUPPORTED, }) await builder.execute() @@ -1413,7 +1579,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: "create view `dogs` as select * from `pet` where `species` = 'dog'", parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `create view "dogs" as select * from "pet" where "species" = 'dog'`, + parameters: [], + }, sqlite: { sql: `create view "dogs" as select * from "pet" where "species" = 'dog'`, parameters: [], @@ -1423,7 +1592,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - if (dialect !== 'mysql') { + if (dialect === 'postgres' || dialect === 'sqlite') { it('should create a temporary view', async () => { const builder = ctx.db.schema .createView('dogs') @@ -1437,19 +1606,19 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: `create temporary view "dogs" as select * from "pet" where "species" = 'dog'`, parameters: [], }, + mysql: NOT_SUPPORTED, mssql: NOT_SUPPORTED, sqlite: { sql: `create temporary view "dogs" as select * from "pet" where "species" = 'dog'`, parameters: [], }, - mysql: NOT_SUPPORTED, }) await builder.execute() }) } - if (dialect !== 'sqlite') { + if (dialect === 'postgres' || dialect === 'mysql') { it('should create or replace a view', async () => { const builder = ctx.db.schema .createView('dogs') @@ -1485,13 +1654,13 @@ for (const dialect of DIALECTS_WITH_MSSQL) { ) testSql(builder, dialect, { + postgres: NOT_SUPPORTED, + mysql: NOT_SUPPORTED, mssql: NOT_SUPPORTED, sqlite: { sql: `create view if not exists "dogs" as select * from "pet" where "species" = 'dog'`, parameters: [], }, - postgres: NOT_SUPPORTED, - mysql: NOT_SUPPORTED, }) await builder.execute() @@ -1558,7 +1727,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop view `dogs`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `drop view "dogs"`, + parameters: [], + }, sqlite: { sql: `drop view "dogs"`, parameters: [], @@ -1580,7 +1752,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop view if exists `dogs`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `drop view if exists "dogs"`, + parameters: [], + }, sqlite: { sql: `drop view if exists "dogs"`, parameters: [], @@ -1589,7 +1764,8 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - if (dialect !== 'sqlite') { + + if (dialect === 'postgres' || dialect === 'mysql') { it('should drop a view cascade', async () => { const builder = ctx.db.schema.dropView('dogs').cascade() @@ -1608,6 +1784,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) + it('should drop a view cascade if it exists', async () => { const builder = ctx.db.schema.dropView('dogs').ifExists().cascade() @@ -1630,10 +1807,14 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) describe('create schema', () => { - if (dialect === 'postgres' || dialect === 'mysql') { - beforeEach(cleanup) - afterEach(cleanup) + beforeEach(cleanup) + afterEach(cleanup) + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { it('should create a schema', async () => { const builder = ctx.db.schema.createSchema('pets') @@ -1646,13 +1827,18 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'create schema `pets`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `create schema "pets"`, + parameters: [], + }, sqlite: NOT_SUPPORTED, }) await builder.execute() }) + } + if (dialect === 'postgres' || dialect === 'mysql') { it('should create a schema if not exists', async () => { const builder = ctx.db.schema.createSchema('pets').ifNotExists() @@ -1679,10 +1865,14 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) describe('drop schema', () => { - if (dialect === 'postgres' || dialect === 'mysql') { - beforeEach(cleanup) - afterEach(cleanup) + beforeEach(cleanup) + afterEach(cleanup) + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { it('should drop a schema', async () => { await ctx.db.schema.createSchema('pets').execute() @@ -1697,7 +1887,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop schema `pets`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `drop schema "pets"`, + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -1716,49 +1909,50 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'drop schema if exists `pets`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `drop schema if exists "pets"`, + parameters: [], + }, sqlite: NOT_SUPPORTED, }) await builder.execute() }) - if (dialect === 'postgres') { - it('should drop a schema cascade', async () => { - await ctx.db.schema.createSchema('pets').execute() - const builder = ctx.db.schema.dropSchema('pets').cascade() + } - testSql(builder, dialect, { - postgres: { - sql: `drop schema "pets" cascade`, - parameters: [], - }, - mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, - }) + if (dialect === 'postgres') { + it('should drop a schema cascade', async () => { + await ctx.db.schema.createSchema('pets').execute() + const builder = ctx.db.schema.dropSchema('pets').cascade() - await builder.execute() + testSql(builder, dialect, { + postgres: { + sql: `drop schema "pets" cascade`, + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, }) - it('should drop a schema cascade if exists', async () => { - const builder = ctx.db.schema - .dropSchema('pets') - .cascade() - .ifExists() + await builder.execute() + }) - testSql(builder, dialect, { - postgres: { - sql: `drop schema if exists "pets" cascade`, - parameters: [], - }, - mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, - }) + it('should drop a schema cascade if exists', async () => { + const builder = ctx.db.schema.dropSchema('pets').cascade().ifExists() - await builder.execute() + testSql(builder, dialect, { + postgres: { + sql: `drop schema if exists "pets" cascade`, + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, }) - } + + await builder.execute() + }) } async function cleanup() { From 09f47aff2a81aba41c093bb3e28579d8b670b413 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 31 Jul 2023 04:12:53 +0300 Subject: [PATCH 28/65] compile mssql alter table alter column properly. --- src/dialect/mssql/mssql-query-compiler.ts | 72 ++++++++++++++++++++ src/query-compiler/default-query-compiler.ts | 22 +++++- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/src/dialect/mssql/mssql-query-compiler.ts b/src/dialect/mssql/mssql-query-compiler.ts index 0b38215df..8bc1181ef 100644 --- a/src/dialect/mssql/mssql-query-compiler.ts +++ b/src/dialect/mssql/mssql-query-compiler.ts @@ -1,7 +1,79 @@ +import { AddColumnNode } from '../../operation-node/add-column-node.js' +import { AlterTableColumnAlterationNode } from '../../operation-node/alter-table-node.js' +import { DropColumnNode } from '../../operation-node/drop-column-node.js' import { DefaultQueryCompiler } from '../../query-compiler/default-query-compiler.js' export class MssqlQueryCompiler extends DefaultQueryCompiler { protected override getCurrentParameterPlaceholder(): string { return `@${this.numParameters}` } + + // mssql allows multi-column alterations in a single statement, + // but you can only use the command keyword/s once. + // it also doesn't support multiple kinds of commands in the same + // alter table statement, but we compile that anyway for the sake + // of WYSIWYG. + protected override compileColumnAlterations( + columnAlterations: readonly AlterTableColumnAlterationNode[] + ): void { + const nodesByKind: Partial< + Record< + AlterTableColumnAlterationNode['kind'], + AlterTableColumnAlterationNode[] + > + > = {} + + for (const columnAlteration of columnAlterations) { + if (!nodesByKind[columnAlteration.kind]) { + nodesByKind[columnAlteration.kind] = [] + } + + nodesByKind[columnAlteration.kind]!.push(columnAlteration) + } + + let first = true + + if (nodesByKind.AddColumnNode) { + this.append('add ') + this.compileList(nodesByKind.AddColumnNode) + first = false + } + + // multiple of these are not really supported by mssql, + // but for the sake of WYSIWYG. + if (nodesByKind.AlterColumnNode) { + if (!first) this.append(', ') + this.compileList(nodesByKind.AlterColumnNode) + } + + if (nodesByKind.DropColumnNode) { + if (!first) this.append(', ') + this.append('drop column ') + this.compileList(nodesByKind.DropColumnNode) + } + + // not really supported by mssql, but for the sake of WYSIWYG. + if (nodesByKind.ModifyColumnNode) { + if (!first) this.append(', ') + this.compileList(nodesByKind.ModifyColumnNode) + } + + // not really supported by mssql, but for the sake of WYSIWYG. + if (nodesByKind.RenameColumnNode) { + if (!first) this.append(', ') + this.compileList(nodesByKind.RenameColumnNode) + } + } + + protected override visitAddColumn(node: AddColumnNode): void { + this.visitNode(node.column) + } + + protected override visitDropColumn(node: DropColumnNode): void { + this.visitNode(node.column) + } + + protected override announcesNewColumnDataType(): boolean { + return false + } } diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index 28368b0b7..f51995eaf 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -58,7 +58,10 @@ import { RootOperationNode, QueryCompiler } from './query-compiler.js' import { HavingNode } from '../operation-node/having-node.js' import { CreateSchemaNode } from '../operation-node/create-schema-node.js' import { DropSchemaNode } from '../operation-node/drop-schema-node.js' -import { AlterTableNode } from '../operation-node/alter-table-node.js' +import { + AlterTableColumnAlterationNode, + AlterTableNode, +} from '../operation-node/alter-table-node.js' import { DropColumnNode } from '../operation-node/drop-column-node.js' import { RenameColumnNode } from '../operation-node/rename-column-node.js' import { AlterColumnNode } from '../operation-node/alter-column-node.js' @@ -997,7 +1000,7 @@ export class DefaultQueryCompiler } if (node.columnAlterations) { - this.compileList(node.columnAlterations) + this.compileColumnAlterations(node.columnAlterations) } } @@ -1024,7 +1027,10 @@ export class DefaultQueryCompiler this.append(' ') if (node.dataType) { - this.append('type ') + if (this.announcesNewColumnDataType()) { + this.append('type ') + } + this.visitNode(node.dataType) if (node.dataTypeExpression) { @@ -1484,6 +1490,16 @@ export class DefaultQueryCompiler return freeze(arr) } + + protected compileColumnAlterations( + columnAlterations: readonly AlterTableColumnAlterationNode[] + ) { + this.compileList(columnAlterations) + } + + protected announcesNewColumnDataType(): boolean { + return true + } } const SELECT_MODIFIER_SQL: Readonly> = freeze({ From ce5598910272fd8e71a0da43fcaf5f3ae870c6d7 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 31 Jul 2023 04:13:19 +0300 Subject: [PATCH 29/65] introduce mssql to schema module test suite, pt. 3. --- test/node/src/schema.test.ts | 571 +++++++++++++++++++---------------- 1 file changed, 308 insertions(+), 263 deletions(-) diff --git a/test/node/src/schema.test.ts b/test/node/src/schema.test.ts index eb1dff5e4..6315b6913 100644 --- a/test/node/src/schema.test.ts +++ b/test/node/src/schema.test.ts @@ -871,7 +871,9 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - } else if (dialect === 'mysql') { + } + + if (dialect === 'mysql') { it('should create a table partitioned by country', async () => { const builder = ctx.db.schema .createTable('test') @@ -899,7 +901,9 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - } else { + } + + if (dialect === 'sqlite') { it('should create a strict table', async () => { const builder = ctx.db.schema .createTable('test') @@ -2047,39 +2051,31 @@ for (const dialect of DIALECTS_WITH_MSSQL) { it('should add a column', async () => { const builder = ctx.db.schema .alterTable('test') - .addColumn('bool_col', 'boolean', (cb) => cb.notNull()) + .addColumn('date_col', 'date', (cb) => cb.notNull()) testSql(builder, dialect, { postgres: { - sql: 'alter table "test" add column "bool_col" boolean not null', + sql: 'alter table "test" add column "date_col" date not null', parameters: [], }, mysql: { - sql: 'alter table `test` add column `bool_col` boolean not null', + sql: 'alter table `test` add column `date_col` date not null', + parameters: [], + }, + mssql: { + sql: 'alter table "test" add "date_col" date not null', parameters: [], }, - mssql: NOT_SUPPORTED, sqlite: { - sql: 'alter table "test" add column "bool_col" boolean not null', + sql: 'alter table "test" add column "date_col" date not null', parameters: [], }, }) await builder.execute() - - expect(await getColumnMeta('test.bool_col')).to.containSubset({ - name: 'bool_col', - isNullable: false, - dataType: - dialect === 'postgres' - ? 'bool' - : dialect === 'sqlite' - ? 'boolean' - : 'tinyint', - }) }) - if (dialect !== 'sqlite') { + if (dialect === 'postgres' || dialect === 'mysql') { it('should add a unique column', async () => { const builder = ctx.db.schema .alterTable('test') @@ -2095,10 +2091,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { parameters: [], }, mssql: NOT_SUPPORTED, - sqlite: { - sql: 'alter table "test" add column "bool_col" boolean not null unique', - parameters: [], - }, + sqlite: NOT_SUPPORTED, }) await builder.execute() @@ -2109,7 +2102,13 @@ for (const dialect of DIALECTS_WITH_MSSQL) { dataType: dialect === 'postgres' ? 'bool' : 'tinyint', }) }) + } + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { it('should add multiple columns', async () => { const builder = ctx.db.schema .alterTable('test') @@ -2133,15 +2132,15 @@ for (const dialect of DIALECTS_WITH_MSSQL) { ], parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { + mssql: { sql: [ 'alter table "test"', - 'add column "another_col" text,', - 'add column "yet_another_col" integer', + 'add "another_col" text,', + '"yet_another_col" integer', ], parameters: [], }, + sqlite: NOT_SUPPORTED, }) await builder.execute() @@ -2157,11 +2156,11 @@ for (const dialect of DIALECTS_WITH_MSSQL) { .modifyColumn('varchar_col', 'text') testSql(builder, dialect, { + postgres: NOT_SUPPORTED, mysql: { sql: 'alter table `test` modify column `varchar_col` text', parameters: [], }, - postgres: NOT_SUPPORTED, mssql: NOT_SUPPORTED, sqlite: NOT_SUPPORTED, }) @@ -2243,57 +2242,63 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) } - if (dialect !== 'sqlite') { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { describe('alter column', () => { - it('should set default value', async () => { - const builder = ctx.db.schema - .alterTable('test') - .alterColumn('varchar_col', (ac) => ac.setDefault('foo')) + if (dialect === 'postgres' || dialect === 'mysql') { + it('should set default value', async () => { + const builder = ctx.db.schema + .alterTable('test') + .alterColumn('varchar_col', (ac) => ac.setDefault('foo')) - testSql(builder, dialect, { - postgres: { - sql: `alter table "test" alter column "varchar_col" set default 'foo'`, - parameters: [], - }, - mysql: { - sql: "alter table `test` alter column `varchar_col` set default 'foo'", - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, - }) + testSql(builder, dialect, { + postgres: { + sql: `alter table "test" alter column "varchar_col" set default 'foo'`, + parameters: [], + }, + mysql: { + sql: "alter table `test` alter column `varchar_col` set default 'foo'", + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) - await builder.execute() - }) + await builder.execute() + }) - it('should drop default value', async () => { - const subject = 'varchar_col' + it('should drop default value', async () => { + const subject = 'varchar_col' - await ctx.db.schema - .alterTable('test') - .alterColumn(subject, (ac) => ac.setDefault('foo')) - .execute() + await ctx.db.schema + .alterTable('test') + .alterColumn(subject, (ac) => ac.setDefault('foo')) + .execute() - const builder = ctx.db.schema - .alterTable('test') - .alterColumn(subject, (ac) => ac.dropDefault()) + const builder = ctx.db.schema + .alterTable('test') + .alterColumn(subject, (ac) => ac.dropDefault()) - testSql(builder, dialect, { - postgres: { - sql: 'alter table "test" alter column "varchar_col" drop default', - parameters: [], - }, - mysql: { - sql: 'alter table `test` alter column `varchar_col` drop default', - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + testSql(builder, dialect, { + postgres: { + sql: 'alter table "test" alter column "varchar_col" drop default', + parameters: [], + }, + mysql: { + sql: 'alter table `test` alter column `varchar_col` drop default', + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + await builder.execute() }) - await builder.execute() - }) + } - if (dialect !== 'mysql') { + if (dialect === 'postgres' || dialect === 'mssql') { it('should set column data type', async () => { const builder = ctx.db.schema .alterTable('test') @@ -2304,12 +2309,12 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table "test" alter column "varchar_col" type text', parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'alter table "test" alter column "varchar_col" type text', + mysql: NOT_SUPPORTED, + mssql: { + sql: 'alter table "test" alter column "varchar_col" text', parameters: [], }, - mysql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, }) await builder.execute() @@ -2325,17 +2330,19 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table "test" alter column "varchar_col" type text', parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'alter table "test" alter column "varchar_col" type text', + mysql: NOT_SUPPORTED, + mssql: { + sql: 'alter table "test" alter column "varchar_col" text', parameters: [], }, - mysql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, }) await builder.execute() }) + } + if (dialect === 'postgres') { it('should add not null constraint for column', async () => { const builder = ctx.db.schema .alterTable('test') @@ -2346,12 +2353,9 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table "test" alter column "varchar_col" set not null', parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'alter table "test" alter column "varchar_col" set not null', - parameters: [], - }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, }) await builder.execute() @@ -2372,47 +2376,46 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table "test" alter column "varchar_col" drop not null', parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'alter table "test" alter column "varchar_col" drop not null', - parameters: [], - }, mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, }) await builder.execute() }) } - it('should alter multiple columns', async () => { - const builder = ctx.db.schema - .alterTable('test') - .alterColumn('varchar_col', (ac) => ac.setDefault('foo')) - .alterColumn('integer_col', (ac) => ac.setDefault(5)) + if (dialect === 'postgres' || dialect === 'mysql') { + it('should alter multiple columns', async () => { + const builder = ctx.db.schema + .alterTable('test') + .alterColumn('varchar_col', (ac) => ac.setDefault('foo')) + .alterColumn('integer_col', (ac) => ac.setDefault(5)) - testSql(builder, dialect, { - postgres: { - sql: [ - `alter table "test"`, - `alter column "varchar_col" set default 'foo',`, - `alter column "integer_col" set default 5`, - ], - parameters: [], - }, - mysql: { - sql: [ - 'alter table `test`', - "alter column `varchar_col` set default 'foo',", - 'alter column `integer_col` set default 5', - ], - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, - }) + testSql(builder, dialect, { + postgres: { + sql: [ + `alter table "test"`, + `alter column "varchar_col" set default 'foo',`, + `alter column "integer_col" set default 5`, + ], + parameters: [], + }, + mysql: { + sql: [ + 'alter table `test`', + "alter column `varchar_col` set default 'foo',", + 'alter column `integer_col` set default 5', + ], + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) - await builder.execute() - }) + await builder.execute() + }) + } }) } @@ -2431,7 +2434,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table `test` drop column `varchar_col`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'alter table "test" drop column "varchar_col"', + parameters: [], + }, sqlite: { sql: 'alter table "test" drop column "varchar_col"', parameters: [], @@ -2441,7 +2447,11 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await builder.execute() }) - if (dialect !== 'sqlite') { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { it('should drop multiple columns', async () => { await ctx.db.schema .alterTable('test') @@ -2470,7 +2480,14 @@ for (const dialect of DIALECTS_WITH_MSSQL) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'alter table "test"', + 'drop column "varchar_col",', + '"text_col"', + ], + parameters: [], + }, sqlite: { sql: [ 'alter table "test"', @@ -2486,29 +2503,35 @@ for (const dialect of DIALECTS_WITH_MSSQL) { } }) - describe('rename', () => { - it('should rename a table', async () => { - const builder = ctx.db.schema.alterTable('test').renameTo('test2') + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { + describe('rename', () => { + it('should rename a table', async () => { + const builder = ctx.db.schema.alterTable('test').renameTo('test2') - testSql(builder, dialect, { - postgres: { - sql: 'alter table "test" rename to "test2"', - parameters: [], - }, - mysql: { - sql: 'alter table `test` rename to `test2`', - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'alter table "test" rename to "test2"', - parameters: [], - }, - }) + testSql(builder, dialect, { + postgres: { + sql: 'alter table "test" rename to "test2"', + parameters: [], + }, + mysql: { + sql: 'alter table `test` rename to `test2`', + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'alter table "test" rename to "test2"', + parameters: [], + }, + }) - await builder.execute() + await builder.execute() + }) }) - }) + } if (dialect === 'postgres') { describe('set schema', () => { @@ -2530,138 +2553,136 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) } - describe('rename column', () => { - it('should rename a column', async () => { - const builder = ctx.db.schema - .alterTable('test') - .renameColumn('varchar_col', 'text_col') - - testSql(builder, dialect, { - postgres: { - sql: 'alter table "test" rename column "varchar_col" to "text_col"', - parameters: [], - }, - mysql: { - sql: 'alter table `test` rename column `varchar_col` to `text_col`', - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'alter table "test" rename column "varchar_col" to "text_col"', - parameters: [], - }, - }) - - await builder.execute() - }) - - if (dialect === 'mysql') { - it('should rename multiple columns', async () => { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ) { + describe('rename column', () => { + it('should rename a column', async () => { const builder = ctx.db.schema .alterTable('test') .renameColumn('varchar_col', 'text_col') - .renameColumn('integer_col', 'number_col') testSql(builder, dialect, { postgres: { - sql: [ - 'alter table "test"', - 'rename column "varchar_col" to "text_col",', - 'rename column "integer_col" to "number_col"', - ], + sql: 'alter table "test" rename column "varchar_col" to "text_col"', parameters: [], }, mysql: { - sql: [ - 'alter table `test`', - 'rename column `varchar_col` to `text_col`,', - 'rename column `integer_col` to `number_col`', - ], + sql: 'alter table `test` rename column `varchar_col` to `text_col`', parameters: [], }, mssql: NOT_SUPPORTED, sqlite: { - sql: [ - 'alter table "test"', - 'rename column "varchar_col" to "text_col",', - 'rename column "integer_col" to "number_col"', - ], + sql: 'alter table "test" rename column "varchar_col" to "text_col"', parameters: [], }, }) await builder.execute() }) - } - }) - describe('mixed column alterations', () => { - if (dialect === 'postgres') { - it('should alter multiple columns in various ways', async () => { - const builder = ctx.db.schema - .alterTable('test') - .addColumn('another_varchar_col', 'varchar(255)') - .alterColumn('varchar_col', (ac) => ac.setDefault('foo')) - .dropColumn('integer_col') + if (dialect === 'mysql') { + it('should rename multiple columns', async () => { + const builder = ctx.db.schema + .alterTable('test') + .renameColumn('varchar_col', 'text_col') + .renameColumn('integer_col', 'number_col') - testSql(builder, dialect, { - postgres: { - sql: [ - `alter table "test"`, - `add column "another_varchar_col" varchar(255),`, - `alter column "varchar_col" set default 'foo',`, - `drop column "integer_col"`, - ], - parameters: [], - }, - mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, - }) + testSql(builder, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: [ + 'alter table `test`', + 'rename column `varchar_col` to `text_col`,', + 'rename column `integer_col` to `number_col`', + ], + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) - await builder.execute() - }) - } + await builder.execute() + }) + } + }) + } - if (dialect === 'mysql') { - it('should alter multiple columns in various ways', async () => { - await ctx.db.schema - .alterTable('test') - .addColumn('rename_me', 'text') - .addColumn('modify_me', 'boolean') - .execute() + if (dialect === 'postgres' || dialect === 'mysql') { + describe('mixed column alterations', () => { + if (dialect === 'postgres') { + it('should alter multiple columns in various ways', async () => { + const builder = ctx.db.schema + .alterTable('test') + .addColumn('another_varchar_col', 'varchar(255)') + .alterColumn('varchar_col', (ac) => ac.setDefault('foo')) + .dropColumn('integer_col') - const builder = ctx.db.schema - .alterTable('test') - .addColumn('another_varchar_col', 'varchar(255)') - .alterColumn('varchar_col', (ac) => ac.setDefault('foo')) - .dropColumn('integer_col') - .renameColumn('rename_me', 'text_col') - .modifyColumn('modify_me', 'bigint') + testSql(builder, dialect, { + postgres: { + sql: [ + `alter table "test"`, + `add column "another_varchar_col" varchar(255),`, + `alter column "varchar_col" set default 'foo',`, + `drop column "integer_col"`, + ], + parameters: [], + }, + mysql: NOT_SUPPORTED, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) - testSql(builder, dialect, { - postgres: NOT_SUPPORTED, - mysql: { - sql: [ - 'alter table `test`', - 'add column `another_varchar_col` varchar(255),', - "alter column `varchar_col` set default 'foo',", - 'drop column `integer_col`,', - 'rename column `rename_me` to `text_col`,', - 'modify column `modify_me` bigint', - ], - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: NOT_SUPPORTED, + await builder.execute() }) + } - await builder.execute() - }) - } - }) + if (dialect === 'mysql') { + it('should alter multiple columns in various ways', async () => { + await ctx.db.schema + .alterTable('test') + .addColumn('rename_me', 'text') + .addColumn('modify_me', 'boolean') + .execute() + + const builder = ctx.db.schema + .alterTable('test') + .addColumn('another_varchar_col', 'varchar(255)') + .alterColumn('varchar_col', (ac) => ac.setDefault('foo')) + .dropColumn('integer_col') + .renameColumn('rename_me', 'text_col') + .modifyColumn('modify_me', 'bigint') + + testSql(builder, dialect, { + postgres: NOT_SUPPORTED, + mysql: { + sql: [ + 'alter table `test`', + 'add column `another_varchar_col` varchar(255),', + "alter column `varchar_col` set default 'foo',", + 'drop column `integer_col`,', + 'rename column `rename_me` to `text_col`,', + 'modify column `modify_me` bigint', + ], + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: NOT_SUPPORTED, + }) + + await builder.execute() + }) + } + }) + } - if (dialect !== 'sqlite') { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { describe('add unique constraint', () => { it('should add a unique constraint', async () => { const builder = ctx.db.schema @@ -2680,7 +2701,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table `test` add constraint `some_constraint` unique (`varchar_col`, `integer_col`)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'alter table "test" add constraint "some_constraint" unique ("varchar_col", "integer_col")', + parameters: [], + }, sqlite: { sql: 'alter table "test" add constraint "some_constraint" unique ("varchar_col", "integer_col")', parameters: [], @@ -2692,7 +2716,11 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) } - if (dialect !== 'sqlite') { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { describe('add check constraint', () => { it('should add a check constraint', async () => { const builder = ctx.db.schema @@ -2708,7 +2736,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table `test` add constraint `some_constraint` check (integer_col > 0)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'alter table "test" add constraint "some_constraint" check (integer_col > 0)', + parameters: [], + }, sqlite: { sql: 'alter table "test" add constraint "some_constraint" check (integer_col > 0)', parameters: [], @@ -2720,7 +2751,11 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) } - if (dialect !== 'sqlite') { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { describe('add foreign key constraint', () => { it('should add a foreign key constraint', async () => { await ctx.db.schema @@ -2748,11 +2783,11 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table `test` add constraint `some_constraint` foreign key (`integer_col`, `varchar_col`) references `test2` (`a`, `b`)', parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { + mssql: { sql: 'alter table "test" add constraint "some_constraint" foreign key ("integer_col", "varchar_col") references "test2" ("a", "b")', parameters: [], }, + sqlite: NOT_SUPPORTED, }) await builder.execute() @@ -2786,11 +2821,11 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table `test` add constraint `some_constraint` foreign key (`integer_col`, `varchar_col`) references `test2` (`a`, `b`) on delete set null on update cascade', parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { + mssql: { sql: 'alter table "test" add constraint "some_constraint" foreign key ("integer_col", "varchar_col") references "test2" ("a", "b") on delete set null on update cascade', parameters: [], }, + sqlite: NOT_SUPPORTED, }) await builder.execute() @@ -2830,7 +2865,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table `test` drop constraint `foreign_key_constraint`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'alter table "test" drop constraint "foreign_key_constraint"', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -2839,19 +2877,20 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) } - if (dialect !== 'sqlite') { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { describe('parse schema name', () => { beforeEach(cleanup) afterEach(cleanup) it('should parse the schema from table name', async () => { - await ctx.db.schema - .createSchema('test_schema') - .ifNotExists() - .execute() + await ctx.db.schema.createSchema('test_schema').execute() await ctx.db.schema .createTable('test_schema.test') - .addColumn('id', 'serial') + .addColumn('id', 'varchar(36)') .execute() const builder = ctx.db.schema @@ -2867,7 +2906,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'alter table `test_schema`.`test` add column `second_column` text', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `alter table "test_schema"."test" add "second_column" text`, + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -2888,21 +2930,24 @@ for (const dialect of DIALECTS_WITH_MSSQL) { const builder = ctx.db.schema .alterTable('test') .$call((builder) => - builder.addColumn('abc', 'integer', (col) => col.defaultTo('42')) + builder.addColumn('abc', 'integer', (col) => col.notNull()) ) testSql(builder, dialect, { postgres: { - sql: [`alter table "test" add column "abc" integer default '42'`], + sql: [`alter table "test" add column "abc" integer not null`], parameters: [], }, mysql: { - sql: ["alter table `test` add column `abc` integer default '42'"], + sql: ['alter table `test` add column `abc` integer not null'], + parameters: [], + }, + mssql: { + sql: [`alter table "test" add "abc" integer not null`], parameters: [], }, - mssql: NOT_SUPPORTED, sqlite: { - sql: [`alter table "test" add column "abc" integer default '42'`], + sql: [`alter table "test" add column "abc" integer not null`], parameters: [], }, }) From cfe611dd639754e9a0aaa3edeff9ff6505f75138 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 31 Jul 2023 04:18:36 +0300 Subject: [PATCH 30/65] fix failing where test. --- test/node/src/where.test.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/node/src/where.test.ts b/test/node/src/where.test.ts index 963fe57fe..c9851be58 100644 --- a/test/node/src/where.test.ts +++ b/test/node/src/where.test.ts @@ -989,7 +989,11 @@ for (const dialect of DIALECTS_WITH_MSSQL) { await query.execute() }) - if (dialect !== 'sqlite') { + if ( + dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'mssql' + ) { it('subquery inside `any` operator', async () => { await ctx.db .insertInto('pet') @@ -1028,7 +1032,10 @@ for (const dialect of DIALECTS_WITH_MSSQL) { sql: 'select `first_name` from `person` where ? = any((select `name` from `pet` where `pet`.`owner_id` = `person`.`id`))', parameters: ['Cat Stevens'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "first_name" from "person" where @1 = any((select "name" from "pet" where "pet"."owner_id" = "person"."id"))', + parameters: ['Cat Stevens'], + }, sqlite: NOT_SUPPORTED, }) From d5531b6553c74c3914d792fa366d165f4b6907f3 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Thu, 3 Aug 2023 01:38:00 +0300 Subject: [PATCH 31/65] introduce mssql to sanitize identifiers test suite. --- test/node/src/replace.test.ts | 4 ++-- test/node/src/sanitize-identifiers.test.ts | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/test/node/src/replace.test.ts b/test/node/src/replace.test.ts index ddf22330a..766f21e38 100644 --- a/test/node/src/replace.test.ts +++ b/test/node/src/replace.test.ts @@ -11,10 +11,10 @@ import { Database, NOT_SUPPORTED, insertDefaultDataSet, - DIALECTS, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -if (DIALECTS.includes('mysql')) { +if (DIALECTS_WITH_MSSQL.includes('mysql')) { const dialect = 'mysql' as const describe(`mysql: replace`, () => { diff --git a/test/node/src/sanitize-identifiers.test.ts b/test/node/src/sanitize-identifiers.test.ts index 672fde4c5..4891869ca 100644 --- a/test/node/src/sanitize-identifiers.test.ts +++ b/test/node/src/sanitize-identifiers.test.ts @@ -1,17 +1,17 @@ import { Updateable } from '../../../dist/cjs' import { - DIALECTS, destroyTest, initTest, TestContext, Person, testSql, NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { - describe(`${dialect}: sanitize identifiers`, () => { +for (const dialect of DIALECTS_WITH_MSSQL) { + describe.only(`${dialect}: sanitize identifiers`, () => { let ctx: TestContext before(async function () { @@ -40,7 +40,10 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name"``` = ?', parameters: ['foo', 'bar'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'update "person" set "first_name" = @1, "last_name""`" = @2', + parameters: ['foo', 'bar'], + }, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name""`" = ?', parameters: ['foo', 'bar'], @@ -66,7 +69,10 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name""````` = ?', parameters: ['foo', 'bar'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'update "person" set "first_name" = @1, "last_name""""``" = @2', + parameters: ['foo', 'bar'], + }, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name""""``" = ?', parameters: ['foo', 'bar'], From f7e7b7d56342d44015e179526f362a71a9d2f627 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Thu, 3 Aug 2023 01:52:31 +0300 Subject: [PATCH 32/65] introduce mssql to raw sql test suite. --- test/node/src/raw-sql.test.ts | 65 ++++++++++++++++------ test/node/src/sanitize-identifiers.test.ts | 2 +- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/test/node/src/raw-sql.test.ts b/test/node/src/raw-sql.test.ts index d421f4891..f0e7cf13d 100644 --- a/test/node/src/raw-sql.test.ts +++ b/test/node/src/raw-sql.test.ts @@ -1,7 +1,6 @@ import { sql, CompiledQuery } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -10,9 +9,10 @@ import { testSql, NOT_SUPPORTED, expect, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: raw sql`, () => { let ctx: TestContext @@ -47,7 +47,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where first_name between ? and ?', parameters: ['A', 'B'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where first_name between @1 and @2', + parameters: ['A', 'B'], + }, sqlite: { sql: 'select * from "person" where first_name between ? and ?', parameters: ['A', 'B'], @@ -74,7 +77,10 @@ for (const dialect of DIALECTS) { sql: "select * from `person` where first_name between 'A' and 'B'", parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select * from "person" where first_name between 'A' and 'B'`, + parameters: [], + }, sqlite: { sql: `select * from "person" where first_name between 'A' and 'B'`, parameters: [], @@ -99,7 +105,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` between ? and ?', parameters: ['A', 'B'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "first_name" between @1 and @2', + parameters: ['A', 'B'], + }, sqlite: { sql: 'select * from "person" where "first_name" between ? and ?', parameters: ['A', 'B'], @@ -109,14 +118,14 @@ for (const dialect of DIALECTS) { await query.execute() }) - if (dialect == 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { it('sql.id should separate multiple arguments by dots', async () => { const query = ctx.db .selectFrom('person') .selectAll() .where( sql`${sql.id( - 'public', + dialect === 'postgres' ? 'public' : 'dbo', 'person', 'first_name' )} between ${'A'} and ${'B'}` @@ -128,7 +137,10 @@ for (const dialect of DIALECTS) { parameters: ['A', 'B'], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "dbo"."person"."first_name" between @1 and @2', + parameters: ['A', 'B'], + }, sqlite: NOT_SUPPORTED, }) @@ -151,7 +163,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where `first_name` between ? and ?', parameters: ['A', 'B'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "first_name" between @1 and @2', + parameters: ['A', 'B'], + }, sqlite: { sql: 'select * from "person" where "first_name" between ? and ?', parameters: ['A', 'B'], @@ -161,14 +176,14 @@ for (const dialect of DIALECTS) { await query.execute() }) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { it('sql.ref should support schemas and table names', async () => { const query = ctx.db .selectFrom('person') .selectAll() .where( sql`${sql.ref( - 'public.person.first_name' + `${dialect === 'postgres' ? 'public' : 'dbo'}.person.first_name` )} between ${'A'} and ${'B'}` ) @@ -178,7 +193,10 @@ for (const dialect of DIALECTS) { parameters: ['A', 'B'], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where "dbo"."person"."first_name" between @1 and @2', + parameters: ['A', 'B'], + }, sqlite: NOT_SUPPORTED, }) @@ -200,7 +218,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` as `person`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" as "person"', + parameters: [], + }, sqlite: { sql: 'select * from "person" as "person"', parameters: [], @@ -210,10 +231,14 @@ for (const dialect of DIALECTS) { await query.execute() }) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { it('sql.table should support schemas', async () => { const query = ctx.db - .selectFrom(sql`${sql.table('public.person')}`.as('person')) + .selectFrom( + sql`${sql.table( + `${dialect === 'postgres' ? 'public' : 'dbo'}.person` + )}`.as('person') + ) .selectAll() testSql(query, dialect, { @@ -222,7 +247,10 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "dbo"."person" as "person"', + parameters: [], + }, sqlite: NOT_SUPPORTED, }) @@ -247,7 +275,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` where first_name in (?, ?)', parameters: names, }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" where first_name in (@1, @2)', + parameters: names, + }, sqlite: { sql: 'select * from "person" where first_name in (?, ?)', parameters: names, diff --git a/test/node/src/sanitize-identifiers.test.ts b/test/node/src/sanitize-identifiers.test.ts index 4891869ca..dbd66672e 100644 --- a/test/node/src/sanitize-identifiers.test.ts +++ b/test/node/src/sanitize-identifiers.test.ts @@ -11,7 +11,7 @@ import { } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { - describe.only(`${dialect}: sanitize identifiers`, () => { + describe(`${dialect}: sanitize identifiers`, () => { let ctx: TestContext before(async function () { From 5e6e2bb3013c20591281080ed03323979f20d96d Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Thu, 3 Aug 2023 02:31:17 +0300 Subject: [PATCH 33/65] introduce mssql to raw query test suite. --- src/dialect/mssql/mssql-driver.ts | 6 +++--- test/node/src/insert.test.ts | 10 +--------- test/node/src/raw-query.test.ts | 26 ++++++++++++++++---------- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index 93dbd917c..da5d74546 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -144,7 +144,7 @@ class MssqlConnection implements DatabaseConnection { ) return { - numAffectedRows: BigInt(rowCount), + numAffectedRows: rowCount !== undefined ? BigInt(rowCount) : undefined, rows, } } catch (err) { @@ -198,7 +198,7 @@ class MssqlConnection implements DatabaseConnection { reject: (reason?: any) => void, resolve: (value: any) => void ): Request { - const { parameters, sql } = compiledQuery + const { parameters, query, sql } = compiledQuery let promisedRowCount: number | undefined const rows: Record[] = [] @@ -241,7 +241,7 @@ class MssqlConnection implements DatabaseConnection { request.off('row', rowListener) resolve({ rows, - rowCount: promisedRowCount!, + rowCount: promisedRowCount, }) }) diff --git a/test/node/src/insert.test.ts b/test/node/src/insert.test.ts index 0fea6ef98..187586d3a 100644 --- a/test/node/src/insert.test.ts +++ b/test/node/src/insert.test.ts @@ -1,13 +1,6 @@ -import { - AliasedRawBuilder, - InsertResult, - Kysely, - SelectQueryBuilder, - sql, -} from '../../../' +import { AliasedRawBuilder, InsertResult, Kysely, sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -19,7 +12,6 @@ import { NOT_SUPPORTED, insertDefaultDataSet, DIALECTS_WITH_MSSQL, - BuiltInDialect, limit, } from './test-setup.js' diff --git a/test/node/src/raw-query.test.ts b/test/node/src/raw-query.test.ts index d1c8c3648..f4e212094 100644 --- a/test/node/src/raw-query.test.ts +++ b/test/node/src/raw-query.test.ts @@ -1,7 +1,6 @@ import { sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -9,11 +8,11 @@ import { expect, insertDefaultDataSet, testSql, - NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { - describe(`${dialect}: raw queries`, () => { +for (const dialect of DIALECTS_WITH_MSSQL) { + describe.only(`${dialect}: raw queries`, () => { let ctx: TestContext before(async function () { @@ -42,7 +41,9 @@ for (const dialect of DIALECTS) { ) expect(result.insertId).to.equal(undefined) - expect(result.numUpdatedOrDeletedRows).to.equal(undefined) + expect(result.numAffectedRows).to.equal( + dialect === 'mssql' ? 2n : undefined + ) expect(result.rows).to.eql([ { first_name: 'Arnold' }, { first_name: 'Sylvester' }, @@ -58,7 +59,7 @@ for (const dialect of DIALECTS) { ctx.db ) - expect(result.numUpdatedOrDeletedRows).to.equal(2n) + expect(result.numAffectedRows).to.equal(2n) expect(result.rows).to.eql([]) }) @@ -68,11 +69,11 @@ for (const dialect of DIALECTS) { const result = await sql`delete from person where gender = ${gender}`.execute(ctx.db) - expect(result.numUpdatedOrDeletedRows).to.equal(2n) + expect(result.numAffectedRows).to.equal(2n) expect(result.rows).to.eql([]) }) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'sqlite') { it('should run a raw insert query', async () => { const firstName = 'New' const lastName = 'Personsson' @@ -88,7 +89,9 @@ for (const dialect of DIALECTS) { { first_name: 'New', last_name: 'Personsson' }, ]) }) - } else { + } + + if (dialect === 'mysql') { it('should run a raw insert query', async () => { const firstName = 'New' const lastName = 'Personsson' @@ -118,7 +121,10 @@ for (const dialect of DIALECTS) { sql: 'select first_name from person where gender = ? order by first_name asc, last_name asc', parameters: [gender], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select first_name from person where gender = @1 order by first_name asc, last_name asc', + parameters: [gender], + }, sqlite: { sql: 'select first_name from person where gender = ? order by first_name asc, last_name asc', parameters: [gender], From f438909d4555d816c3b58b7cc33602038db58fba Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Thu, 3 Aug 2023 02:37:55 +0300 Subject: [PATCH 34/65] introduce mssql to order by test suite. --- test/node/src/order-by.test.ts | 70 ++++++++++++++++++++++--------- test/node/src/performance.test.ts | 4 +- test/node/src/raw-query.test.ts | 2 +- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/test/node/src/order-by.test.ts b/test/node/src/order-by.test.ts index 9fac66320..149473c2e 100644 --- a/test/node/src/order-by.test.ts +++ b/test/node/src/order-by.test.ts @@ -1,7 +1,6 @@ import { sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -10,9 +9,10 @@ import { expect, NOT_SUPPORTED, insertDefaultDataSet, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: order by`, () => { let ctx: TestContext @@ -47,7 +47,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` order by `first_name`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" order by "first_name"', + parameters: [], + }, sqlite: { sql: 'select * from "person" order by "first_name"', parameters: [], @@ -80,7 +83,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` order by `first_name`, `last_name` desc', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" order by "first_name", "last_name" desc', + parameters: [], + }, sqlite: { sql: 'select * from "person" order by "first_name", "last_name" desc', parameters: [], @@ -105,7 +111,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` order by `first_name`, `last_name` desc', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select * from "person" order by "first_name", "last_name" desc', + parameters: [], + }, sqlite: { sql: 'select * from "person" order by "first_name", "last_name" desc', parameters: [], @@ -149,7 +158,16 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'select "first_name" as "fn",', + '"middle_name" as "mn",', + '"last_name" as "ln",', + '"gender" as "g"', + 'from "person" order by "fn", "mn" asc, "ln" desc, "g"', + ], + parameters: [], + }, sqlite: { sql: [ 'select "first_name" as "fn",', @@ -170,10 +188,10 @@ for (const dialect of DIALECTS) { .selectFrom('person') .selectAll() .orderBy(sql`coalesce(${sql.ref('first_name')}, ${sql.lit('foo')}) asc`) - .orderBy((eb) => eb.fn.coalesce('first_name', sql.lit('foo'))) + .orderBy((eb) => eb.fn.coalesce('last_name', sql.lit('foo'))) .orderBy([ - sql`coalesce(${sql.ref('first_name')}, ${sql.lit('foo')})`, - (eb) => sql`${eb.fn.coalesce('first_name', sql.lit('foo'))} desc`, + sql`coalesce(${sql.ref('gender')}, ${sql.lit('foo')})`, + (eb) => sql`${eb.fn.coalesce('middle_name', sql.lit('foo'))} desc`, ]) testSql(query, dialect, { @@ -181,9 +199,9 @@ for (const dialect of DIALECTS) { sql: [ 'select * from "person"', `order by coalesce("first_name", 'foo') asc,`, - `coalesce("first_name", 'foo'),`, - `coalesce("first_name", 'foo'),`, - `coalesce("first_name", 'foo') desc`, + `coalesce("last_name", 'foo'),`, + `coalesce("gender", 'foo'),`, + `coalesce("middle_name", 'foo') desc`, ], parameters: [], }, @@ -191,20 +209,29 @@ for (const dialect of DIALECTS) { sql: [ 'select * from `person`', "order by coalesce(`first_name`, 'foo') asc,", - "coalesce(`first_name`, 'foo'),", - "coalesce(`first_name`, 'foo'),", - "coalesce(`first_name`, 'foo') desc", + "coalesce(`last_name`, 'foo'),", + "coalesce(`gender`, 'foo'),", + "coalesce(`middle_name`, 'foo') desc", + ], + parameters: [], + }, + mssql: { + sql: [ + 'select * from "person"', + `order by coalesce("first_name", 'foo') asc,`, + `coalesce("last_name", 'foo'),`, + `coalesce("gender", 'foo'),`, + `coalesce("middle_name", 'foo') desc`, ], parameters: [], }, - mssql: NOT_SUPPORTED, sqlite: { sql: [ 'select * from "person"', `order by coalesce("first_name", 'foo') asc,`, - `coalesce("first_name", 'foo'),`, - `coalesce("first_name", 'foo'),`, - `coalesce("first_name", 'foo') desc`, + `coalesce("last_name", 'foo'),`, + `coalesce("gender", 'foo'),`, + `coalesce("middle_name", 'foo') desc`, ], parameters: [], }, @@ -228,7 +255,10 @@ for (const dialect of DIALECTS) { sql: "select * from `person` order by coalesce(`first_name`, 'foo') asc", parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select * from "person" order by coalesce("first_name", 'foo') asc`, + parameters: [], + }, sqlite: { sql: `select * from "person" order by coalesce("first_name", 'foo') asc`, parameters: [], diff --git a/test/node/src/performance.test.ts b/test/node/src/performance.test.ts index db389782b..50eafff11 100644 --- a/test/node/src/performance.test.ts +++ b/test/node/src/performance.test.ts @@ -6,10 +6,10 @@ import { initTest, TestContext, insertDefaultDataSet, - DIALECTS, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -if (DIALECTS.includes('postgres')) { +if (DIALECTS_WITH_MSSQL.includes('postgres')) { describe.skip(`query builder performance`, () => { let ctx: TestContext diff --git a/test/node/src/raw-query.test.ts b/test/node/src/raw-query.test.ts index f4e212094..a57e4a202 100644 --- a/test/node/src/raw-query.test.ts +++ b/test/node/src/raw-query.test.ts @@ -12,7 +12,7 @@ import { } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { - describe.only(`${dialect}: raw queries`, () => { + describe(`${dialect}: raw queries`, () => { let ctx: TestContext before(async function () { From 3268674e3e8add8e511dc421172fa7e0675279a3 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Thu, 3 Aug 2023 22:51:28 +0300 Subject: [PATCH 35/65] introduce mssql to join test suite. --- test/node/src/join.test.ts | 155 +++++++++++++++++++-------- test/node/src/json-traversal.test.ts | 6 +- test/node/src/json.test.ts | 6 +- 3 files changed, 121 insertions(+), 46 deletions(-) diff --git a/test/node/src/join.test.ts b/test/node/src/join.test.ts index b6ae811d7..e8ebff3db 100644 --- a/test/node/src/join.test.ts +++ b/test/node/src/join.test.ts @@ -1,7 +1,6 @@ import { sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -10,9 +9,11 @@ import { testSql, expect, NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, + limit, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: join`, () => { let ctx: TestContext @@ -74,7 +75,10 @@ for (const dialect of DIALECTS) { sql: `select * from \`person\` inner join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` order by \`person\`.\`first_name\``, parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select * from "person" inner join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, + parameters: [], + }, sqlite: { sql: `select * from "person" inner join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, parameters: [], @@ -136,7 +140,15 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select * from "person"`, + `inner join (select "owner_id" as "oid", "name" from "pet") as "p"`, + `on "p"."oid" = "person"."id"`, + `order by "person"."first_name"`, + ], + parameters: [], + }, sqlite: { sql: [ `select * from "person"`, @@ -187,7 +199,10 @@ for (const dialect of DIALECTS) { sql: `select \`pet\`.\`name\` as \`pet_name\`, \`toy\`.\`name\` as \`toy_name\` from \`person\` inner join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` inner join \`toy\` on \`toy\`.\`pet_id\` = \`pet\`.\`id\` where \`first_name\` = ?`, parameters: ['Jennifer'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select "pet"."name" as "pet_name", "toy"."name" as "toy_name" from "person" inner join "pet" on "pet"."owner_id" = "person"."id" inner join "toy" on "toy"."pet_id" = "pet"."id" where "first_name" = @1`, + parameters: ['Jennifer'], + }, sqlite: { sql: `select "pet"."name" as "pet_name", "toy"."name" as "toy_name" from "person" inner join "pet" on "pet"."owner_id" = "person"."id" inner join "toy" on "toy"."pet_id" = "pet"."id" where "first_name" = ?`, parameters: ['Jennifer'], @@ -221,8 +236,7 @@ for (const dialect of DIALECTS) { '=', selectFrom('pet') .select(sql<'hamster'>`'hamster'`.as('hamster')) - .limit(1) - .offset(0) + .$call(limit(1, dialect)) ), ]) ) @@ -237,10 +251,10 @@ for (const dialect of DIALECTS) { `inner join "pet"`, `on "pet"."owner_id" = "person"."id"`, `and "pet"."name" in ($1, $2, $3)`, - `and ("pet"."species" = $4 or "species" = $5 or "species" = (select 'hamster' as "hamster" from "pet" limit $6 offset $7))`, + `and ("pet"."species" = $4 or "species" = $5 or "species" = (select 'hamster' as "hamster" from "pet" limit $6))`, `order by "person"."first_name"`, ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], }, mysql: { sql: [ @@ -248,22 +262,32 @@ for (const dialect of DIALECTS) { 'inner join `pet`', 'on `pet`.`owner_id` = `person`.`id`', 'and `pet`.`name` in (?, ?, ?)', - "and (`pet`.`species` = ? or `species` = ? or `species` = (select 'hamster' as `hamster` from `pet` limit ? offset ?))", + "and (`pet`.`species` = ? or `species` = ? or `species` = (select 'hamster' as `hamster` from `pet` limit ?))", 'order by `person`.`first_name`', ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], + }, + mssql: { + sql: [ + `select * from "person"`, + `inner join "pet"`, + `on "pet"."owner_id" = "person"."id"`, + `and "pet"."name" in (@1, @2, @3)`, + `and ("pet"."species" = @4 or "species" = @5 or "species" = (select top 1 'hamster' as "hamster" from "pet"))`, + `order by "person"."first_name"`, + ], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog'], }, - mssql: NOT_SUPPORTED, sqlite: { sql: [ `select * from "person"`, `inner join "pet"`, `on "pet"."owner_id" = "person"."id"`, `and "pet"."name" in (?, ?, ?)`, - `and ("pet"."species" = ? or "species" = ? or "species" = (select 'hamster' as "hamster" from "pet" limit ? offset ?))`, + `and ("pet"."species" = ? or "species" = ? or "species" = (select 'hamster' as "hamster" from "pet" limit ?))`, `order by "person"."first_name"`, ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], }, }) @@ -287,8 +311,7 @@ for (const dialect of DIALECTS) { eb .selectFrom('pet') .select(sql<'hamster'>`'hamster'`.as('hamster')) - .limit(1) - .offset(0) + .$call(limit(1, dialect)) ), ]) ) @@ -303,10 +326,10 @@ for (const dialect of DIALECTS) { `inner join "pet"`, `on "pet"."owner_id" = "person"."id"`, `and "pet"."name" in ($1, $2, $3)`, - `and ("pet"."species" = $4 or "species" = $5 or "species" = (select 'hamster' as "hamster" from "pet" limit $6 offset $7))`, + `and ("pet"."species" = $4 or "species" = $5 or "species" = (select 'hamster' as "hamster" from "pet" limit $6))`, `order by "person"."first_name"`, ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], }, mysql: { sql: [ @@ -314,22 +337,32 @@ for (const dialect of DIALECTS) { 'inner join `pet`', 'on `pet`.`owner_id` = `person`.`id`', 'and `pet`.`name` in (?, ?, ?)', - "and (`pet`.`species` = ? or `species` = ? or `species` = (select 'hamster' as `hamster` from `pet` limit ? offset ?))", + "and (`pet`.`species` = ? or `species` = ? or `species` = (select 'hamster' as `hamster` from `pet` limit ?))", 'order by `person`.`first_name`', ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], + }, + mssql: { + sql: [ + `select * from "person"`, + `inner join "pet"`, + `on "pet"."owner_id" = "person"."id"`, + `and "pet"."name" in (@1, @2, @3)`, + `and ("pet"."species" = @4 or "species" = @5 or "species" = (select top 1 'hamster' as "hamster" from "pet"))`, + `order by "person"."first_name"`, + ], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog'], }, - mssql: NOT_SUPPORTED, sqlite: { sql: [ `select * from "person"`, `inner join "pet"`, `on "pet"."owner_id" = "person"."id"`, `and "pet"."name" in (?, ?, ?)`, - `and ("pet"."species" = ? or "species" = ? or "species" = (select 'hamster' as "hamster" from "pet" limit ? offset ?))`, + `and ("pet"."species" = ? or "species" = ? or "species" = (select 'hamster' as "hamster" from "pet" limit ?))`, `order by "person"."first_name"`, ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], }, }) @@ -368,7 +401,14 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select "pet"."id" from "person"`, + `inner join "pet" on exists`, + `(select "id" from "pet" as "p" where "p"."id" = "pet"."id" and "p"."owner_id" = "person"."id")`, + ], + parameters: [], + }, sqlite: { sql: [ `select "pet"."id" from "person"`, @@ -400,7 +440,10 @@ for (const dialect of DIALECTS) { sql: `select * from \`person\` left join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` order by \`person\`.\`first_name\``, parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select * from "person" left join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, + parameters: [], + }, sqlite: { sql: `select * from "person" left join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, parameters: [], @@ -443,7 +486,15 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select * from "person"`, + `left join (select "owner_id" as "oid", "name" from "pet") as "p"`, + `on "p"."oid" = "person"."id"`, + `order by "person"."first_name"`, + ], + parameters: [], + }, sqlite: { sql: [ `select * from "person"`, @@ -475,7 +526,10 @@ for (const dialect of DIALECTS) { sql: `select \`pet\`.\`name\` as \`pet_name\`, \`toy\`.\`name\` as \`toy_name\` from \`person\` left join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` left join \`toy\` on \`toy\`.\`pet_id\` = \`pet\`.\`id\` where \`first_name\` = ?`, parameters: ['Jennifer'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select "pet"."name" as "pet_name", "toy"."name" as "toy_name" from "person" left join "pet" on "pet"."owner_id" = "person"."id" left join "toy" on "toy"."pet_id" = "pet"."id" where "first_name" = @1`, + parameters: ['Jennifer'], + }, sqlite: { sql: `select "pet"."name" as "pet_name", "toy"."name" as "toy_name" from "person" left join "pet" on "pet"."owner_id" = "person"."id" left join "toy" on "toy"."pet_id" = "pet"."id" where "first_name" = ?`, parameters: ['Jennifer'], @@ -502,8 +556,7 @@ for (const dialect of DIALECTS) { eb .selectFrom('pet') .select(sql`'hamster'`.as('hamster')) - .limit(1) - .offset(0) + .$call(limit(1, dialect)) ), ]) ) @@ -518,10 +571,10 @@ for (const dialect of DIALECTS) { `left join "pet"`, `on "pet"."owner_id" = "person"."id"`, `and "pet"."name" in ($1, $2, $3)`, - `and ("pet"."species" = $4 or "species" = $5 or "species" = (select 'hamster' as "hamster" from "pet" limit $6 offset $7))`, + `and ("pet"."species" = $4 or "species" = $5 or "species" = (select 'hamster' as "hamster" from "pet" limit $6))`, `order by "person"."first_name"`, ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], }, mysql: { sql: [ @@ -529,22 +582,32 @@ for (const dialect of DIALECTS) { 'left join `pet`', 'on `pet`.`owner_id` = `person`.`id`', 'and `pet`.`name` in (?, ?, ?)', - "and (`pet`.`species` = ? or `species` = ? or `species` = (select 'hamster' as `hamster` from `pet` limit ? offset ?))", + "and (`pet`.`species` = ? or `species` = ? or `species` = (select 'hamster' as `hamster` from `pet` limit ?))", 'order by `person`.`first_name`', ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], + }, + mssql: { + sql: [ + `select * from "person"`, + `left join "pet"`, + `on "pet"."owner_id" = "person"."id"`, + `and "pet"."name" in (@1, @2, @3)`, + `and ("pet"."species" = @4 or "species" = @5 or "species" = (select top 1 'hamster' as "hamster" from "pet"))`, + `order by "person"."first_name"`, + ], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog'], }, - mssql: NOT_SUPPORTED, sqlite: { sql: [ `select * from "person"`, `left join "pet"`, `on "pet"."owner_id" = "person"."id"`, `and "pet"."name" in (?, ?, ?)`, - `and ("pet"."species" = ? or "species" = ? or "species" = (select 'hamster' as "hamster" from "pet" limit ? offset ?))`, + `and ("pet"."species" = ? or "species" = ? or "species" = (select 'hamster' as "hamster" from "pet" limit ?))`, `order by "person"."first_name"`, ], - parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1, 0], + parameters: ['Catto', 'Doggo', 'Hammo', 'cat', 'dog', 1], }, }) @@ -577,7 +640,10 @@ for (const dialect of DIALECTS) { sql: 'select `pet`.`id` from `person` left join `pet` on not exists (select `id` from `pet` as `p` where `p`.`id` = `pet`.`id` and `p`.`owner_id` = `person`.`id`)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select "pet"."id" from "person" left join "pet" on not exists (select "id" from "pet" as "p" where "p"."id" = "pet"."id" and "p"."owner_id" = "person"."id")`, + parameters: [], + }, sqlite: { sql: `select "pet"."id" from "person" left join "pet" on not exists (select "id" from "pet" as "p" where "p"."id" = "pet"."id" and "p"."owner_id" = "person"."id")`, parameters: [], @@ -588,7 +654,7 @@ for (const dialect of DIALECTS) { }) }) - if (dialect !== 'sqlite') { + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { describe('right join', () => { it(`should right join a table`, async () => { const query = ctx.db @@ -606,11 +672,11 @@ for (const dialect of DIALECTS) { sql: `select * from \`person\` right join \`pet\` on \`pet\`.\`owner_id\` = \`person\`.\`id\` order by \`person\`.\`first_name\``, parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { + mssql: { sql: `select * from "person" right join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, parameters: [], }, + sqlite: NOT_SUPPORTED, }) await query.execute() @@ -618,7 +684,7 @@ for (const dialect of DIALECTS) { }) } - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { describe('full join', () => { it(`should full join a table`, async () => { const query = ctx.db @@ -633,14 +699,19 @@ for (const dialect of DIALECTS) { parameters: [], }, mysql: NOT_SUPPORTED, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select * from "person" full join "pet" on "pet"."owner_id" = "person"."id" order by "person"."first_name"`, + parameters: [], + }, sqlite: NOT_SUPPORTED, }) await query.execute() }) }) + } + if (dialect === 'postgres') { describe('lateral join', () => { it('should join an expression laterally', async () => { const query = ctx.db diff --git a/test/node/src/json-traversal.test.ts b/test/node/src/json-traversal.test.ts index 4ef53a802..ddff9ad87 100644 --- a/test/node/src/json-traversal.test.ts +++ b/test/node/src/json-traversal.test.ts @@ -7,7 +7,7 @@ import { } from '../../..' import { BuiltInDialect, - DIALECTS, + DIALECTS_WITH_MSSQL, NOT_SUPPORTED, clearDatabase, destroyTest, @@ -19,7 +19,9 @@ import { type TestContext = Awaited> -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL.filter( + (dialect) => dialect !== 'mssql' +)) { describe(`${dialect}: json traversal`, () => { let ctx: TestContext diff --git a/test/node/src/json.test.ts b/test/node/src/json.test.ts index f3ec67fd2..6855a55d7 100644 --- a/test/node/src/json.test.ts +++ b/test/node/src/json.test.ts @@ -29,7 +29,7 @@ import { Database, insertDefaultDataSet, clearDatabase, - DIALECTS, + DIALECTS_WITH_MSSQL, } from './test-setup.js' interface JsonTable { @@ -66,7 +66,9 @@ const jsonFunctions = { }, } as const -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL.filter( + (dialect) => dialect !== 'mssql' +)) { const { jsonArrayFrom, jsonObjectFrom, jsonBuildObject } = jsonFunctions[dialect] From e067376d9a9ae7b273ff9326eb2e70355e002d0e Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Thu, 3 Aug 2023 23:08:22 +0300 Subject: [PATCH 36/65] introduce mssql to having test suite. --- test/node/src/having.test.ts | 37 ++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/test/node/src/having.test.ts b/test/node/src/having.test.ts index f3384e219..68982b9ef 100644 --- a/test/node/src/having.test.ts +++ b/test/node/src/having.test.ts @@ -1,7 +1,6 @@ import { sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -9,10 +8,10 @@ import { TestContext, testSql, expect, - NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: having`, () => { let ctx: TestContext @@ -88,7 +87,16 @@ for (const dialect of DIALECTS) { ], parameters: [1], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select "first_name", count(pet.id) as "num_pets"`, + `from "person"`, + `inner join "pet" on "pet"."owner_id" = "person"."id"`, + `group by "first_name"`, + `having count(pet.id) > @1`, + ], + parameters: [1], + }, sqlite: { sql: [ `select "first_name", count(pet.id) as "num_pets"`, @@ -104,7 +112,7 @@ for (const dialect of DIALECTS) { const result = await query.execute() expect(result).to.have.length(2) - if (dialect === 'sqlite') { + if (dialect === 'mssql' || dialect === 'sqlite') { expect(result).to.containSubset([ { first_name: 'Jennifer', num_pets: 2 }, { first_name: 'Arnold', num_pets: 2 }, @@ -148,7 +156,16 @@ for (const dialect of DIALECTS) { ], parameters: [1], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select "person"."id", count("pet"."id") as "num_pets"`, + `from "person"`, + `inner join "pet" on "pet"."owner_id" = "person"."id"`, + `group by "person"."id"`, + `having count("pet"."id") > @1`, + ], + parameters: [1], + }, sqlite: { sql: [ `select "person"."id", count("pet"."id") as "num_pets"`, @@ -164,7 +181,7 @@ for (const dialect of DIALECTS) { const result = await query.execute() expect(result).to.have.length(2) - if (dialect === 'sqlite') { + if (dialect === 'mssql' || dialect === 'sqlite') { expect(result).to.containSubset([{ num_pets: 2 }, { num_pets: 2 }]) } else { expect(result).to.containSubset([{ num_pets: '2' }, { num_pets: '2' }]) @@ -185,6 +202,7 @@ for (const dialect of DIALECTS) { ) .havingRef('first_name', '=', 'first_name') .having((eb) => eb.not(eb.exists(eb.selectFrom('pet').select('id')))) + testSql(query, dialect, { postgres: { sql: `select * from "person" group by "first_name" having ("id" in ($1, $2, $3) or "first_name" < $4 or "first_name" = "first_name") and "first_name" = "first_name" and not exists (select "id" from "pet")`, @@ -194,7 +212,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` group by `first_name` having (`id` in (?, ?, ?) or `first_name` < ? or `first_name` = `first_name`) and `first_name` = `first_name` and not exists (select `id` from `pet`)', parameters: [1, 2, 3, 'foo'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select * from "person" group by "first_name" having ("id" in (@1, @2, @3) or "first_name" < @4 or "first_name" = "first_name") and "first_name" = "first_name" and not exists (select "id" from "pet")`, + parameters: [1, 2, 3, 'foo'], + }, sqlite: { sql: `select * from "person" group by "first_name" having ("id" in (?, ?, ?) or "first_name" < ? or "first_name" = "first_name") and "first_name" = "first_name" and not exists (select "id" from "pet")`, parameters: [1, 2, 3, 'foo'], From 60559e200db5bb86fb85267179e57ff7ec2ed0f8 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Thu, 3 Aug 2023 23:23:43 +0300 Subject: [PATCH 37/65] introduce mssql into group by test suite. --- test/node/src/group-by.test.ts | 190 +++++++++++++++++++-------------- 1 file changed, 107 insertions(+), 83 deletions(-) diff --git a/test/node/src/group-by.test.ts b/test/node/src/group-by.test.ts index 1c7ec0d17..112b65310 100644 --- a/test/node/src/group-by.test.ts +++ b/test/node/src/group-by.test.ts @@ -1,7 +1,6 @@ import { sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -10,9 +9,10 @@ import { expect, insertDefaultDataSet, NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: group by`, () => { let ctx: TestContext @@ -48,7 +48,10 @@ for (const dialect of DIALECTS) { sql: 'select `gender`, max(first_name) as `max_first_name` from `person` group by `gender` order by `gender`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "gender" order by "gender"', + parameters: [], + }, sqlite: { sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "gender" order by "gender"', parameters: [], @@ -70,43 +73,45 @@ for (const dialect of DIALECTS) { ]) }) - it('group by selection', async () => { - const query = ctx.db - .selectFrom('person') - .select(['gender as g', sql`max(first_name)`.as('max_first_name')]) - .groupBy('g') - .orderBy('g') + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'sqlite') { + it('group by selection', async () => { + const query = ctx.db + .selectFrom('person') + .select(['gender as g', sql`max(first_name)`.as('max_first_name')]) + .groupBy('g') + .orderBy('g') - testSql(query, dialect, { - postgres: { - sql: 'select "gender" as "g", max(first_name) as "max_first_name" from "person" group by "g" order by "g"', - parameters: [], - }, - mysql: { - sql: 'select `gender` as `g`, max(first_name) as `max_first_name` from `person` group by `g` order by `g`', - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: 'select "gender" as "g", max(first_name) as "max_first_name" from "person" group by "g" order by "g"', - parameters: [], - }, - }) + testSql(query, dialect, { + postgres: { + sql: 'select "gender" as "g", max(first_name) as "max_first_name" from "person" group by "g" order by "g"', + parameters: [], + }, + mysql: { + sql: 'select `gender` as `g`, max(first_name) as `max_first_name` from `person` group by `g` order by `g`', + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: 'select "gender" as "g", max(first_name) as "max_first_name" from "person" group by "g" order by "g"', + parameters: [], + }, + }) - const persons = await query.execute() + const persons = await query.execute() - expect(persons).to.have.length(2) - expect(persons).to.containSubset([ - { - max_first_name: 'Jennifer', - g: 'female', - }, - { - max_first_name: 'Sylvester', - g: 'male', - }, - ]) - }) + expect(persons).to.have.length(2) + expect(persons).to.containSubset([ + { + max_first_name: 'Jennifer', + g: 'female', + }, + { + max_first_name: 'Sylvester', + g: 'male', + }, + ]) + }) + } it('group by two columns', async () => { const query = ctx.db @@ -124,7 +129,10 @@ for (const dialect of DIALECTS) { sql: 'select `gender`, max(first_name) as `max_first_name` from `person` group by `gender`, `id` order by `gender`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "gender", "id" order by "gender"', + parameters: [], + }, sqlite: { sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "gender", "id" order by "gender"', parameters: [], @@ -150,7 +158,10 @@ for (const dialect of DIALECTS) { sql: 'select `gender`, max(first_name) as `max_first_name` from `person` group by `person`.`gender` order by `gender` asc', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "person"."gender" order by "gender" asc', + parameters: [], + }, sqlite: { sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by "person"."gender" order by "gender" asc', parameters: [], @@ -176,7 +187,10 @@ for (const dialect of DIALECTS) { sql: 'select `gender`, max(first_name) as `max_first_name` from `person` group by person.gender order by `gender` asc', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by person.gender order by "gender" asc', + parameters: [], + }, sqlite: { sql: 'select "gender", max(first_name) as "max_first_name" from "person" group by person.gender order by "gender" asc', parameters: [], @@ -186,51 +200,61 @@ for (const dialect of DIALECTS) { await query.execute() }) - it('group by a sub query', async () => { - const query = ctx.db - .selectFrom('person') - .select(sql`max(first_name)`.as('max_first_name')) - .groupBy((qb) => - qb - .selectFrom('pet') - .whereRef('person.id', '=', 'pet.owner_id') - .select('pet.name') - ) - .orderBy('max_first_name') + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'sqlite') { + it('group by a sub query', async () => { + const query = ctx.db + .selectFrom('person') + .select(sql`max(first_name)`.as('max_first_name')) + .groupBy((qb) => + qb + .selectFrom('pet') + .whereRef('person.id', '=', 'pet.owner_id') + .select('pet.name') + ) + .orderBy('max_first_name') - testSql(query, dialect, { - postgres: { - sql: [ - 'select max(first_name) as "max_first_name"', - 'from "person"', - 'group by (select "pet"."name" from "pet" where "person"."id" = "pet"."owner_id")', - 'order by "max_first_name"', - ], - parameters: [], - }, - mysql: { - sql: [ - 'select max(first_name) as `max_first_name`', - 'from `person`', - 'group by (select `pet`.`name` from `pet` where `person`.`id` = `pet`.`owner_id`)', - 'order by `max_first_name`', - ], - parameters: [], - }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: [ - 'select max(first_name) as "max_first_name"', - 'from "person"', - 'group by (select "pet"."name" from "pet" where "person"."id" = "pet"."owner_id")', - 'order by "max_first_name"', - ], - parameters: [], - }, - }) + testSql(query, dialect, { + postgres: { + sql: [ + 'select max(first_name) as "max_first_name"', + 'from "person"', + 'group by (select "pet"."name" from "pet" where "person"."id" = "pet"."owner_id")', + 'order by "max_first_name"', + ], + parameters: [], + }, + mysql: { + sql: [ + 'select max(first_name) as `max_first_name`', + 'from `person`', + 'group by (select `pet`.`name` from `pet` where `person`.`id` = `pet`.`owner_id`)', + 'order by `max_first_name`', + ], + parameters: [], + }, + mssql: { + sql: [ + 'select max(first_name) as "max_first_name"', + 'from "person"', + 'group by (select "pet"."name" from "pet" where "person"."id" = "pet"."owner_id")', + 'order by "max_first_name"', + ], + parameters: [], + }, + sqlite: { + sql: [ + 'select max(first_name) as "max_first_name"', + 'from "person"', + 'group by (select "pet"."name" from "pet" where "person"."id" = "pet"."owner_id")', + 'order by "max_first_name"', + ], + parameters: [], + }, + }) - await query.execute() - }) + await query.execute() + }) + } it('conditional group by', async () => { const filterByPetCount = true From cc4cf34b62ea53796ecad5b2f78eefd247107f58 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 00:20:43 +0300 Subject: [PATCH 38/65] empty and/or works everywhere. --- src/parser/binary-operation-parser.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/parser/binary-operation-parser.ts b/src/parser/binary-operation-parser.ts index f788e6637..981b59fa2 100644 --- a/src/parser/binary-operation-parser.ts +++ b/src/parser/binary-operation-parser.ts @@ -133,7 +133,11 @@ export function parseFilterList( const combine = combinator === 'and' ? AndNode.create : OrNode.create if (list.length === 0) { - return ValueNode.createImmediate(combinator === 'and') + return BinaryOperationNode.create( + ValueNode.createImmediate(1), + OperatorNode.create('='), + ValueNode.createImmediate(combinator === 'and' ? 1 : 0) + ) } let node = toOperationNode(list[0]) From ebcca8bb9a3d9cff53f324bcdb583308f3f7745c Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 00:21:10 +0300 Subject: [PATCH 39/65] introduce mssql to expression test suite. --- test/node/src/expression.test.ts | 48 ++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/test/node/src/expression.test.ts b/test/node/src/expression.test.ts index 4a3c08d63..ee77b9686 100644 --- a/test/node/src/expression.test.ts +++ b/test/node/src/expression.test.ts @@ -1,16 +1,15 @@ import { - DIALECTS, clearDatabase, destroyTest, initTest, insertDefaultDataSet, TestContext, testSql, - NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { - describe(`${dialect}: expressions`, () => { +for (const dialect of DIALECTS_WITH_MSSQL) { + describe.only(`${dialect}: expressions`, () => { let ctx: TestContext before(async function () { @@ -70,7 +69,11 @@ for (const dialect of DIALECTS) { last_name: eb.ref('first_name'), }), // Boolean literal - eb.lit(true), + ...(dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ? [eb.lit(true)] + : []), // Between expressions eb.between('id', 1000, 2000), ...(dialect === 'postgres' @@ -81,7 +84,7 @@ for (const dialect of DIALECTS) { testSql(query, dialect, { postgres: { - sql: 'select "person".* from "person" where ((not "first_name" = $1 or "id" + $2 > $3 or "id" in ($4, $5, $6) or upper("first_name") = $7 or false) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and true and ("id" = $8 or "id" = $9 or "id" = $10 or "id" = $11) and ("id" = $12 and "first_name" = $13 and "last_name" = $14 and "marital_status" = $15) and ("id" = $16 or "id" = $17) and ("id" + $18) > $19 and ("first_name" = $20 and "last_name" = $21) and ("first_name" = "last_name" or "last_name" = "first_name") and true and "id" between $22 and $23 and "id" between symmetric $24 and $25)', + sql: 'select "person".* from "person" where ((not "first_name" = $1 or "id" + $2 > $3 or "id" in ($4, $5, $6) or upper("first_name") = $7 or 1 = 0) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and 1 = 1 and ("id" = $8 or "id" = $9 or "id" = $10 or "id" = $11) and ("id" = $12 and "first_name" = $13 and "last_name" = $14 and "marital_status" = $15) and ("id" = $16 or "id" = $17) and ("id" + $18) > $19 and ("first_name" = $20 and "last_name" = $21) and ("first_name" = "last_name" or "last_name" = "first_name") and true and "id" between $22 and $23 and "id" between symmetric $24 and $25)', parameters: [ 'Jennifer', 1, @@ -111,7 +114,35 @@ for (const dialect of DIALECTS) { ], }, mysql: { - sql: 'select `person`.* from `person` where ((not `first_name` = ? or `id` + ? > ? or `id` in (?, ?, ?) or upper(`first_name`) = ? or false) and exists (select `pet`.`id` from `pet` where `pet`.`owner_id` = `person`.`id`) and true and (`id` = ? or `id` = ? or `id` = ? or `id` = ?) and (`id` = ? and `first_name` = ? and `last_name` = ? and `marital_status` = ?) and (`id` = ? or `id` = ?) and (`id` + ?) > ? and (`first_name` = ? and `last_name` = ?) and (`first_name` = `last_name` or `last_name` = `first_name`) and true and `id` between ? and ?)', + sql: 'select `person`.* from `person` where ((not `first_name` = ? or `id` + ? > ? or `id` in (?, ?, ?) or upper(`first_name`) = ? or 1 = 0) and exists (select `pet`.`id` from `pet` where `pet`.`owner_id` = `person`.`id`) and 1 = 1 and (`id` = ? or `id` = ? or `id` = ? or `id` = ?) and (`id` = ? and `first_name` = ? and `last_name` = ? and `marital_status` = ?) and (`id` = ? or `id` = ?) and (`id` + ?) > ? and (`first_name` = ? and `last_name` = ?) and (`first_name` = `last_name` or `last_name` = `first_name`) and true and `id` between ? and ?)', + parameters: [ + 'Jennifer', + 1, + 10, + 10, + 20, + 30, + 'SYLVESTER', + 1, + 2, + 3, + 4, + 1, + 'Jennifer', + 'Aniston', + 'divorced', + 1, + 2, + 1, + 10, + 'Jennifer', + 'Aniston', + 1000, + 2000, + ], + }, + mssql: { + sql: 'select "person".* from "person" where ((not "first_name" = @1 or "id" + @2 > @3 or "id" in (@4, @5, @6) or upper("first_name") = @7 or 1 = 0) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and 1 = 1 and ("id" = @8 or "id" = @9 or "id" = @10 or "id" = @11) and ("id" = @12 and "first_name" = @13 and "last_name" = @14 and "marital_status" = @15) and ("id" = @16 or "id" = @17) and ("id" + @18) > @19 and ("first_name" = @20 and "last_name" = @21) and ("first_name" = "last_name" or "last_name" = "first_name") and "id" between @22 and @23)', parameters: [ 'Jennifer', 1, @@ -138,9 +169,8 @@ for (const dialect of DIALECTS) { 2000, ], }, - mssql: NOT_SUPPORTED, sqlite: { - sql: 'select "person".* from "person" where ((not "first_name" = ? or "id" + ? > ? or "id" in (?, ?, ?) or upper("first_name") = ? or false) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and true and ("id" = ? or "id" = ? or "id" = ? or "id" = ?) and ("id" = ? and "first_name" = ? and "last_name" = ? and "marital_status" = ?) and ("id" = ? or "id" = ?) and ("id" + ?) > ? and ("first_name" = ? and "last_name" = ?) and ("first_name" = "last_name" or "last_name" = "first_name") and true and "id" between ? and ?)', + sql: 'select "person".* from "person" where ((not "first_name" = ? or "id" + ? > ? or "id" in (?, ?, ?) or upper("first_name") = ? or 1 = 0) and exists (select "pet"."id" from "pet" where "pet"."owner_id" = "person"."id") and 1 = 1 and ("id" = ? or "id" = ? or "id" = ? or "id" = ?) and ("id" = ? and "first_name" = ? and "last_name" = ? and "marital_status" = ?) and ("id" = ? or "id" = ?) and ("id" + ?) > ? and ("first_name" = ? and "last_name" = ?) and ("first_name" = "last_name" or "last_name" = "first_name") and true and "id" between ? and ?)', parameters: [ 'Jennifer', 1, From 1328b0b333026e9f5ae6f5b9705512c3dcc94c39 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 00:39:39 +0300 Subject: [PATCH 40/65] introduce mssql to execute test suite. --- test/node/src/execute.test.ts | 4 ++-- test/node/src/explain.test.ts | 14 ++++++++------ test/node/src/expression.test.ts | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/test/node/src/execute.test.ts b/test/node/src/execute.test.ts index 470aa7b80..e49170dfe 100644 --- a/test/node/src/execute.test.ts +++ b/test/node/src/execute.test.ts @@ -6,16 +6,16 @@ import { QueryNode, } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, insertPersons, TestContext, expect, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: execute`, () => { let ctx: TestContext diff --git a/test/node/src/explain.test.ts b/test/node/src/explain.test.ts index 0e3dfd0f2..8cf9692c2 100644 --- a/test/node/src/explain.test.ts +++ b/test/node/src/explain.test.ts @@ -2,16 +2,18 @@ import { expect } from 'chai' import { createSandbox, SinonSpy } from 'sinon' import { DefaultQueryExecutor, sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, insertDefaultDataSet, NOT_SUPPORTED, TestContext, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL.filter( + (dialect) => dialect !== 'mssql' +)) { describe(`${dialect}: explain test`, () => { let ctx: TestContext const sandbox = createSandbox() @@ -46,7 +48,7 @@ for (const dialect of DIALECTS) { { postgres: 'explain select * from "person" limit $1', mysql: 'explain select * from `person` limit ?', - mssql: 'explain select * from "person" limit ?', + mssql: NOT_SUPPORTED, sqlite: 'explain select * from "person" limit ?', }[dialect] ) @@ -60,7 +62,7 @@ for (const dialect of DIALECTS) { { postgres: 'explain insert into "person" ("gender") values ($1)', mysql: 'explain insert into `person` (`gender`) values (?)', - mssql: 'explain insert into "person" ("gender") values ($1)', + mssql: NOT_SUPPORTED, sqlite: 'explain insert into "person" ("gender") values (?)', }[dialect] ) @@ -78,7 +80,7 @@ for (const dialect of DIALECTS) { { postgres: 'explain update "person" set "gender" = $1 where "id" = $2', mysql: 'explain update `person` set `gender` = ? where `id` = ?', - mssql: 'explain update "person" set "gender" = $1 where "id" = $2', + mssql: NOT_SUPPORTED, sqlite: 'explain update "person" set "gender" = ? where "id" = ?', }[dialect] ) @@ -92,7 +94,7 @@ for (const dialect of DIALECTS) { { postgres: 'explain delete from "person" where "id" = $1', mysql: 'explain delete from `person` where `id` = ?', - mssql: 'explain delete from "person" where "id" = $1', + mssql: NOT_SUPPORTED, sqlite: 'explain delete from "person" where "id" = ?', }[dialect] ) diff --git a/test/node/src/expression.test.ts b/test/node/src/expression.test.ts index ee77b9686..d4dcf93aa 100644 --- a/test/node/src/expression.test.ts +++ b/test/node/src/expression.test.ts @@ -9,7 +9,7 @@ import { } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { - describe.only(`${dialect}: expressions`, () => { + describe(`${dialect}: expressions`, () => { let ctx: TestContext before(async function () { From aae2c401b27925239070de0618da06ebcb044da6 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 00:53:11 +0300 Subject: [PATCH 41/65] introduce mssql to deduplicate joins test suite. --- test/node/src/deduplicate-joins.test.ts | 20 ++++++++++++++------ test/node/src/error-stack.test.ts | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/test/node/src/deduplicate-joins.test.ts b/test/node/src/deduplicate-joins.test.ts index b5ce8623b..6757bf4e1 100644 --- a/test/node/src/deduplicate-joins.test.ts +++ b/test/node/src/deduplicate-joins.test.ts @@ -1,17 +1,16 @@ import { DeduplicateJoinsPlugin } from '../../..' import { - DIALECTS, clearDatabase, destroyTest, initTest, TestContext, testSql, insertDefaultDataSet, - NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: deduplicate joins`, () => { let ctx: TestContext @@ -47,7 +46,10 @@ for (const dialect of DIALECTS) { sql: 'select from `person` inner join `pet` on `pet`.`owner_id` = `person`.`id`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select from "person" inner join "pet" on "pet"."owner_id" = "person"."id"', + parameters: [], + }, sqlite: { sql: 'select from "person" inner join "pet" on "pet"."owner_id" = "person"."id"', parameters: [], @@ -89,7 +91,10 @@ for (const dialect of DIALECTS) { sql: 'select from `person` inner join (select `owner_id`, `id` as `pet_id`, `species` from `pet`) as `p` on `p`.`owner_id` = `person`.`id` and `p`.`species` in (?, ?)', parameters: ['cat', 'hamster'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select from "person" inner join (select "owner_id", "id" as "pet_id", "species" from "pet") as "p" on "p"."owner_id" = "person"."id" and "p"."species" in (@1, @2)', + parameters: ['cat', 'hamster'], + }, sqlite: { sql: 'select from "person" inner join (select "owner_id", "id" as "pet_id", "species" from "pet") as "p" on "p"."owner_id" = "person"."id" and "p"."species" in (?, ?)', parameters: ['cat', 'hamster'], @@ -113,7 +118,10 @@ for (const dialect of DIALECTS) { sql: 'select from `person` inner join `pet` on `pet`.`owner_id` = `person`.`id` inner join `toy` on `toy`.`pet_id` = `pet`.`id`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'select from "person" inner join "pet" on "pet"."owner_id" = "person"."id" inner join "toy" on "toy"."pet_id" = "pet"."id"', + parameters: [], + }, sqlite: { sql: 'select from "person" inner join "pet" on "pet"."owner_id" = "person"."id" inner join "toy" on "toy"."pet_id" = "pet"."id"', parameters: [], diff --git a/test/node/src/error-stack.test.ts b/test/node/src/error-stack.test.ts index a5929ee9d..9e447c26f 100644 --- a/test/node/src/error-stack.test.ts +++ b/test/node/src/error-stack.test.ts @@ -2,14 +2,14 @@ import { AssertionError } from 'chai' import { sql } from '../../../' import { - DIALECTS, destroyTest, initTest, TestContext, expect, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: error stack`, () => { let ctx: TestContext From b393ba7e966bf04612b1aa9ea219d099eac8fc54 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 01:32:02 +0300 Subject: [PATCH 42/65] introduce mssql to coalesce test suite. --- test/node/src/coalesce.test.ts | 60 +++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/test/node/src/coalesce.test.ts b/test/node/src/coalesce.test.ts index 15f3f5772..45ea4c62f 100644 --- a/test/node/src/coalesce.test.ts +++ b/test/node/src/coalesce.test.ts @@ -1,6 +1,5 @@ import { sql } from '../../..' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -8,9 +7,10 @@ import { NOT_SUPPORTED, TestContext, testSql, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: coalesce`, () => { let ctx: TestContext @@ -87,7 +87,11 @@ for (const dialect of DIALECTS) { coalesce('first_name', ctx.db.dynamic.ref('last_name')).as( 'ColumnReference1' ), - coalesce('first_name', sql`${1}`).as('ColumnReference2'), + ...(dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ? [coalesce('first_name', sql`${1}`).as('ColumnReference2')] + : []), coalesce('first_name', max('last_name')).as('ColumnReference3'), coalesce( ctx.db.dynamic.ref('first_name'), @@ -96,9 +100,15 @@ for (const dialect of DIALECTS) { coalesce(ctx.db.dynamic.ref('first_name'), 'last_name').as( 'DynamicReference1' ), - coalesce(ctx.db.dynamic.ref('first_name'), sql`${2}`).as( - 'DynamicReference2' - ), + ...(dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ? [ + coalesce(ctx.db.dynamic.ref('first_name'), sql`${2}`).as( + 'DynamicReference2' + ), + ] + : []), coalesce(ctx.db.dynamic.ref('first_name'), max('last_name')).as( 'DynamicReference3' ), @@ -115,7 +125,11 @@ for (const dialect of DIALECTS) { coalesce(max('first_name'), ctx.db.dynamic.ref('last_name')).as( 'AggregateFunction2' ), - coalesce(max('first_name'), sql`${8}`).as('AggregateFunction3'), + ...(dialect === 'postgres' || + dialect === 'mysql' || + dialect === 'sqlite' + ? [coalesce(max('first_name'), sql`${8}`).as('AggregateFunction3')] + : []), ]) .groupBy(['first_name', 'last_name']) @@ -168,7 +182,27 @@ for (const dialect of DIALECTS) { ], parameters: [1, 2, 3, 4, 5, 6, 7, 8], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'select', + 'coalesce("first_name", "last_name") as "ColumnReference0",', + 'coalesce("first_name", "last_name") as "ColumnReference1",', + 'coalesce("first_name", max("last_name")) as "ColumnReference3",', + 'coalesce("first_name", "last_name") as "DynamicReference0",', + 'coalesce("first_name", "last_name") as "DynamicReference1",', + 'coalesce("first_name", max("last_name")) as "DynamicReference3",', + 'coalesce(@1, @2) as "RawBuilder0",', + 'coalesce(@3, "last_name") as "RawBuilder1",', + 'coalesce(@4, "last_name") as "RawBuilder2",', + 'coalesce(@5, max("last_name")) as "RawBuilder3",', + 'coalesce(max("first_name"), max("last_name")) as "AggregateFunction0",', + 'coalesce(max("first_name"), "last_name") as "AggregateFunction1",', + 'coalesce(max("first_name"), "last_name") as "AggregateFunction2"', + 'from "person"', + 'group by "first_name", "last_name"', + ], + parameters: [3, 4, 5, 6, 7], + }, sqlite: { sql: [ 'select', @@ -232,7 +266,15 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'select', + `coalesce("first_name", "last_name", max("last_name"), '(N/A)') as "name"`, + 'from "person"', + 'group by "first_name", "last_name"', + ], + parameters: [], + }, sqlite: { sql: [ 'select', From 23b053562770aff78153e85cc99e0482ca620596 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 01:40:07 +0300 Subject: [PATCH 43/65] introduce mssql to clear test suite. --- test/node/src/clear.test.ts | 91 ++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/test/node/src/clear.test.ts b/test/node/src/clear.test.ts index be18b6633..7dbf1672d 100644 --- a/test/node/src/clear.test.ts +++ b/test/node/src/clear.test.ts @@ -1,13 +1,14 @@ import { - DIALECTS, destroyTest, initTest, TestContext, testSql, NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, + limit, } from './test-setup' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect} clear`, () => { let ctx: TestContext @@ -35,7 +36,10 @@ for (const dialect of DIALECTS) { sql: 'select `id` from `person`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select "id" from "person"`, + parameters: [], + }, sqlite: { sql: `select "id" from "person"`, parameters: [], @@ -59,7 +63,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select * from "person"`, + parameters: [], + }, sqlite: { sql: `select * from "person"`, parameters: [], @@ -81,7 +88,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` on conflict do nothing', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `insert into "person" on conflict do nothing`, + parameters: [], + }, sqlite: { sql: `insert into "person" on conflict do nothing`, parameters: [], @@ -108,7 +118,10 @@ for (const dialect of DIALECTS) { sql: 'insert into `person` on conflict do update set `gender` = ?', parameters: ['other'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `insert into "person" on conflict do update set "gender" = @1`, + parameters: ['other'], + }, sqlite: { sql: `insert into "person" on conflict do update set "gender" = ?`, parameters: ['other'], @@ -132,7 +145,10 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `gender` = ?', parameters: ['other'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `update "person" set "gender" = @1`, + parameters: ['other'], + }, sqlite: { sql: `update "person" set "gender" = ?`, parameters: ['other'], @@ -155,7 +171,10 @@ for (const dialect of DIALECTS) { sql: 'delete from `person`', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `delete from "person"`, + parameters: [], + }, sqlite: { sql: `delete from "person"`, parameters: [], @@ -179,31 +198,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person`', parameters: [], }, - mssql: NOT_SUPPORTED, - sqlite: { - sql: `select * from "person"`, - parameters: [], - }, - }) - }) - - it('should clear limit', () => { - const query = ctx.db - .selectFrom('person') - .selectAll() - .limit(100) - .clearLimit() - - testSql(query, dialect, { - postgres: { + mssql: { sql: `select * from "person"`, parameters: [], }, - mysql: { - sql: 'select * from `person`', - parameters: [], - }, - mssql: NOT_SUPPORTED, sqlite: { sql: `select * from "person"`, parameters: [], @@ -211,11 +209,37 @@ for (const dialect of DIALECTS) { }) }) + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'sqlite') { + it('should clear limit', () => { + const query = ctx.db + .selectFrom('person') + .selectAll() + .limit(100) + .clearLimit() + + testSql(query, dialect, { + postgres: { + sql: `select * from "person"`, + parameters: [], + }, + mysql: { + sql: 'select * from `person`', + parameters: [], + }, + mssql: NOT_SUPPORTED, + sqlite: { + sql: `select * from "person"`, + parameters: [], + }, + }) + }) + } + it('should clear offset', () => { const query = ctx.db .selectFrom('person') .selectAll() - .limit(1) + .$call(limit(1, dialect)) .offset(100) .clearOffset() @@ -228,7 +252,10 @@ for (const dialect of DIALECTS) { sql: 'select * from `person` limit ?', parameters: [1], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select top 1 * from "person"`, + parameters: [], + }, sqlite: { sql: `select * from "person" limit ?`, parameters: [1], From f9771c3d69dce955f6ea344aa5a1373c2e9a5cc0 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 01:49:02 +0300 Subject: [PATCH 44/65] introduce mssql case test suite. --- test/node/src/case.test.ts | 54 +++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/test/node/src/case.test.ts b/test/node/src/case.test.ts index 5023bb77a..6bf9222a1 100644 --- a/test/node/src/case.test.ts +++ b/test/node/src/case.test.ts @@ -1,7 +1,6 @@ import { sql } from '../../..' import { - DIALECTS, - NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, TestContext, clearDatabase, destroyTest, @@ -10,7 +9,7 @@ import { testSql, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: case`, () => { let ctx: TestContext @@ -46,7 +45,10 @@ for (const dialect of DIALECTS) { sql: 'select case when `gender` = ? then ? end as `title` from `person`', parameters: ['male', 'Mr.'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select case when "gender" = @1 then @2 end as "title" from "person"`, + parameters: ['male', 'Mr.'], + }, sqlite: { sql: `select case when "gender" = ? then ? end as "title" from "person"`, parameters: ['male', 'Mr.'], @@ -72,7 +74,10 @@ for (const dialect of DIALECTS) { sql: 'select case `gender` when ? then ? end as `title` from `person`', parameters: ['male', 'Mr.'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `select case "gender" when @1 then @2 end as "title" from "person"`, + parameters: ['male', 'Mr.'], + }, sqlite: { sql: `select case "gender" when ? then ? end as "title" from "person"`, parameters: ['male', 'Mr.'], @@ -113,7 +118,14 @@ for (const dialect of DIALECTS) { ], parameters: ['male', 'female'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select case when "gender" = @1 then 'Mr.'`, + `when "gender" = @2 then 'Mrs.'`, + `end as "title" from "person"`, + ], + parameters: ['male', 'female'], + }, sqlite: { sql: [ `select case when "gender" = ? then 'Mr.'`, @@ -158,7 +170,14 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select case "gender" when 'male' then 'Mr.'`, + `when 'female' then 'Mrs.'`, + `end as "title" from "person"`, + ], + parameters: [], + }, sqlite: { sql: [ `select case "gender" when 'male' then 'Mr.'`, @@ -204,7 +223,14 @@ for (const dialect of DIALECTS) { ], parameters: ['male', 'Mr.', 'female', 'Mrs.'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select case when "gender" = @1 then @2`, + `when "gender" = @3 then @4`, + `else null end as "title" from "person"`, + ], + parameters: ['male', 'Mr.', 'female', 'Mrs.'], + }, sqlite: { sql: [ `select case when "gender" = ? then ?`, @@ -265,7 +291,17 @@ for (const dialect of DIALECTS) { ], parameters: ['male', 'Mr.', 'female', 'single', 'Ms.', 'Mrs.'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'select case "gender" when @1 then @2', + 'when @3 then', + 'case when ("marital_status" = @4 or', + '"marital_status" is null) then @5', + 'else @6 end', + 'end as "title" from "person"', + ], + parameters: ['male', 'Mr.', 'female', 'single', 'Ms.', 'Mrs.'], + }, sqlite: { sql: [ 'select case "gender" when ? then ?', From 9f82a6d242884eb910a15910c9bcc9199403f5ed Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 02:01:39 +0300 Subject: [PATCH 45/65] introduce mssql to camel case test suite. --- test/node/src/camel-case.test.ts | 44 ++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/test/node/src/camel-case.test.ts b/test/node/src/camel-case.test.ts index db4745176..0f16ec632 100644 --- a/test/node/src/camel-case.test.ts +++ b/test/node/src/camel-case.test.ts @@ -1,17 +1,16 @@ import { CamelCasePlugin, Generated, Kysely, RawBuilder, sql } from '../../../' import { - DIALECTS, destroyTest, initTest, TestContext, testSql, expect, createTableWithId, - NOT_SUPPORTED, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: camel case test`, () => { let ctx: TestContext let camelDb: Kysely @@ -41,7 +40,10 @@ for (const dialect of DIALECTS) { await createTableWithId(camelDb.schema, dialect, 'camelPerson') .addColumn('firstName', 'varchar(255)') .addColumn('lastName', 'varchar(255)') - .addColumn('preferences', 'json') + .addColumn( + 'preferences', + dialect === 'mssql' ? 'varchar(8000)' : 'json' + ) .execute() }) @@ -75,7 +77,7 @@ for (const dialect of DIALECTS) { // Can't run this test on SQLite because we can't access the same database // from the other Kysely instance. - if (dialect !== 'sqlite') { + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { it('should have created the table and its columns in snake_case', async () => { const result = await sql`select * from camel_person`.execute( ctx.db @@ -118,7 +120,15 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select "camel_person"."first_name"`, + `from "camel_person"`, + `inner join "camel_person" as "camel_person2" on "camel_person2"."id" = "camel_person"."id"`, + `order by "camel_person"."first_name"`, + ], + parameters: [], + }, sqlite: { sql: [ `select "camel_person"."first_name"`, @@ -169,7 +179,15 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select "camel_person"."first_name"`, + `from "camel_person"`, + `inner join "camel_person" as "camel_person2" on "camel_person2"."id" = "camel_person"."id"`, + `order by "camel_person"."first_name"`, + ], + parameters: [], + }, sqlite: { sql: [ `select "camel_person"."first_name"`, @@ -206,7 +224,10 @@ for (const dialect of DIALECTS) { sql: 'alter table `camel_person` add column `middle_name` text references `camel_person` (`first_name`)', parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: 'alter table "camel_person" add "middle_name" text references "camel_person" ("first_name")', + parameters: [], + }, sqlite: { sql: 'alter table "camel_person" add column "middle_name" text references "camel_person" ("first_name")', parameters: [], @@ -229,7 +250,10 @@ for (const dialect of DIALECTS) { sql: 'delete from `camel_person` as `c` using `camel_person` where `camel_person`.`first_name` = ?', parameters: ['Arnold'], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: `delete from "camel_person" as "c" using "camel_person" where "camel_person"."first_name" = @1`, + parameters: ['Arnold'], + }, sqlite: { sql: `delete from "camel_person" as "c" using "camel_person" where "camel_person"."first_name" = ?`, parameters: ['Arnold'], @@ -245,7 +269,7 @@ for (const dialect of DIALECTS) { .selectAll() .executeTakeFirstOrThrow() - if (dialect === 'sqlite') { + if (dialect === 'mssql' || dialect === 'sqlite') { data.preferences = JSON.parse(data.preferences.toString()) } From fb8741c7d9f01492aee41ff61e7b58a429d7aec6 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 03:00:15 +0300 Subject: [PATCH 46/65] introduce mssql to aggregate function test suite. --- test/node/src/aggregate-function.test.ts | 274 +++++++++++++++++------ test/node/src/array.test.ts | 16 +- test/node/src/test-setup.ts | 2 + 3 files changed, 211 insertions(+), 81 deletions(-) diff --git a/test/node/src/aggregate-function.test.ts b/test/node/src/aggregate-function.test.ts index 804aa8e41..8f612ea42 100644 --- a/test/node/src/aggregate-function.test.ts +++ b/test/node/src/aggregate-function.test.ts @@ -5,19 +5,19 @@ import { sql, } from '../../../' import { - DIALECTS, Database, destroyTest, initTest, NOT_SUPPORTED, TestContext, testSql, + DIALECTS_WITH_MSSQL, } from './test-setup.js' const funcNames = ['avg', 'count', 'max', 'min', 'sum'] as const -for (const dialect of DIALECTS) { - describe(`${dialect}: aggregate functions`, () => { +for (const dialect of DIALECTS_WITH_MSSQL) { + describe.only(`${dialect}: aggregate functions`, () => { let ctx: TestContext before(async function () { @@ -65,7 +65,14 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}("id") as "${funcName}",`, + `${funcName}("person"."id") as "another_${funcName}"`, + `from "person"`, + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}("id") as "${funcName}",`, @@ -110,7 +117,14 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}(distinct "id") as "${funcName}",`, + `${funcName}(distinct "person"."id") as "another_${funcName}"`, + `from "person"`, + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}(distinct "id") as "${funcName}",`, @@ -155,7 +169,14 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}("id") over() as "${funcName}",`, + `${funcName}("person"."id") over() as "another_${funcName}"`, + `from "person"`, + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}("id") over() as "${funcName}",`, @@ -204,7 +225,16 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}("id")`, + `over(partition by "first_name") as "${funcName}",`, + `${funcName}("person"."id")`, + `over(partition by "person"."first_name") as "another_${funcName}"`, + `from "person"`, + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}("id")`, @@ -265,7 +295,18 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}("id")`, + `over(order by "last_name" asc,`, + `"first_name" asc) as "${funcName}",`, + `${funcName}("person"."id")`, + `over(order by "person"."last_name" desc,`, + `"person"."first_name" desc) as "another_${funcName}"`, + `from "person"`, + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}("id")`, @@ -336,7 +377,20 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}("id")`, + `over(partition by "gender"`, + `order by "last_name" asc,`, + `"first_name" asc) as "${funcName}",`, + `${funcName}("person"."id")`, + `over(partition by "person"."gender"`, + `order by "person"."last_name" desc,`, + `"person"."first_name" desc) as "another_${funcName}"`, + `from "person"`, + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}("id")`, @@ -359,79 +413,99 @@ for (const dialect of DIALECTS) { it(`should execute a query with ${funcName}(column) in having clause`, async () => { const query = ctx.db .selectFrom('person') - .selectAll() - .groupBy(['person.gender']) - .having(func('person.id'), '>=', 3) + .select(['person.children']) + .groupBy(['person.children']) + .having(func('person.children'), '>=', 3) testSql(query, dialect, { postgres: { sql: [ - `select *`, + `select "person"."children"`, `from "person"`, - `group by "person"."gender"`, - `having ${funcName}("person"."id") >= $1`, + `group by "person"."children"`, + `having ${funcName}("person"."children") >= $1`, ], parameters: [3], }, mysql: { sql: [ - `select *`, + `select \`person\`.\`children\``, `from \`person\``, - `group by \`person\`.\`gender\``, - `having ${funcName}(\`person\`.\`id\`) >= ?`, + `group by \`person\`.\`children\``, + `having ${funcName}(\`person\`.\`children\`) >= ?`, + ], + parameters: [3], + }, + mssql: { + sql: [ + `select "person"."children"`, + `from "person"`, + `group by "person"."children"`, + `having ${funcName}("person"."children") >= @1`, ], parameters: [3], }, - mssql: NOT_SUPPORTED, sqlite: { sql: [ - `select *`, + `select "person"."children"`, `from "person"`, - `group by "person"."gender"`, - `having ${funcName}("person"."id") >= ?`, + `group by "person"."children"`, + `having ${funcName}("person"."children") >= ?`, ], parameters: [3], }, }) + + await query.execute() }) it(`should execute a query with ${funcName}(column) in order by clause`, async () => { const query = ctx.db .selectFrom('person') - .selectAll() - .groupBy(['person.gender']) - .orderBy(func('person.id'), 'desc') + .select(['person.children']) + .groupBy(['person.children']) + .orderBy(func('person.children'), 'desc') testSql(query, dialect, { postgres: { sql: [ - `select *`, + `select "person"."children"`, `from "person"`, - `group by "person"."gender"`, - `order by ${funcName}("person"."id") desc`, + `group by "person"."children"`, + `order by ${funcName}("person"."children") desc`, ], parameters: [], }, mysql: { sql: [ - `select *`, + `select \`person\`.\`children\``, `from \`person\``, - `group by \`person\`.\`gender\``, - `order by ${funcName}(\`person\`.\`id\`) desc`, + `group by \`person\`.\`children\``, + `order by ${funcName}(\`person\`.\`children\`) desc`, + ], + parameters: [], + }, + mssql: { + sql: [ + `select "person"."children"`, + `from "person"`, + `group by "person"."children"`, + `order by ${funcName}("person"."children") desc`, ], parameters: [], }, - mssql: NOT_SUPPORTED, sqlite: { sql: [ - `select *`, + `select "person"."children"`, `from "person"`, - `group by "person"."gender"`, - `order by ${funcName}("person"."id") desc`, + `group by "person"."children"`, + `order by ${funcName}("person"."children") desc`, ], parameters: [], }, }) + + await query.execute() }) it(`should execute a query with ${funcName}(column) and a dynamic reference in select clause`, async () => { @@ -465,7 +539,14 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}("person"."id") as "${funcName}",`, + `${funcName}("person"."id") as "another_${funcName}"`, + `from "person"`, + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}("person"."id") as "${funcName}",`, @@ -475,84 +556,106 @@ for (const dialect of DIALECTS) { parameters: [], }, }) + + await query.execute() }) it(`should execute a query with ${funcName}(column) and a dynamic reference in having clause`, async () => { const query = ctx.db .selectFrom('person') - .selectAll() - .groupBy(['person.gender']) - .having(func(ctx.db.dynamic.ref('person.id')), '>=', 3) + .select(['person.children']) + .groupBy(['person.children']) + .having(func(ctx.db.dynamic.ref('person.children')), '>=', 3) testSql(query, dialect, { postgres: { sql: [ - `select *`, + `select "person"."children"`, `from "person"`, - `group by "person"."gender"`, - `having ${funcName}("person"."id") >= $1`, + `group by "person"."children"`, + `having ${funcName}("person"."children") >= $1`, ], parameters: [3], }, mysql: { sql: [ - `select *`, + `select \`person\`.\`children\``, `from \`person\``, - `group by \`person\`.\`gender\``, - `having ${funcName}(\`person\`.\`id\`) >= ?`, + `group by \`person\`.\`children\``, + `having ${funcName}(\`person\`.\`children\`) >= ?`, + ], + parameters: [3], + }, + mssql: { + sql: [ + `select "person"."children"`, + `from "person"`, + `group by "person"."children"`, + `having ${funcName}("person"."children") >= @1`, ], parameters: [3], }, - mssql: NOT_SUPPORTED, sqlite: { sql: [ - `select *`, + `select "person"."children"`, `from "person"`, - `group by "person"."gender"`, - `having ${funcName}("person"."id") >= ?`, + `group by "person"."children"`, + `having ${funcName}("person"."children") >= ?`, ], parameters: [3], }, }) + + await query.execute() }) it(`should execute a query with ${funcName}(column) and a dynamic reference in order by clause`, async () => { const query = ctx.db .selectFrom('person') - .selectAll() - .groupBy(['person.gender']) - .orderBy(func(ctx.db.dynamic.ref('person.id')), 'desc') + .select(['person.children']) + .groupBy(['person.children']) + .orderBy(func(ctx.db.dynamic.ref('person.children')), 'desc') testSql(query, dialect, { postgres: { sql: [ - `select *`, + `select "person"."children"`, `from "person"`, - `group by "person"."gender"`, - `order by ${funcName}("person"."id") desc`, + `group by "person"."children"`, + `order by ${funcName}("person"."children") desc`, ], parameters: [], }, mysql: { sql: [ - `select *`, + `select \`person\`.\`children\``, `from \`person\``, - `group by \`person\`.\`gender\``, - `order by ${funcName}(\`person\`.\`id\`) desc`, + `group by \`person\`.\`children\``, + `order by ${funcName}(\`person\`.\`children\`) desc`, + ], + parameters: [], + }, + mssql: { + sql: [ + `select "person"."children"`, + `from "person"`, + `group by "person"."children"`, + `order by ${funcName}("person"."children") desc`, ], parameters: [], }, - mssql: NOT_SUPPORTED, sqlite: { sql: [ - `select *`, + `select "person"."children"`, `from "person"`, - `group by "person"."gender"`, - `order by ${funcName}("person"."id") desc`, + `group by "person"."children"`, + `order by ${funcName}("person"."children") desc`, ], parameters: [], }, }) + + await query.execute() }) if (dialect === 'postgres' || dialect === 'sqlite') { @@ -783,7 +886,14 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}(*) as "${funcName}",`, + `${funcName}(*) as "another_${funcName}"`, + 'from "person"', + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}(*) as "${funcName}",`, @@ -853,7 +963,14 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + `select ${funcName}(*) over() as "${funcName}",`, + `${funcName}(*) over() as "another_${funcName}"`, + 'from "person"', + ], + parameters: [], + }, sqlite: { sql: [ `select ${funcName}(*) over() as "${funcName}",`, @@ -916,15 +1033,25 @@ for (const dialect of DIALECTS) { it('should execute "dynamic" aggregate functions', async () => { const query = ctx.db .selectFrom('person') + .$if(dialect === 'mssql', (qb) => qb.groupBy('person.first_name')) .select([ - ctx.db.fn.agg('rank').over().as('rank'), - (eb) => eb.fn.agg('rank').over().as('another_rank'), + ctx.db.fn + .agg('rank') + .over((ob) => (dialect === 'mssql' ? ob.orderBy('first_name') : ob)) + .as('rank'), + (eb) => + eb.fn + .agg('rank') + .over((ob) => + dialect === 'mssql' ? ob.orderBy('first_name') : ob + ) + .as('another_rank'), ]) - .$if(dialect === 'postgres', (qb) => + .$if(dialect === 'postgres' || dialect === 'mssql', (qb) => qb.select((eb) => eb.fn .agg('string_agg', ['first_name', sql.lit(',')]) - .distinct() + .$call((eb) => (dialect === 'mssql' ? eb : eb.distinct())) .as('first_names') ) ) @@ -956,7 +1083,16 @@ for (const dialect of DIALECTS) { ], parameters: [], }, - mssql: NOT_SUPPORTED, + mssql: { + sql: [ + 'select rank() over(order by "first_name") as "rank",', + 'rank() over(order by "first_name") as "another_rank",', + `string_agg("first_name", ',') as "first_names"`, + 'from "person"', + 'group by "person"."first_name"', + ], + parameters: [], + }, sqlite: { sql: [ 'select rank() over() as "rank",', diff --git a/test/node/src/array.test.ts b/test/node/src/array.test.ts index 64df251c5..ea444101a 100644 --- a/test/node/src/array.test.ts +++ b/test/node/src/array.test.ts @@ -1,10 +1,4 @@ -import { - Generated, - Kysely, - RawBuilder, - sql, - ParseJSONResultsPlugin, -} from '../../..' +import { Generated, Kysely, sql } from '../../..' import { destroyTest, @@ -14,8 +8,8 @@ import { Database, insertDefaultDataSet, clearDatabase, - DIALECTS, Person, + DIALECTS_WITH_MSSQL, } from './test-setup.js' interface PersonWithArrays extends Person { @@ -24,10 +18,8 @@ interface PersonWithArrays extends Person { nicknames: string[] | null } -for (const dialect of DIALECTS) { - if (dialect !== 'postgres') { - continue - } +if (DIALECTS_WITH_MSSQL.includes('postgres')) { + const dialect = 'postgres' describe(`${dialect} array tests`, () => { let ctx: TestContext diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 56d3c14ca..87e634d23 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -45,6 +45,7 @@ export interface Person { last_name: string | null gender: 'male' | 'female' | 'other' marital_status: 'single' | 'married' | 'divorced' | 'widowed' | null + children: Generated } export interface Pet { @@ -296,6 +297,7 @@ async function createDatabase( .addColumn('last_name', 'varchar(255)') .addColumn('gender', 'varchar(50)', (col) => col.notNull()) .addColumn('marital_status', 'varchar(50)') + .addColumn('children', 'integer', (col) => col.notNull().defaultTo(0)) .execute() await createTableWithId(db.schema, dialect, 'pet') From a69f3addc82f8c131b4f9762065053c08fab0eda Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 03:00:41 +0300 Subject: [PATCH 47/65] lift only. --- test/node/src/aggregate-function.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node/src/aggregate-function.test.ts b/test/node/src/aggregate-function.test.ts index 8f612ea42..b9b05aed5 100644 --- a/test/node/src/aggregate-function.test.ts +++ b/test/node/src/aggregate-function.test.ts @@ -17,7 +17,7 @@ import { const funcNames = ['avg', 'count', 'max', 'min', 'sum'] as const for (const dialect of DIALECTS_WITH_MSSQL) { - describe.only(`${dialect}: aggregate functions`, () => { + describe(`${dialect}: aggregate functions`, () => { let ctx: TestContext before(async function () { From 40b228d6be3a908cd5d9bdbaf0996a7605103ce5 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 15:31:02 +0300 Subject: [PATCH 48/65] connection validate shouldn't trigger kysely stuff. --- src/dialect/mssql/mssql-driver.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index da5d74546..abd6c2253 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -52,15 +52,7 @@ export class MssqlDriver implements Driver { await connection[PRIVATE_DESTROY_METHOD]() }, // @ts-ignore - validate: async (connection) => { - try { - await connection.executeQuery(CompiledQuery.raw('select 1')) - - return true - } catch (err) { - return false - } - }, + validate: (connection) => connection.validate(), }) } @@ -193,6 +185,18 @@ class MssqlConnection implements DatabaseConnection { // } } + validate(): Promise { + return new Promise((resolve) => + this.#connection.execSql( + this.#createTediousRequest( + CompiledQuery.raw('select 1'), + () => resolve(false), + () => resolve(true) + ) + ) + ) + } + #createTediousRequest( compiledQuery: CompiledQuery, reject: (reason?: any) => void, From 62ef70ee163c712347dd80b7ab45a0ba13008fcf Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 15:31:23 +0300 Subject: [PATCH 49/65] introduce mssql to transaction test suite, pt. 1. --- test/node/src/transaction.test.ts | 181 ++++++++++++++++++++---------- 1 file changed, 120 insertions(+), 61 deletions(-) diff --git a/test/node/src/transaction.test.ts b/test/node/src/transaction.test.ts index 15b3c33a8..b275bb168 100644 --- a/test/node/src/transaction.test.ts +++ b/test/node/src/transaction.test.ts @@ -1,7 +1,8 @@ -import { CompiledQuery, Transaction } from '../../../' +import * as sinon from 'sinon' +import { Connection, ISOLATION_LEVEL } from 'tedious' +import { CompiledQuery, IsolationLevel, Transaction } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, @@ -9,12 +10,26 @@ import { expect, Database, insertDefaultDataSet, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { - describe(`${dialect}: transaction`, () => { +for (const dialect of DIALECTS_WITH_MSSQL) { + describe.only(`${dialect}: transaction`, () => { let ctx: TestContext - let executedQueries: CompiledQuery[] = [] + const executedQueries: CompiledQuery[] = [] + const sandbox = sinon.createSandbox() + let tediousBeginTransactionSpy: sinon.SinonSpy< + Parameters, + ReturnType + > + let tediousCommitTransactionSpy: sinon.SinonSpy< + Parameters, + ReturnType + > + let tediousRollbackTransactionSpy: sinon.SinonSpy< + Parameters, + ReturnType + > before(async function () { ctx = await initTest(this, dialect, (event) => { @@ -26,73 +41,117 @@ for (const dialect of DIALECTS) { beforeEach(async () => { await insertDefaultDataSet(ctx) - executedQueries = [] + executedQueries.length = 0 + tediousBeginTransactionSpy = sandbox.spy( + Connection.prototype, + 'beginTransaction' + ) + tediousCommitTransactionSpy = sandbox.spy( + Connection.prototype, + 'commitTransaction' + ) + tediousRollbackTransactionSpy = sandbox.spy( + Connection.prototype, + 'rollbackTransaction' + ) }) afterEach(async () => { await clearDatabase(ctx) + sandbox.restore() }) after(async () => { await destroyTest(ctx) }) - if (dialect !== 'sqlite') { - it('should set the transaction isolation level', async () => { - await ctx.db - .transaction() - .setIsolationLevel('serializable') - .execute(async (trx) => { - await trx - .insertInto('person') - .values({ - first_name: 'Foo', - last_name: 'Barson', - gender: 'male', - }) - .execute() - }) + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { + for (const isolationLevel of [ + 'read uncommitted', + 'read committed', + 'repeatable read', + 'serializable', + ...(dialect === 'mssql' ? (['snapshot'] as const) : []), + ] satisfies IsolationLevel[]) { + it(`should set the transaction isolation level as "${isolationLevel}"`, async () => { + await ctx.db + .transaction() + .setIsolationLevel(isolationLevel) + .execute(async (trx) => { + await trx + .insertInto('person') + .values({ + first_name: 'Foo', + last_name: 'Barson', + gender: 'male', + }) + .execute() + }) - if (dialect == 'postgres') { - expect( - executedQueries.map((it) => ({ - sql: it.sql, - parameters: it.parameters, - })) - ).to.eql([ - { - sql: 'start transaction isolation level serializable', - parameters: [], - }, - { - sql: 'insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3)', - parameters: ['Foo', 'Barson', 'male'], - }, - { sql: 'commit', parameters: [] }, - ]) - } else if (dialect === 'mysql') { - expect( - executedQueries.map((it) => ({ - sql: it.sql, - parameters: it.parameters, - })) - ).to.eql([ - { - sql: 'set transaction isolation level serializable', - parameters: [], - }, - { - sql: 'begin', - parameters: [], - }, - { - sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', - parameters: ['Foo', 'Barson', 'male'], - }, - { sql: 'commit', parameters: [] }, - ]) - } - }) + if (dialect == 'postgres') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })) + ).to.eql([ + { + sql: `start transaction isolation level ${isolationLevel}`, + parameters: [], + }, + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values ($1, $2, $3)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'commit', parameters: [] }, + ]) + } else if (dialect === 'mysql') { + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })) + ).to.eql([ + { + sql: `set transaction isolation level ${isolationLevel}`, + parameters: [], + }, + { + sql: 'begin', + parameters: [], + }, + { + sql: 'insert into `person` (`first_name`, `last_name`, `gender`) values (?, ?, ?)', + parameters: ['Foo', 'Barson', 'male'], + }, + { sql: 'commit', parameters: [] }, + ]) + } else if (dialect === 'mssql') { + expect(tediousBeginTransactionSpy.calledOnce).to.be.true + expect(tediousBeginTransactionSpy.getCall(0).args[1]).to.not.be + .undefined + expect(tediousBeginTransactionSpy.getCall(0).args[2]).to.equal( + ISOLATION_LEVEL[ + isolationLevel.replace(' ', '_').toUpperCase() as any + ] + ) + + expect( + executedQueries.map((it) => ({ + sql: it.sql, + parameters: it.parameters, + })) + ).to.eql([ + { + sql: 'insert into "person" ("first_name", "last_name", "gender") values (@1, @2, @3)', + parameters: ['Foo', 'Barson', 'male'], + }, + ]) + + expect(tediousCommitTransactionSpy.calledOnce).to.be.true + } + }) + } } if (dialect === 'postgres') { From fa3494bbc9987c40334dee472e45e3f9dbfa6510 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Fri, 4 Aug 2023 15:40:53 +0300 Subject: [PATCH 50/65] fix introspect tests. --- test/node/src/introspect.test.ts | 22 ++++++++++++++++++++++ test/node/src/transaction.test.ts | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/test/node/src/introspect.test.ts b/test/node/src/introspect.test.ts index 56a8bfe2f..d3a2439fe 100644 --- a/test/node/src/introspect.test.ts +++ b/test/node/src/introspect.test.ts @@ -101,6 +101,14 @@ for (const dialect of DIALECTS) { isAutoIncrementing: false, hasDefaultValue: false, }, + { + name: 'children', + dataType: 'int4', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: true, + }, ], }, { @@ -270,6 +278,13 @@ for (const dialect of DIALECTS) { isAutoIncrementing: false, hasDefaultValue: false, }, + { + name: 'children', + dataType: 'int', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: true, + }, ], }, { @@ -406,6 +421,13 @@ for (const dialect of DIALECTS) { isAutoIncrementing: false, hasDefaultValue: false, }, + { + name: 'children', + dataType: 'INTEGER', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: true, + }, ], }, { diff --git a/test/node/src/transaction.test.ts b/test/node/src/transaction.test.ts index b275bb168..a0f4130e4 100644 --- a/test/node/src/transaction.test.ts +++ b/test/node/src/transaction.test.ts @@ -14,7 +14,7 @@ import { } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { - describe.only(`${dialect}: transaction`, () => { + describe(`${dialect}: transaction`, () => { let ctx: TestContext const executedQueries: CompiledQuery[] = [] const sandbox = sinon.createSandbox() From 772f6a2bd853a357208811e3948bc5612e865fd3 Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Fri, 4 Aug 2023 22:01:34 +0300 Subject: [PATCH 51/65] remove mssql types. --- package-lock.json | 87 ----------------------------------------------- package.json | 1 - 2 files changed, 88 deletions(-) diff --git a/package-lock.json b/package-lock.json index b9d8b94b3..69881a5d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "@types/chai-as-promised": "^7.1.5", "@types/chai-subset": "^1.3.3", "@types/mocha": "^10.0.1", - "@types/mssql": "^8.1.2", "@types/node": "^18.15.11", "@types/pg": "^8.10.2", "@types/pg-cursor": "^2.7.0", @@ -603,17 +602,6 @@ "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, - "node_modules/@types/mssql": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-8.1.2.tgz", - "integrity": "sha512-hoDM+mZUClfXu0J1pyVdbhv2Ve0dl0TdagAE3M5rd1slqoVEEHuNObPD+giwtJgyo99CcS58qbF9ektVKdxSfQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/tedious": "*", - "tarn": "^3.0.1" - } - }, "node_modules/@types/node": { "version": "18.16.16", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.16.tgz", @@ -662,15 +650,6 @@ "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", "dev": true }, - "node_modules/@types/tedious": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.9.tgz", - "integrity": "sha512-ipwFvfy9b2m0gjHsIX0D6NAAwGCKokzf5zJqUZHUGt+7uWVlBIy6n2eyMgiKQ8ChLFVxic/zwQUhjLYNzbHDRA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -2569,12 +2548,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, "node_modules/jsonwebtoken": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", @@ -4064,18 +4037,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/shiki": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.2.tgz", - "integrity": "sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A==", - "dev": true, - "dependencies": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -4677,42 +4638,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedoc": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.24.8.tgz", - "integrity": "sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==", - "dev": true, - "dependencies": { - "lunr": "^2.3.9", - "marked": "^4.3.0", - "minimatch": "^9.0.0", - "shiki": "^0.14.1" - }, - "bin": { - "typedoc": "bin/typedoc" - }, - "engines": { - "node": ">= 14.14" - }, - "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x" - } - }, - "node_modules/typedoc/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/typescript": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", @@ -4766,18 +4691,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "node_modules/vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index d137ec858..e99322908 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "@types/chai-as-promised": "^7.1.5", "@types/chai-subset": "^1.3.3", "@types/mocha": "^10.0.1", - "@types/mssql": "^8.1.2", "@types/node": "^18.15.11", "@types/pg": "^8.10.2", "@types/pg-cursor": "^2.7.0", From 66d765550d1d9a556de228da29297783b543a8be Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Fri, 4 Aug 2023 23:41:53 +0300 Subject: [PATCH 52/65] add @types/tedious dev dep. --- package-lock.json | 10 ++++++++++ package.json | 1 + 2 files changed, 11 insertions(+) diff --git a/package-lock.json b/package-lock.json index 69881a5d0..c7397574e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@types/pg": "^8.10.2", "@types/pg-cursor": "^2.7.0", "@types/sinon": "^10.0.15", + "@types/tedious": "^4.0.9", "better-sqlite3": "^8.4.0", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", @@ -650,6 +651,15 @@ "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", "dev": true }, + "node_modules/@types/tedious": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.9.tgz", + "integrity": "sha512-ipwFvfy9b2m0gjHsIX0D6NAAwGCKokzf5zJqUZHUGt+7uWVlBIy6n2eyMgiKQ8ChLFVxic/zwQUhjLYNzbHDRA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", diff --git a/package.json b/package.json index e99322908..fb4cf837e 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@types/pg": "^8.10.2", "@types/pg-cursor": "^2.7.0", "@types/sinon": "^10.0.15", + "@types/tedious": "^4.0.9", "better-sqlite3": "^8.4.0", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", From 00df597b5790af536e7b257e6b849c9020698886 Mon Sep 17 00:00:00 2001 From: Igal Klebanov Date: Fri, 4 Aug 2023 23:46:32 +0300 Subject: [PATCH 53/65] introduce mssql to transaction test suite, pt. 2. --- src/dialect/mssql/mssql-driver.ts | 3 ++- test/node/src/transaction.test.ts | 36 ++++++++++++++++--------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index abd6c2253..6b5ee8b20 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -23,6 +23,7 @@ import { import { MssqlDialectConfig, Tedious } from './mssql-dialect-config.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' import { extendStackTrace } from '../../util/stack-trace-utils.js' +import { randomString } from '../../util/random-string.js' const PRIVATE_RELEASE_METHOD = Symbol() const PRIVATE_DESTROY_METHOD = Symbol() @@ -107,7 +108,7 @@ class MssqlConnection implements DatabaseConnection { if (error) reject(error) else resolve(undefined) }, - isolationLevel ? Date.now().toString() : undefined, + isolationLevel ? randomString(8) : undefined, isolationLevel ? this.#getTediousIsolationLevel(isolationLevel) : undefined diff --git a/test/node/src/transaction.test.ts b/test/node/src/transaction.test.ts index a0f4130e4..e04af1a50 100644 --- a/test/node/src/transaction.test.ts +++ b/test/node/src/transaction.test.ts @@ -26,10 +26,6 @@ for (const dialect of DIALECTS_WITH_MSSQL) { Parameters, ReturnType > - let tediousRollbackTransactionSpy: sinon.SinonSpy< - Parameters, - ReturnType - > before(async function () { ctx = await initTest(this, dialect, (event) => { @@ -50,10 +46,6 @@ for (const dialect of DIALECTS_WITH_MSSQL) { Connection.prototype, 'commitTransaction' ) - tediousRollbackTransactionSpy = sandbox.spy( - Connection.prototype, - 'rollbackTransaction' - ) }) afterEach(async () => { @@ -231,15 +223,25 @@ for (const dialect of DIALECTS_WITH_MSSQL) { trx: Transaction, id: number ): Promise { - await trx - .insertInto('person') - .values({ - id: id, - first_name: `Person ${id}`, - last_name: null, - gender: 'other', - }) - .execute() + const query = trx.insertInto('person').values({ + id: id, + first_name: `Person ${id}`, + last_name: null, + gender: 'other', + }) + + if (dialect === 'mssql') { + const compiledQuery = query.compile() + + await trx.executeQuery( + CompiledQuery.raw( + `set identity_insert "person" on; ${compiledQuery.sql}; set identity_insert "person" off;`, + [...compiledQuery.parameters] + ) + ) + } else { + await query.execute() + } } async function doesPersonExists(id: number): Promise { From 989de4ac616ea305db685e56c23799051cfd13e6 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 5 Aug 2023 23:29:14 +0300 Subject: [PATCH 54/65] introduce mssql to introspect test suite, pt. 1. --- src/dialect/mssql/mssql-introspector.ts | 14 +++++-- test/node/src/introspect.test.ts | 49 ++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/dialect/mssql/mssql-introspector.ts b/src/dialect/mssql/mssql-introspector.ts index 96a202bd2..564d9972c 100644 --- a/src/dialect/mssql/mssql-introspector.ts +++ b/src/dialect/mssql/mssql-introspector.ts @@ -8,14 +8,14 @@ import { } from '../database-introspector.js' export class MssqlIntrospector implements DatabaseIntrospector { - readonly #db: Kysely + readonly #db: Kysely constructor(db: Kysely) { this.#db = db } async getSchemas(): Promise { - throw new Error('Not implemented') + return await this.#db.selectFrom('sys.schemas').select('name').execute() } async getTables( @@ -27,6 +27,14 @@ export class MssqlIntrospector implements DatabaseIntrospector { async getMetadata( options?: DatabaseMetadataOptions | undefined ): Promise { - throw new Error('Not implemented') + return { + tables: await this.getTables(options), + } + } +} + +interface MssqlSysTables { + 'sys.schemas': { + name: string } } diff --git a/test/node/src/introspect.test.ts b/test/node/src/introspect.test.ts index d3a2439fe..546fad888 100644 --- a/test/node/src/introspect.test.ts +++ b/test/node/src/introspect.test.ts @@ -1,22 +1,22 @@ import { sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, TestContext, expect, insertDefaultDataSet, + DIALECTS_WITH_MSSQL, } from './test-setup.js' -for (const dialect of DIALECTS) { - describe(`${dialect}: introspect`, () => { +for (const dialect of DIALECTS_WITH_MSSQL) { + describe.only(`${dialect}: introspect`, () => { let ctx: TestContext before(async function () { ctx = await initTest(this, dialect) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { await dropSchema() await createSchema() } @@ -42,6 +42,40 @@ for (const dialect of DIALECTS) { await destroyTest(ctx) }) + describe('getSchemas', () => { + it('should get schema names', async () => { + const schemas = await ctx.db.introspection.getSchemas() + + if (dialect === 'postgres') { + expect(schemas).to.containSubset([ + { name: 'public' }, + { name: 'information_schema' }, + { name: 'pg_catalog' }, + { name: 'some_schema' }, + { name: 'dtype_schema' }, + ]) + } else if (dialect === 'mysql') { + expect(schemas).to.containSubset([ + { name: 'mysql' }, + { name: 'information_schema' }, + { name: 'performance_schema' }, + { name: 'sys' }, + { name: 'kysely_test' }, + ]) + } else if (dialect === 'mssql') { + expect(schemas).to.containSubset([ + { name: 'dbo' }, + { name: 'sys' }, + { name: 'guest' }, + { name: 'INFORMATION_SCHEMA' }, + { name: 'some_schema' }, + ]) + } else if (dialect === 'sqlite') { + expect(schemas).to.eql([]) + } + }) + }) + it('should get table metadata', async () => { const meta = await ctx.db.introspection.getTables() @@ -544,7 +578,12 @@ for (const dialect of DIALECTS) { } else { await ctx.db.schema .createTable('some_schema.pet') - .addColumn('some_column', 'serial', (col) => col.primaryKey()) + .addColumn('some_column', 'integer', (col) => + col + .notNull() + .modifyFront(sql`identity(1,1)`) + .primaryKey() + ) .execute() } } From aa8fdb99c6a01790e45ddd5159c0b3cc79c21fff Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 6 Aug 2023 02:26:09 +0300 Subject: [PATCH 55/65] introduce mssql to introspect test suite, pt. 2. --- src/dialect/mssql/mssql-introspector.ts | 264 +++++++++++++++++++++++- test/node/src/introspect.test.ts | 185 +++++++++++++++++ 2 files changed, 446 insertions(+), 3 deletions(-) diff --git a/src/dialect/mssql/mssql-introspector.ts b/src/dialect/mssql/mssql-introspector.ts index 564d9972c..b54c50cbd 100644 --- a/src/dialect/mssql/mssql-introspector.ts +++ b/src/dialect/mssql/mssql-introspector.ts @@ -6,6 +6,11 @@ import { SchemaMetadata, TableMetadata, } from '../database-introspector.js' +import { + DEFAULT_MIGRATION_LOCK_TABLE, + DEFAULT_MIGRATION_TABLE, +} from '../../migration/migrator.js' +import { freeze } from '../../util/object-utils.js' export class MssqlIntrospector implements DatabaseIntrospector { readonly #db: Kysely @@ -19,13 +24,132 @@ export class MssqlIntrospector implements DatabaseIntrospector { } async getTables( - options?: DatabaseMetadataOptions | undefined + options: DatabaseMetadataOptions = { withInternalKyselyTables: false } ): Promise { - throw new Error('Not implemented') + const rawColumns = await this.#db + .selectFrom('sys.tables as tables') + .leftJoin( + 'sys.schemas as table_schemas', + 'table_schemas.schema_id', + 'tables.schema_id' + ) + .innerJoin( + 'sys.columns as columns', + 'columns.object_id', + 'tables.object_id' + ) + .innerJoin( + 'sys.types as types', + 'types.system_type_id', + 'columns.system_type_id' + ) + .leftJoin( + 'sys.schemas as type_schemas', + 'type_schemas.schema_id', + 'types.schema_id' + ) + .$if(!options.withInternalKyselyTables, (qb) => + qb + .where('tables.name', '!=', DEFAULT_MIGRATION_TABLE) + .where('tables.name', '!=', DEFAULT_MIGRATION_LOCK_TABLE) + ) + .select([ + 'tables.name as table_name', + (eb) => + eb + .ref('tables.type') + .$castTo< + | MssqlSysTables['sys.tables']['type'] + | MssqlSysTables['sys.views']['type'] + >() + .as('table_type'), + 'table_schemas.name as table_schema_name', + 'columns.generated_always_type_desc as column_generated_always_type', + 'columns.is_computed as column_is_computed', + 'columns.is_identity as column_is_identity', + 'columns.is_nullable as column_is_nullable', + 'columns.is_rowguidcol as column_is_rowguidcol', + 'columns.name as column_name', + 'types.is_nullable as type_is_nullable', + 'types.name as type_name', + 'type_schemas.name as type_schema_name', + ]) + .unionAll( + this.#db + .selectFrom('sys.views as views') + .leftJoin( + 'sys.schemas as view_schemas', + 'view_schemas.schema_id', + 'views.schema_id' + ) + .innerJoin( + 'sys.columns as columns', + 'columns.object_id', + 'views.object_id' + ) + .innerJoin( + 'sys.types as types', + 'types.system_type_id', + 'columns.system_type_id' + ) + .leftJoin( + 'sys.schemas as type_schemas', + 'type_schemas.schema_id', + 'types.schema_id' + ) + .select([ + 'views.name as table_name', + 'views.type as table_type', + 'view_schemas.name as table_schema_name', + 'columns.generated_always_type_desc as column_generated_always_type', + 'columns.is_computed as column_is_computed', + 'columns.is_identity as column_is_identity', + 'columns.is_nullable as column_is_nullable', + 'columns.is_rowguidcol as column_is_rowguidcol', + 'columns.name as column_name', + 'types.is_nullable as type_is_nullable', + 'types.name as type_name', + 'type_schemas.name as type_schema_name', + ]) + ) + .orderBy('table_schema_name') + .orderBy('table_name') + .orderBy('column_name') + .execute() + + const tableDictionary: Record = {} + + for (const rawColumn of rawColumns) { + const key = `${rawColumn.table_schema_name}.${rawColumn.table_name}` + + const table = (tableDictionary[key] ||= freeze({ + columns: [], + isView: rawColumn.table_type === 'V ', + name: rawColumn.table_name, + schema: rawColumn.table_schema_name ?? undefined, + })) + + table.columns.push( + freeze({ + dataType: rawColumn.type_name, + dataTypeSchema: rawColumn.type_schema_name ?? undefined, + hasDefaultValue: + rawColumn.column_generated_always_type !== 'NOT_APPLICABLE' || + rawColumn.column_is_computed || + rawColumn.column_is_rowguidcol, + isAutoIncrementing: rawColumn.column_is_identity, + isNullable: + rawColumn.column_is_nullable && rawColumn.type_is_nullable, + name: rawColumn.column_name, + }) + ) + } + + return Object.values(tableDictionary) } async getMetadata( - options?: DatabaseMetadataOptions | undefined + options?: DatabaseMetadataOptions ): Promise { return { tables: await this.getTables(options), @@ -34,7 +158,141 @@ export class MssqlIntrospector implements DatabaseIntrospector { } interface MssqlSysTables { + 'sys.columns': { + // collation_name: string | null + // column_encryption_key_database_name: null + // column_encryption_key_id: null + column_id: number + // default_object_id: number + // encryption_algorithm_name: null + // encryption_type: null + // encryption_type_desc: null + // generated_always_type: number + generated_always_type_desc: string + // graph_type: number + // graph_type_desc: string + // is_ansi_padded: boolean + // is_column_set: boolean + is_computed: boolean + // is_data_deletion_filter_column: boolean + // is_dropped_ledger_column: boolean + // is_dts_replicated: boolean + // is_filestream: boolean + // is_hidden: boolean + is_identity: boolean + // is_masked: boolean + // is_merge_published: boolean + // is_non_sql_subscribed: boolean + is_nullable: boolean + // is_replicated: boolean + is_rowguidcol: boolean + // is_sparse: boolean + // is_xml_document: boolean + // ledger_view_column_type: null + // ledger_view_column_type_desc: null + // max_length: number + name: string + object_id: number + // precision: number + // rule_object_id: number + // scale: number + system_type_id: number + } 'sys.schemas': { name: string + // principal_id: number + schema_id: number + } + 'sys.tables': { + // create_date: Date + // data_retention_period: number + // data_retention_period_unit: number + // data_retention_period_unit_desc: string + // durability: number + // durability_desc: string + // filestream_data_space_id: number | null + // has_replication_filter: boolean + // has_unchecked_assembly_data: boolean + // history_retention_period: null + // history_retention_period_unit: null + // history_retention_period_unit_desc: null + // history_table_id: null + // is_dropped_ledger_table: boolean + // is_edge: boolean + // is_external: boolean + // is_filetable: boolean + // is_memory_optimized: boolean + // is_merge_published: boolean + // is_ms_shipped: boolean + // is_node: boolean + // is_published: boolean + // is_remote_data_archive_enabled: boolean + // is_replicated: boolean + // is_schema_published: boolean + // is_sync_tran_subscribed: boolean + // is_tracked_by_cdc: boolean + // large_value_types_out_of_row: boolean + // ledger_type: number + // ledger_type_desc: string + // ledger_view_id: null + // lob_data_space_id: number + // lock_escalation: number + // lock_escalation_desc: string + // lock_on_bulk_load: boolean + // max_column_id_used: number + // modify_date: Date + name: string + object_id: number + // parent_object_id: number + // principal_id: number | null + schema_id: number + // temporal_type: number + // temporal_type_desc: string + // text_in_row_limit: number + type: 'U ' + // type_desc: 'USER_TABLE' + // uses_ansi_nulls: boolean + } + 'sys.types': { + // collation_name: string | null + // default_object_id: number + // is_assembly_type: boolean + is_nullable: boolean + // is_table_type: boolean + // is_user_defined: boolean + // max_length: number + name: string + // precision: number + // principal_id: number | null + // rule_object_id: number + // scale: number + schema_id: number + system_type_id: number + // user_type_id: number + } + 'sys.views': { + // create_date: Date + // has_opaque_metadata: boolean + // has_replication_filter: boolean + // has_snapshot: boolean + // has_unchecked_assembly_data: boolean + // is_date_correlation_view: boolean + // is_dropped_ledger_view: boolean + // is_msh_shipped: boolean + // is_published: boolean + // is_replicated: boolean + // is_schema_published: boolean + // is_tracked_by_cdc: boolean + // ledger_view_type: number + // ledger_view_type_desc: string + // modify_date: Date + name: string + object_id: number + // parent_object_id: number + // principal_id: number | null + schema_id: number + type: 'V ' + // type_desc: 'VIEW' + // with_check_option: boolean } } diff --git a/test/node/src/introspect.test.ts b/test/node/src/introspect.test.ts index 546fad888..9ae0a6a40 100644 --- a/test/node/src/introspect.test.ts +++ b/test/node/src/introspect.test.ts @@ -406,6 +406,188 @@ for (const dialect of DIALECTS_WITH_MSSQL) { ], }, ]) + } else if (dialect === 'mssql') { + expect(meta).to.eql([ + { + isView: false, + name: 'person', + schema: 'dbo', + columns: [ + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: true, + isAutoIncrementing: false, + isNullable: false, + name: 'children', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'first_name', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'gender', + }, + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: true, + isNullable: false, + name: 'id', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'last_name', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'marital_status', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'middle_name', + }, + ], + }, + { + isView: false, + name: 'pet', + schema: 'dbo', + columns: [ + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: true, + isNullable: false, + name: 'id', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'name', + }, + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'owner_id', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'species', + }, + ], + }, + { + isView: false, + name: 'toy', + schema: 'dbo', + columns: [ + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: true, + isNullable: false, + name: 'id', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'name', + }, + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'pet_id', + }, + { + dataType: 'float', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'price', + }, + ], + }, + { + isView: true, + name: 'toy_names', + schema: 'dbo', + columns: [ + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'name', + }, + ], + }, + { + isView: false, + name: 'pet', + schema: 'some_schema', + columns: [ + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: true, + isNullable: false, + name: 'some_column', + }, + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: true, + isAutoIncrementing: false, + isNullable: true, + name: 'some_column_plus_1', + }, + ], + }, + ]) } else if (dialect === 'sqlite') { expect(meta).to.eql([ { @@ -584,6 +766,9 @@ for (const dialect of DIALECTS_WITH_MSSQL) { .modifyFront(sql`identity(1,1)`) .primaryKey() ) + .addColumn('some_column_plus_1', sql``, (col) => + col.modifyEnd(sql`as (some_column + 1)`) + ) .execute() } } From 0b52cb0f9421eb3f68bdb6161ca3f73f8520df9f Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 6 Aug 2023 02:36:08 +0300 Subject: [PATCH 56/65] fix default to column introspection @ mssql. --- src/dialect/mssql/mssql-introspector.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dialect/mssql/mssql-introspector.ts b/src/dialect/mssql/mssql-introspector.ts index b54c50cbd..49c723682 100644 --- a/src/dialect/mssql/mssql-introspector.ts +++ b/src/dialect/mssql/mssql-introspector.ts @@ -64,6 +64,7 @@ export class MssqlIntrospector implements DatabaseIntrospector { >() .as('table_type'), 'table_schemas.name as table_schema_name', + 'columns.default_object_id as column_default_object_id', 'columns.generated_always_type_desc as column_generated_always_type', 'columns.is_computed as column_is_computed', 'columns.is_identity as column_is_identity', @@ -101,6 +102,7 @@ export class MssqlIntrospector implements DatabaseIntrospector { 'views.name as table_name', 'views.type as table_type', 'view_schemas.name as table_schema_name', + 'columns.default_object_id as column_default_object_id', 'columns.generated_always_type_desc as column_generated_always_type', 'columns.is_computed as column_is_computed', 'columns.is_identity as column_is_identity', @@ -134,6 +136,7 @@ export class MssqlIntrospector implements DatabaseIntrospector { dataType: rawColumn.type_name, dataTypeSchema: rawColumn.type_schema_name ?? undefined, hasDefaultValue: + rawColumn.column_default_object_id > 0 || rawColumn.column_generated_always_type !== 'NOT_APPLICABLE' || rawColumn.column_is_computed || rawColumn.column_is_rowguidcol, @@ -163,7 +166,7 @@ interface MssqlSysTables { // column_encryption_key_database_name: null // column_encryption_key_id: null column_id: number - // default_object_id: number + default_object_id: number // encryption_algorithm_name: null // encryption_type: null // encryption_type_desc: null From efb029772fc25e3f24aecda7c3ff528b52302db1 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 6 Aug 2023 23:20:24 +0300 Subject: [PATCH 57/65] autoincrementing columns are technically columns with default values. --- src/dialect/mssql/mssql-introspector.ts | 1 + test/node/src/introspect.test.ts | 1302 ++++++++++++----------- 2 files changed, 653 insertions(+), 650 deletions(-) diff --git a/src/dialect/mssql/mssql-introspector.ts b/src/dialect/mssql/mssql-introspector.ts index 49c723682..1367cf8dd 100644 --- a/src/dialect/mssql/mssql-introspector.ts +++ b/src/dialect/mssql/mssql-introspector.ts @@ -138,6 +138,7 @@ export class MssqlIntrospector implements DatabaseIntrospector { hasDefaultValue: rawColumn.column_default_object_id > 0 || rawColumn.column_generated_always_type !== 'NOT_APPLICABLE' || + rawColumn.column_is_identity || rawColumn.column_is_computed || rawColumn.column_is_rowguidcol, isAutoIncrementing: rawColumn.column_is_identity, diff --git a/test/node/src/introspect.test.ts b/test/node/src/introspect.test.ts index 9ae0a6a40..36d14188c 100644 --- a/test/node/src/introspect.test.ts +++ b/test/node/src/introspect.test.ts @@ -10,7 +10,7 @@ import { } from './test-setup.js' for (const dialect of DIALECTS_WITH_MSSQL) { - describe.only(`${dialect}: introspect`, () => { + describe(`${dialect}: introspect`, () => { let ctx: TestContext before(async function () { @@ -76,659 +76,661 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) }) - it('should get table metadata', async () => { - const meta = await ctx.db.introspection.getTables() + describe('getTables', () => { + it('should get table metadata', async () => { + const meta = await ctx.db.introspection.getTables() - if (dialect === 'postgres') { - expect(meta).to.eql([ - { - name: 'person', - isView: false, - schema: 'public', - columns: [ - { - name: 'id', - dataType: 'int4', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: true, - hasDefaultValue: true, - }, - { - name: 'first_name', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'middle_name', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, + if (dialect === 'postgres') { + expect(meta).to.eql([ + { + name: 'person', + isView: false, + schema: 'public', + columns: [ + { + name: 'id', + dataType: 'int4', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: true, + hasDefaultValue: true, + }, + { + name: 'first_name', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'middle_name', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, - { - name: 'last_name', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'gender', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'marital_status', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'children', - dataType: 'int4', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: true, - }, - ], - }, - { - name: 'pet', - isView: false, - schema: 'public', - columns: [ - { - name: 'id', - dataType: 'int4', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: true, - hasDefaultValue: true, - }, - { - name: 'name', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'owner_id', - dataType: 'int4', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'species', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - ], - }, - { - name: 'toy', - isView: false, - schema: 'public', - columns: [ - { - name: 'id', - dataType: 'int4', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: true, - hasDefaultValue: true, - }, - { - name: 'name', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'pet_id', - dataType: 'int4', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'price', - dataType: 'float8', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - ], - }, - { - name: 'toy_names', - isView: true, - schema: 'public', - columns: [ - { - name: 'name', - dataType: 'varchar', - dataTypeSchema: 'pg_catalog', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - ], - }, - { - name: 'pet', - isView: false, - schema: 'some_schema', - columns: [ - { - name: 'some_column', - dataType: 'int4', - dataTypeSchema: 'pg_catalog', - isNullable: false, - isAutoIncrementing: true, - hasDefaultValue: true, - }, - { - dataType: 'species', - dataTypeSchema: 'dtype_schema', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: true, - name: 'spcies', - }, - ], - }, - ]) - } else if (dialect === 'mysql') { - expect(meta).to.eql([ - { - name: 'person', - isView: false, - schema: 'kysely_test', - columns: [ - { - name: 'id', - dataType: 'int', - isNullable: false, - isAutoIncrementing: true, - hasDefaultValue: false, - }, - { - name: 'first_name', - dataType: 'varchar', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'middle_name', - dataType: 'varchar', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'last_name', - dataType: 'varchar', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, + { + name: 'last_name', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'gender', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'marital_status', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'children', + dataType: 'int4', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: true, + }, + ], + }, + { + name: 'pet', + isView: false, + schema: 'public', + columns: [ + { + name: 'id', + dataType: 'int4', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: true, + hasDefaultValue: true, + }, + { + name: 'name', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'owner_id', + dataType: 'int4', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'species', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + ], + }, + { + name: 'toy', + isView: false, + schema: 'public', + columns: [ + { + name: 'id', + dataType: 'int4', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: true, + hasDefaultValue: true, + }, + { + name: 'name', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'pet_id', + dataType: 'int4', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'price', + dataType: 'float8', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + ], + }, + { + name: 'toy_names', + isView: true, + schema: 'public', + columns: [ + { + name: 'name', + dataType: 'varchar', + dataTypeSchema: 'pg_catalog', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + ], + }, + { + name: 'pet', + isView: false, + schema: 'some_schema', + columns: [ + { + name: 'some_column', + dataType: 'int4', + dataTypeSchema: 'pg_catalog', + isNullable: false, + isAutoIncrementing: true, + hasDefaultValue: true, + }, + { + dataType: 'species', + dataTypeSchema: 'dtype_schema', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'spcies', + }, + ], + }, + ]) + } else if (dialect === 'mysql') { + expect(meta).to.eql([ + { + name: 'person', + isView: false, + schema: 'kysely_test', + columns: [ + { + name: 'id', + dataType: 'int', + isNullable: false, + isAutoIncrementing: true, + hasDefaultValue: false, + }, + { + name: 'first_name', + dataType: 'varchar', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'middle_name', + dataType: 'varchar', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'last_name', + dataType: 'varchar', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, - { - name: 'gender', - dataType: 'varchar', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'marital_status', - dataType: 'varchar', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'children', - dataType: 'int', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: true, - }, - ], - }, - { - name: 'pet', - isView: false, - schema: 'kysely_test', - columns: [ - { - name: 'id', - dataType: 'int', - isNullable: false, - isAutoIncrementing: true, - hasDefaultValue: false, - }, - { - name: 'name', - dataType: 'varchar', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'owner_id', - dataType: 'int', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'species', - dataType: 'varchar', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - ], - }, - { - name: 'toy', - isView: false, - schema: 'kysely_test', - columns: [ - { - name: 'id', - dataType: 'int', - isNullable: false, - isAutoIncrementing: true, - hasDefaultValue: false, - }, - { - name: 'name', - dataType: 'varchar', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'pet_id', - dataType: 'int', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'price', - dataType: 'double', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - ], - }, - { - name: 'toy_names', - isView: true, - schema: 'kysely_test', - columns: [ - { - dataType: 'varchar', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'name', - }, - ], - }, - ]) - } else if (dialect === 'mssql') { - expect(meta).to.eql([ - { - isView: false, - name: 'person', - schema: 'dbo', - columns: [ - { - dataType: 'int', - dataTypeSchema: 'sys', - hasDefaultValue: true, - isAutoIncrementing: false, - isNullable: false, - name: 'children', - }, - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: true, - name: 'first_name', - }, - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'gender', - }, - { - dataType: 'int', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: true, - isNullable: false, - name: 'id', - }, - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: true, - name: 'last_name', - }, - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: true, - name: 'marital_status', - }, - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: true, - name: 'middle_name', - }, - ], - }, - { - isView: false, - name: 'pet', - schema: 'dbo', - columns: [ - { - dataType: 'int', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: true, - isNullable: false, - name: 'id', - }, - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'name', - }, - { - dataType: 'int', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'owner_id', - }, - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'species', - }, - ], - }, - { - isView: false, - name: 'toy', - schema: 'dbo', - columns: [ - { - dataType: 'int', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: true, - isNullable: false, - name: 'id', - }, - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'name', - }, - { - dataType: 'int', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'pet_id', - }, - { - dataType: 'float', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'price', - }, - ], - }, - { - isView: true, - name: 'toy_names', - schema: 'dbo', - columns: [ - { - dataType: 'varchar', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: false, - name: 'name', - }, - ], - }, - { - isView: false, - name: 'pet', - schema: 'some_schema', - columns: [ - { - dataType: 'int', - dataTypeSchema: 'sys', - hasDefaultValue: false, - isAutoIncrementing: true, - isNullable: false, - name: 'some_column', - }, - { - dataType: 'int', - dataTypeSchema: 'sys', - hasDefaultValue: true, - isAutoIncrementing: false, - isNullable: true, - name: 'some_column_plus_1', - }, - ], - }, - ]) - } else if (dialect === 'sqlite') { - expect(meta).to.eql([ - { - name: 'person', - isView: false, - columns: [ - { - name: 'id', - dataType: 'INTEGER', - isNullable: true, - isAutoIncrementing: true, - hasDefaultValue: false, - }, - { - name: 'first_name', - dataType: 'varchar(255)', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'middle_name', - dataType: 'varchar(255)', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'last_name', - dataType: 'varchar(255)', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, + { + name: 'gender', + dataType: 'varchar', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'marital_status', + dataType: 'varchar', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'children', + dataType: 'int', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: true, + }, + ], + }, + { + name: 'pet', + isView: false, + schema: 'kysely_test', + columns: [ + { + name: 'id', + dataType: 'int', + isNullable: false, + isAutoIncrementing: true, + hasDefaultValue: false, + }, + { + name: 'name', + dataType: 'varchar', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'owner_id', + dataType: 'int', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'species', + dataType: 'varchar', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + ], + }, + { + name: 'toy', + isView: false, + schema: 'kysely_test', + columns: [ + { + name: 'id', + dataType: 'int', + isNullable: false, + isAutoIncrementing: true, + hasDefaultValue: false, + }, + { + name: 'name', + dataType: 'varchar', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'pet_id', + dataType: 'int', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'price', + dataType: 'double', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + ], + }, + { + name: 'toy_names', + isView: true, + schema: 'kysely_test', + columns: [ + { + dataType: 'varchar', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'name', + }, + ], + }, + ]) + } else if (dialect === 'mssql') { + expect(meta).to.eql([ + { + isView: false, + name: 'person', + schema: 'dbo', + columns: [ + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: true, + isAutoIncrementing: false, + isNullable: false, + name: 'children', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'first_name', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'gender', + }, + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: true, + isAutoIncrementing: true, + isNullable: false, + name: 'id', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'last_name', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'marital_status', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'middle_name', + }, + ], + }, + { + isView: false, + name: 'pet', + schema: 'dbo', + columns: [ + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: true, + isAutoIncrementing: true, + isNullable: false, + name: 'id', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'name', + }, + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'owner_id', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'species', + }, + ], + }, + { + isView: false, + name: 'toy', + schema: 'dbo', + columns: [ + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: true, + isAutoIncrementing: true, + isNullable: false, + name: 'id', + }, + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'name', + }, + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'pet_id', + }, + { + dataType: 'float', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'price', + }, + ], + }, + { + isView: true, + name: 'toy_names', + schema: 'dbo', + columns: [ + { + dataType: 'varchar', + dataTypeSchema: 'sys', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: false, + name: 'name', + }, + ], + }, + { + isView: false, + name: 'pet', + schema: 'some_schema', + columns: [ + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: true, + isAutoIncrementing: true, + isNullable: false, + name: 'some_column', + }, + { + dataType: 'int', + dataTypeSchema: 'sys', + hasDefaultValue: true, + isAutoIncrementing: false, + isNullable: true, + name: 'some_column_plus_1', + }, + ], + }, + ]) + } else if (dialect === 'sqlite') { + expect(meta).to.eql([ + { + name: 'person', + isView: false, + columns: [ + { + name: 'id', + dataType: 'INTEGER', + isNullable: true, + isAutoIncrementing: true, + hasDefaultValue: false, + }, + { + name: 'first_name', + dataType: 'varchar(255)', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'middle_name', + dataType: 'varchar(255)', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'last_name', + dataType: 'varchar(255)', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, - { - name: 'gender', - dataType: 'varchar(50)', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'marital_status', - dataType: 'varchar(50)', - isNullable: true, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'children', - dataType: 'INTEGER', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: true, - }, - ], - }, - { - name: 'pet', - isView: false, - columns: [ - { - name: 'id', - dataType: 'INTEGER', - isNullable: true, - isAutoIncrementing: true, - hasDefaultValue: false, - }, - { - name: 'name', - dataType: 'varchar(255)', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'owner_id', - dataType: 'INTEGER', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'species', - dataType: 'varchar(50)', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - ], - }, - { - name: 'toy', - isView: false, - columns: [ - { - name: 'id', - dataType: 'INTEGER', - isNullable: true, - isAutoIncrementing: true, - hasDefaultValue: false, - }, - { - name: 'name', - dataType: 'varchar(255)', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'pet_id', - dataType: 'INTEGER', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - { - name: 'price', - dataType: 'double precision', - isNullable: false, - isAutoIncrementing: false, - hasDefaultValue: false, - }, - ], - }, - { - name: 'toy_names', - isView: true, - columns: [ - { - dataType: 'varchar(255)', - hasDefaultValue: false, - isAutoIncrementing: false, - isNullable: true, - name: 'name', - }, - ], - }, - ]) - } + { + name: 'gender', + dataType: 'varchar(50)', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'marital_status', + dataType: 'varchar(50)', + isNullable: true, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'children', + dataType: 'INTEGER', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: true, + }, + ], + }, + { + name: 'pet', + isView: false, + columns: [ + { + name: 'id', + dataType: 'INTEGER', + isNullable: true, + isAutoIncrementing: true, + hasDefaultValue: false, + }, + { + name: 'name', + dataType: 'varchar(255)', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'owner_id', + dataType: 'INTEGER', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'species', + dataType: 'varchar(50)', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + ], + }, + { + name: 'toy', + isView: false, + columns: [ + { + name: 'id', + dataType: 'INTEGER', + isNullable: true, + isAutoIncrementing: true, + hasDefaultValue: false, + }, + { + name: 'name', + dataType: 'varchar(255)', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'pet_id', + dataType: 'INTEGER', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + { + name: 'price', + dataType: 'double precision', + isNullable: false, + isAutoIncrementing: false, + hasDefaultValue: false, + }, + ], + }, + { + name: 'toy_names', + isView: true, + columns: [ + { + dataType: 'varchar(255)', + hasDefaultValue: false, + isAutoIncrementing: false, + isNullable: true, + name: 'name', + }, + ], + }, + ]) + } + }) }) async function createView() { From 9489d697a47ee3a68fc17aeaae8fe73da9972408 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 7 Aug 2023 00:22:56 +0300 Subject: [PATCH 58/65] dont use ifNotExists in mssql migrations. --- src/dialect/dialect-adapter-base.ts | 4 ++ src/dialect/dialect-adapter.ts | 8 ++++ src/dialect/mssql/mssql-adapter.ts | 6 ++- src/dialect/sqlite/sqlite-adapter.ts | 2 +- src/migration/migrator.ts | 68 ++++++++++++++++------------ 5 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/dialect/dialect-adapter-base.ts b/src/dialect/dialect-adapter-base.ts index 556795aeb..1bb22fb73 100644 --- a/src/dialect/dialect-adapter-base.ts +++ b/src/dialect/dialect-adapter-base.ts @@ -8,6 +8,10 @@ import { DialectAdapter, MigrationLockOptions } from './dialect-adapter.js' * they are added and there will be less breaking changes. */ export abstract class DialectAdapterBase implements DialectAdapter { + get supportsCreateIfNotExists(): boolean { + return true + } + get supportsTransactionalDdl(): boolean { return false } diff --git a/src/dialect/dialect-adapter.ts b/src/dialect/dialect-adapter.ts index 2aede8b1f..d1ddfe2bd 100644 --- a/src/dialect/dialect-adapter.ts +++ b/src/dialect/dialect-adapter.ts @@ -9,6 +9,14 @@ import { Kysely } from '../kysely.js' * it. For that there's a `supportsTransactionalDdl` boolean in this interface. */ export interface DialectAdapter { + /** + * Whether or not this dialect supports `if not exists` in creation of tables/schemas/views/etc. + * + * If this is false, tables and schemas are created without `if not exists` in + * migrations. This is not a problem if the dialect supports transactional DDL. + */ + readonly supportsCreateIfNotExists: boolean + /** * Whether or not this dialect supports transactional DDL. * diff --git a/src/dialect/mssql/mssql-adapter.ts b/src/dialect/mssql/mssql-adapter.ts index 2d186ef6f..da2a01a1d 100644 --- a/src/dialect/mssql/mssql-adapter.ts +++ b/src/dialect/mssql/mssql-adapter.ts @@ -4,6 +4,10 @@ import { sql } from '../../raw-builder/sql.js' import { DialectAdapterBase } from '../dialect-adapter-base.js' export class MssqlAdapter extends DialectAdapterBase { + get supportsCreateIfNotExists(): boolean { + return false + } + get supportsTransactionalDdl(): boolean { return true } @@ -21,8 +25,6 @@ export class MssqlAdapter extends DialectAdapterBase { )}, @Resource = ${sql.lit(DEFAULT_MIGRATION_TABLE)}, @LockMode = ${sql.lit( 'Exclusive' )}`.execute(db) - - console.log('sp_getapplock result', result) } async releaseMigrationLock(): Promise { diff --git a/src/dialect/sqlite/sqlite-adapter.ts b/src/dialect/sqlite/sqlite-adapter.ts index 74e4ba777..df13331ae 100644 --- a/src/dialect/sqlite/sqlite-adapter.ts +++ b/src/dialect/sqlite/sqlite-adapter.ts @@ -2,7 +2,7 @@ import { Kysely } from '../../kysely.js' import { DialectAdapterBase } from '../dialect-adapter-base.js' import { MigrationLockOptions } from '../dialect-adapter.js' -export class SqliteAdapter implements DialectAdapterBase { +export class SqliteAdapter extends DialectAdapterBase { get supportsTransactionalDdl(): boolean { return false } diff --git a/src/migration/migrator.ts b/src/migration/migrator.ts index eb9f6e79a..1fa1a3fcd 100644 --- a/src/migration/migrator.ts +++ b/src/migration/migrator.ts @@ -3,6 +3,8 @@ import { Kysely } from '../kysely.js' import { KyselyPlugin } from '../plugin/kysely-plugin.js' import { NoopPlugin } from '../plugin/noop-plugin.js' import { WithSchemaPlugin } from '../plugin/with-schema/with-schema-plugin.js' +import { CreateSchemaBuilder } from '../schema/create-schema-builder.js' +import { CreateTableBuilder } from '../schema/create-table-builder.js' import { freeze, getLast } from '../util/object-utils.js' export const DEFAULT_MIGRATION_TABLE = 'kysely_migration' @@ -265,10 +267,9 @@ export class Migrator { if (!(await this.#doesSchemaExists())) { try { - await this.#props.db.schema - .createSchema(this.#migrationTableSchema) - .ifNotExists() - .execute() + await this.#createIfNotExists( + this.#props.db.schema.createSchema(this.#migrationTableSchema) + ) } catch (error) { // At least on PostgreSQL, `if not exists` doesn't guarantee the `create schema` // query doesn't throw if the schema already exits. That's why we check if @@ -284,23 +285,22 @@ export class Migrator { if (!(await this.#doesTableExists(this.#migrationTable))) { try { if (this.#migrationTableSchema) { - await this.#props.db.schema - .createSchema(this.#migrationTableSchema) - .ifNotExists() - .execute() + await this.#createIfNotExists( + this.#props.db.schema.createSchema(this.#migrationTableSchema) + ) } - await this.#props.db.schema - .withPlugin(this.#schemaPlugin) - .createTable(this.#migrationTable) - .ifNotExists() - .addColumn('name', 'varchar(255)', (col) => - col.notNull().primaryKey() - ) - // The migration run time as ISO string. This is not a real date type as we - // can't know which data type is supported by all future dialects. - .addColumn('timestamp', 'varchar(255)', (col) => col.notNull()) - .execute() + await this.#createIfNotExists( + this.#props.db.schema + .withPlugin(this.#schemaPlugin) + .createTable(this.#migrationTable) + .addColumn('name', 'varchar(255)', (col) => + col.notNull().primaryKey() + ) + // The migration run time as ISO string. This is not a real date type as we + // can't know which data type is supported by all future dialects. + .addColumn('timestamp', 'varchar(255)', (col) => col.notNull()) + ) } catch (error) { // At least on PostgreSQL, `if not exists` doesn't guarantee the `create table` // query doesn't throw if the table already exits. That's why we check if @@ -315,15 +315,17 @@ export class Migrator { async #ensureMigrationLockTableExists(): Promise { if (!(await this.#doesTableExists(this.#migrationLockTable))) { try { - await this.#props.db.schema - .withPlugin(this.#schemaPlugin) - .createTable(this.#migrationLockTable) - .ifNotExists() - .addColumn('id', 'varchar(255)', (col) => col.notNull().primaryKey()) - .addColumn('is_locked', 'integer', (col) => - col.notNull().defaultTo(0) - ) - .execute() + await this.#createIfNotExists( + this.#props.db.schema + .withPlugin(this.#schemaPlugin) + .createTable(this.#migrationLockTable) + .addColumn('id', 'varchar(255)', (col) => + col.notNull().primaryKey() + ) + .addColumn('is_locked', 'integer', (col) => + col.notNull().defaultTo(0) + ) + ) } catch (error) { // At least on PostgreSQL, `if not exists` doesn't guarantee the `create table` // query doesn't throw if the table already exits. That's why we check if @@ -593,6 +595,16 @@ export class Migrator { return { results } } + + async #createIfNotExists( + qb: CreateTableBuilder | CreateSchemaBuilder + ): Promise { + if (this.#props.db.getExecutor().adapter.supportsCreateIfNotExists) { + qb = qb.ifNotExists() + } + + await qb.execute() + } } export interface MigratorProps { From f221a98ca3a70b0c297b274828730ffbbf2e372c Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Mon, 7 Aug 2023 00:23:29 +0300 Subject: [PATCH 59/65] introduce mssql to migration test suite. --- test/node/src/migration.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/node/src/migration.test.ts b/test/node/src/migration.test.ts index 739ef3b11..16dc559b0 100644 --- a/test/node/src/migration.test.ts +++ b/test/node/src/migration.test.ts @@ -13,19 +13,19 @@ import { } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, expect, initTest, TestContext, + DIALECTS_WITH_MSSQL, } from './test-setup.js' const CUSTOM_MIGRATION_SCHEMA = 'migrate' const CUSTOM_MIGRATION_TABLE = 'custom_migrations' const CUSTOM_MIGRATION_LOCK_TABLE = 'custom_migrations_lock' -for (const dialect of DIALECTS) { +for (const dialect of DIALECTS_WITH_MSSQL) { describe(`${dialect}: migration`, () => { let ctx: TestContext From a07213b03b341fcbb8d3d343f7d675537ad5836c Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Thu, 10 Aug 2023 02:55:33 +0300 Subject: [PATCH 60/65] implement streaming. --- src/dialect/mssql/mssql-driver.ts | 116 ++++++++++++++++++------------ test/node/src/select.test.ts | 5 +- 2 files changed, 74 insertions(+), 47 deletions(-) diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index 6b5ee8b20..6af478ca3 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -132,7 +132,7 @@ class MssqlConnection implements DatabaseConnection { rowCount: number }>((resolve, reject) => this.#connection.execSql( - this.#createTediousRequest(compiledQuery, reject, resolve) + this.#createTediousRequest(compiledQuery, { reject, resolve }) ) ) @@ -162,73 +162,65 @@ class MssqlConnection implements DatabaseConnection { throw new Error('chunkSize must be a positive integer') } - // const request = this.#createMssqlRequest(this.#request, compiledQuery) - // request.stream = true + const request = this.#createTediousRequest(compiledQuery) - // const cursor = new MssqlCursor(request) + this.#connection.execSql(request) - // try { - // request.query(compiledQuery.sql) + try { + while (true) { + const rows = await request.readChunk(chunkSize) - // while (true) { - // const rows = await cursor.read(chunkSize) + if (rows.length === 0) { + break + } - // if (rows.length === 0) { - // break - // } + yield { rows } - // yield { - // rows: rows, - // } - // } - // } finally { - // request.cancel() - // } + if (rows.length < chunkSize) { + break + } + } + } finally { + this.#connection.cancel() + } } validate(): Promise { return new Promise((resolve) => this.#connection.execSql( - this.#createTediousRequest( - CompiledQuery.raw('select 1'), - () => resolve(false), - () => resolve(true) - ) + this.#createTediousRequest(CompiledQuery.raw('select 1'), { + reject: () => resolve(false), + resolve: () => resolve(true), + }) ) ) } #createTediousRequest( compiledQuery: CompiledQuery, - reject: (reason?: any) => void, - resolve: (value: any) => void - ): Request { - const { parameters, query, sql } = compiledQuery + promise?: { + reject: (reason?: any) => void + resolve: (value: any) => void + } + ): Request & { + readChunk: (chunkSize: number) => Promise + } { + const { parameters, sql } = compiledQuery let promisedRowCount: number | undefined + let error: Error | any[] | undefined const rows: Record[] = [] const request = new this.#tedious.Request(sql, (err, rowCount) => { if (err) { - if (err instanceof AggregateError) { - reject(err.errors) - } else { - reject(err) - } + error = err instanceof AggregateError ? err.errors : err + promise?.reject(error) } else { promisedRowCount = rowCount } }) - for (let i = 0; i < parameters.length; i++) { - const parameter = parameters[i] - - request.addParameter( - String(i + 1), - this.#getTediousDataType(parameter), - parameter - ) - } + this.#addParametersToTediousRequest(request, parameters) const rowListener = (columns: ColumnValue[]) => { const row: Record = {} @@ -242,15 +234,51 @@ class MssqlConnection implements DatabaseConnection { request.on('row', rowListener) + let requestCompleted = false + request.once('requestCompleted', () => { + requestCompleted = true request.off('row', rowListener) - resolve({ - rows, + + promise?.resolve({ rowCount: promisedRowCount, + rows, }) }) - return request + return Object.defineProperty(request, 'readChunk', { + configurable: false, + enumerable: false, + value: (chunkSize: number) => { + return new Promise((resolve, reject) => { + const interval = setInterval(() => { + if (error) { + clearInterval(interval) + reject(error) + } else if (requestCompleted || rows.length >= chunkSize) { + clearInterval(interval) + resolve(rows.splice(0, chunkSize)) + } + }, 0) + }) + }, + writable: false, + }) as any + } + + #addParametersToTediousRequest( + request: Request, + parameters: readonly unknown[] + ): void { + for (let i = 0; i < parameters.length; i++) { + const parameter = parameters[i] + + request.addParameter( + String(i + 1), + this.#getTediousDataType(parameter), + parameter + ) + } } #getTediousDataType(value: unknown): any { diff --git a/test/node/src/select.test.ts b/test/node/src/select.test.ts index 5b895205d..c3b41dacc 100644 --- a/test/node/src/select.test.ts +++ b/test/node/src/select.test.ts @@ -879,8 +879,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) } - // TODO: introduce mssql - if (dialect === 'mysql' || dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql') { it('should stream results', async () => { const males: unknown[] = [] @@ -910,7 +909,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { ]) }) - if (dialect === 'postgres') { + if (dialect === 'postgres' || dialect === 'mssql') { it('should stream results with a specific chunk size', async () => { const males: unknown[] = [] From ff422f2d5de54296cadbea4ce5ae48eba7ef22f2 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 12 Aug 2023 02:09:13 +0300 Subject: [PATCH 61/65] fix schema tests. --- test/node/src/schema.test.ts | 47 ++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/test/node/src/schema.test.ts b/test/node/src/schema.test.ts index 88f3de237..08450dc55 100644 --- a/test/node/src/schema.test.ts +++ b/test/node/src/schema.test.ts @@ -2876,29 +2876,52 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) }) } - + if ( dialect === 'postgres' || dialect === 'mysql' || dialect === 'mssql' ) { describe('add primary key constraint', async () => { + beforeEach(() => { + return ctx.db.schema + .alterTable('test') + .addColumn('decimal_col', 'decimal', (cb) => cb.notNull()) + .addColumn('smallint_col', sql`smallint`, (cb) => cb.notNull()) + .execute() + }) + + afterEach(async () => { + if (dialect === 'mssql') { + await ctx.db.schema + .alterTable('test') + .dropConstraint('test_pkey') + .execute() + } + + await ctx.db.schema + .alterTable('test') + .dropColumn('decimal_col') + .dropColumn('smallint_col') + .execute() + }) + it('should add a primary key constraint', async () => { const builder = ctx.db.schema .alterTable('test') - .addPrimaryKeyConstraint('test_pkey', ['integer_col']) + .addPrimaryKeyConstraint('test_pkey', ['decimal_col']) testSql(builder, dialect, { postgres: { - sql: 'alter table "test" add constraint "test_pkey" primary key ("integer_col")', + sql: 'alter table "test" add constraint "test_pkey" primary key ("decimal_col")', parameters: [], }, mysql: { - sql: 'alter table `test` add constraint `test_pkey` primary key (`integer_col`)', + sql: 'alter table `test` add constraint `test_pkey` primary key (`decimal_col`)', parameters: [], }, mssql: { - sql: 'alter table "test" add constraint "test_pkey" primary key ("integer_col")', + sql: 'alter table "test" add constraint "test_pkey" primary key ("decimal_col")', parameters: [], }, sqlite: NOT_SUPPORTED, @@ -2911,17 +2934,21 @@ for (const dialect of DIALECTS_WITH_MSSQL) { const builder = ctx.db.schema .alterTable('test') .addPrimaryKeyConstraint('test_pkey', [ - 'integer_col', - 'varchar_col', + 'decimal_col', + 'smallint_col', ]) testSql(builder, dialect, { postgres: { - sql: 'alter table "test" add constraint "test_pkey" primary key ("integer_col", "varchar_col")', + sql: 'alter table "test" add constraint "test_pkey" primary key ("decimal_col", "smallint_col")', parameters: [], }, mysql: { - sql: 'alter table `test` add constraint `test_pkey` primary key (`integer_col`, `varchar_col`)', + sql: 'alter table `test` add constraint `test_pkey` primary key (`decimal_col`, `smallint_col`)', + parameters: [], + }, + mssql: { + sql: 'alter table "test" add constraint "test_pkey" primary key ("decimal_col", "smallint_col")', parameters: [], }, sqlite: NOT_SUPPORTED, @@ -2931,7 +2958,7 @@ for (const dialect of DIALECTS_WITH_MSSQL) { }) }) } - + if ( dialect === 'postgres' || dialect === 'mysql' || From 8bb0c0e6a4a1ad61cdf1a1e458da238e5f5bcc0c Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 12 Aug 2023 02:29:43 +0300 Subject: [PATCH 62/65] rename DIALECTS_WITH_MSSQL back to DIALECTS. --- test/node/src/aggregate-function.test.ts | 4 ++-- test/node/src/array.test.ts | 4 ++-- test/node/src/camel-case.test.ts | 4 ++-- test/node/src/case.test.ts | 4 ++-- test/node/src/clear.test.ts | 4 ++-- test/node/src/coalesce.test.ts | 4 ++-- test/node/src/deduplicate-joins.test.ts | 4 ++-- test/node/src/delete.test.ts | 4 ++-- test/node/src/error-stack.test.ts | 4 ++-- test/node/src/execute.test.ts | 4 ++-- test/node/src/explain.test.ts | 6 ++---- test/node/src/expression.test.ts | 4 ++-- test/node/src/group-by.test.ts | 4 ++-- test/node/src/having.test.ts | 4 ++-- test/node/src/insert.test.ts | 4 ++-- test/node/src/introspect.test.ts | 4 ++-- test/node/src/join.test.ts | 4 ++-- test/node/src/json-traversal.test.ts | 6 ++---- test/node/src/json.test.ts | 6 ++---- test/node/src/migration.test.ts | 4 ++-- test/node/src/order-by.test.ts | 4 ++-- test/node/src/performance.test.ts | 4 ++-- test/node/src/raw-query.test.ts | 4 ++-- test/node/src/raw-sql.test.ts | 4 ++-- test/node/src/replace.test.ts | 4 ++-- test/node/src/sanitize-identifiers.test.ts | 4 ++-- test/node/src/schema.test.ts | 4 ++-- test/node/src/select.test.ts | 4 ++-- test/node/src/set-operation.test.ts | 4 ++-- test/node/src/test-setup.ts | 7 +------ test/node/src/transaction.test.ts | 4 ++-- test/node/src/update.test.ts | 4 ++-- test/node/src/where.test.ts | 4 ++-- test/node/src/with-schema.test.ts | 4 ++-- test/node/src/with.test.ts | 4 ++-- 35 files changed, 69 insertions(+), 80 deletions(-) diff --git a/test/node/src/aggregate-function.test.ts b/test/node/src/aggregate-function.test.ts index b9b05aed5..531d186c4 100644 --- a/test/node/src/aggregate-function.test.ts +++ b/test/node/src/aggregate-function.test.ts @@ -11,12 +11,12 @@ import { NOT_SUPPORTED, TestContext, testSql, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' const funcNames = ['avg', 'count', 'max', 'min', 'sum'] as const -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: aggregate functions`, () => { let ctx: TestContext diff --git a/test/node/src/array.test.ts b/test/node/src/array.test.ts index ea444101a..ab67c885d 100644 --- a/test/node/src/array.test.ts +++ b/test/node/src/array.test.ts @@ -9,7 +9,7 @@ import { insertDefaultDataSet, clearDatabase, Person, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' interface PersonWithArrays extends Person { @@ -18,7 +18,7 @@ interface PersonWithArrays extends Person { nicknames: string[] | null } -if (DIALECTS_WITH_MSSQL.includes('postgres')) { +if (DIALECTS.includes('postgres')) { const dialect = 'postgres' describe(`${dialect} array tests`, () => { diff --git a/test/node/src/camel-case.test.ts b/test/node/src/camel-case.test.ts index 0f16ec632..bc9584b0f 100644 --- a/test/node/src/camel-case.test.ts +++ b/test/node/src/camel-case.test.ts @@ -7,10 +7,10 @@ import { testSql, expect, createTableWithId, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: camel case test`, () => { let ctx: TestContext let camelDb: Kysely diff --git a/test/node/src/case.test.ts b/test/node/src/case.test.ts index 6bf9222a1..bbcf45956 100644 --- a/test/node/src/case.test.ts +++ b/test/node/src/case.test.ts @@ -1,6 +1,6 @@ import { sql } from '../../..' import { - DIALECTS_WITH_MSSQL, + DIALECTS, TestContext, clearDatabase, destroyTest, @@ -9,7 +9,7 @@ import { testSql, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: case`, () => { let ctx: TestContext diff --git a/test/node/src/clear.test.ts b/test/node/src/clear.test.ts index 7dbf1672d..8d1b88c32 100644 --- a/test/node/src/clear.test.ts +++ b/test/node/src/clear.test.ts @@ -4,11 +4,11 @@ import { TestContext, testSql, NOT_SUPPORTED, - DIALECTS_WITH_MSSQL, + DIALECTS, limit, } from './test-setup' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect} clear`, () => { let ctx: TestContext diff --git a/test/node/src/coalesce.test.ts b/test/node/src/coalesce.test.ts index 45ea4c62f..695de79e7 100644 --- a/test/node/src/coalesce.test.ts +++ b/test/node/src/coalesce.test.ts @@ -7,10 +7,10 @@ import { NOT_SUPPORTED, TestContext, testSql, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: coalesce`, () => { let ctx: TestContext diff --git a/test/node/src/deduplicate-joins.test.ts b/test/node/src/deduplicate-joins.test.ts index 6757bf4e1..f1b5ddabd 100644 --- a/test/node/src/deduplicate-joins.test.ts +++ b/test/node/src/deduplicate-joins.test.ts @@ -7,10 +7,10 @@ import { TestContext, testSql, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: deduplicate joins`, () => { let ctx: TestContext diff --git a/test/node/src/delete.test.ts b/test/node/src/delete.test.ts index bd1e5efa2..d01c01608 100644 --- a/test/node/src/delete.test.ts +++ b/test/node/src/delete.test.ts @@ -10,10 +10,10 @@ import { NOT_SUPPORTED, insertDefaultDataSet, DEFAULT_DATA_SET, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: delete`, () => { let ctx: TestContext diff --git a/test/node/src/error-stack.test.ts b/test/node/src/error-stack.test.ts index 9e447c26f..da7a8cc6b 100644 --- a/test/node/src/error-stack.test.ts +++ b/test/node/src/error-stack.test.ts @@ -6,10 +6,10 @@ import { initTest, TestContext, expect, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: error stack`, () => { let ctx: TestContext diff --git a/test/node/src/execute.test.ts b/test/node/src/execute.test.ts index e49170dfe..cd3025685 100644 --- a/test/node/src/execute.test.ts +++ b/test/node/src/execute.test.ts @@ -12,10 +12,10 @@ import { insertPersons, TestContext, expect, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: execute`, () => { let ctx: TestContext diff --git a/test/node/src/explain.test.ts b/test/node/src/explain.test.ts index 8cf9692c2..a482634a2 100644 --- a/test/node/src/explain.test.ts +++ b/test/node/src/explain.test.ts @@ -8,12 +8,10 @@ import { insertDefaultDataSet, NOT_SUPPORTED, TestContext, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL.filter( - (dialect) => dialect !== 'mssql' -)) { +for (const dialect of DIALECTS.filter((dialect) => dialect !== 'mssql')) { describe(`${dialect}: explain test`, () => { let ctx: TestContext const sandbox = createSandbox() diff --git a/test/node/src/expression.test.ts b/test/node/src/expression.test.ts index d4dcf93aa..e7b19d2d8 100644 --- a/test/node/src/expression.test.ts +++ b/test/node/src/expression.test.ts @@ -5,10 +5,10 @@ import { insertDefaultDataSet, TestContext, testSql, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: expressions`, () => { let ctx: TestContext diff --git a/test/node/src/group-by.test.ts b/test/node/src/group-by.test.ts index 112b65310..8b32dddbd 100644 --- a/test/node/src/group-by.test.ts +++ b/test/node/src/group-by.test.ts @@ -9,10 +9,10 @@ import { expect, insertDefaultDataSet, NOT_SUPPORTED, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: group by`, () => { let ctx: TestContext diff --git a/test/node/src/having.test.ts b/test/node/src/having.test.ts index 68982b9ef..8233e27a5 100644 --- a/test/node/src/having.test.ts +++ b/test/node/src/having.test.ts @@ -8,10 +8,10 @@ import { TestContext, testSql, expect, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: having`, () => { let ctx: TestContext diff --git a/test/node/src/insert.test.ts b/test/node/src/insert.test.ts index 187586d3a..ed32f4136 100644 --- a/test/node/src/insert.test.ts +++ b/test/node/src/insert.test.ts @@ -11,11 +11,11 @@ import { Database, NOT_SUPPORTED, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, limit, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: insert`, () => { let ctx: TestContext diff --git a/test/node/src/introspect.test.ts b/test/node/src/introspect.test.ts index 36d14188c..f2fe4e116 100644 --- a/test/node/src/introspect.test.ts +++ b/test/node/src/introspect.test.ts @@ -6,10 +6,10 @@ import { TestContext, expect, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: introspect`, () => { let ctx: TestContext diff --git a/test/node/src/join.test.ts b/test/node/src/join.test.ts index e8ebff3db..c9b44df0b 100644 --- a/test/node/src/join.test.ts +++ b/test/node/src/join.test.ts @@ -9,11 +9,11 @@ import { testSql, expect, NOT_SUPPORTED, - DIALECTS_WITH_MSSQL, + DIALECTS, limit, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: join`, () => { let ctx: TestContext diff --git a/test/node/src/json-traversal.test.ts b/test/node/src/json-traversal.test.ts index ddff9ad87..01b89320e 100644 --- a/test/node/src/json-traversal.test.ts +++ b/test/node/src/json-traversal.test.ts @@ -7,7 +7,7 @@ import { } from '../../..' import { BuiltInDialect, - DIALECTS_WITH_MSSQL, + DIALECTS, NOT_SUPPORTED, clearDatabase, destroyTest, @@ -19,9 +19,7 @@ import { type TestContext = Awaited> -for (const dialect of DIALECTS_WITH_MSSQL.filter( - (dialect) => dialect !== 'mssql' -)) { +for (const dialect of DIALECTS.filter((dialect) => dialect !== 'mssql')) { describe(`${dialect}: json traversal`, () => { let ctx: TestContext diff --git a/test/node/src/json.test.ts b/test/node/src/json.test.ts index 6855a55d7..929c2e8b8 100644 --- a/test/node/src/json.test.ts +++ b/test/node/src/json.test.ts @@ -29,7 +29,7 @@ import { Database, insertDefaultDataSet, clearDatabase, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' interface JsonTable { @@ -66,9 +66,7 @@ const jsonFunctions = { }, } as const -for (const dialect of DIALECTS_WITH_MSSQL.filter( - (dialect) => dialect !== 'mssql' -)) { +for (const dialect of DIALECTS.filter((dialect) => dialect !== 'mssql')) { const { jsonArrayFrom, jsonObjectFrom, jsonBuildObject } = jsonFunctions[dialect] diff --git a/test/node/src/migration.test.ts b/test/node/src/migration.test.ts index 16dc559b0..853e14e7a 100644 --- a/test/node/src/migration.test.ts +++ b/test/node/src/migration.test.ts @@ -18,14 +18,14 @@ import { expect, initTest, TestContext, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' const CUSTOM_MIGRATION_SCHEMA = 'migrate' const CUSTOM_MIGRATION_TABLE = 'custom_migrations' const CUSTOM_MIGRATION_LOCK_TABLE = 'custom_migrations_lock' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: migration`, () => { let ctx: TestContext diff --git a/test/node/src/order-by.test.ts b/test/node/src/order-by.test.ts index 149473c2e..f205c2c44 100644 --- a/test/node/src/order-by.test.ts +++ b/test/node/src/order-by.test.ts @@ -9,10 +9,10 @@ import { expect, NOT_SUPPORTED, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: order by`, () => { let ctx: TestContext diff --git a/test/node/src/performance.test.ts b/test/node/src/performance.test.ts index 50eafff11..db389782b 100644 --- a/test/node/src/performance.test.ts +++ b/test/node/src/performance.test.ts @@ -6,10 +6,10 @@ import { initTest, TestContext, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -if (DIALECTS_WITH_MSSQL.includes('postgres')) { +if (DIALECTS.includes('postgres')) { describe.skip(`query builder performance`, () => { let ctx: TestContext diff --git a/test/node/src/raw-query.test.ts b/test/node/src/raw-query.test.ts index a57e4a202..eef7a3efa 100644 --- a/test/node/src/raw-query.test.ts +++ b/test/node/src/raw-query.test.ts @@ -8,10 +8,10 @@ import { expect, insertDefaultDataSet, testSql, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: raw queries`, () => { let ctx: TestContext diff --git a/test/node/src/raw-sql.test.ts b/test/node/src/raw-sql.test.ts index f0e7cf13d..748e7ed42 100644 --- a/test/node/src/raw-sql.test.ts +++ b/test/node/src/raw-sql.test.ts @@ -9,10 +9,10 @@ import { testSql, NOT_SUPPORTED, expect, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: raw sql`, () => { let ctx: TestContext diff --git a/test/node/src/replace.test.ts b/test/node/src/replace.test.ts index 766f21e38..ddf22330a 100644 --- a/test/node/src/replace.test.ts +++ b/test/node/src/replace.test.ts @@ -11,10 +11,10 @@ import { Database, NOT_SUPPORTED, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -if (DIALECTS_WITH_MSSQL.includes('mysql')) { +if (DIALECTS.includes('mysql')) { const dialect = 'mysql' as const describe(`mysql: replace`, () => { diff --git a/test/node/src/sanitize-identifiers.test.ts b/test/node/src/sanitize-identifiers.test.ts index dbd66672e..24673672f 100644 --- a/test/node/src/sanitize-identifiers.test.ts +++ b/test/node/src/sanitize-identifiers.test.ts @@ -7,10 +7,10 @@ import { Person, testSql, NOT_SUPPORTED, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: sanitize identifiers`, () => { let ctx: TestContext diff --git a/test/node/src/schema.test.ts b/test/node/src/schema.test.ts index 08450dc55..0edbfb58e 100644 --- a/test/node/src/schema.test.ts +++ b/test/node/src/schema.test.ts @@ -9,10 +9,10 @@ import { NOT_SUPPORTED, TestContext, testSql, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: schema`, () => { let ctx: TestContext diff --git a/test/node/src/select.test.ts b/test/node/src/select.test.ts index c3b41dacc..2f9222506 100644 --- a/test/node/src/select.test.ts +++ b/test/node/src/select.test.ts @@ -14,11 +14,11 @@ import { DIALECT_CONFIGS, Database, POOL_SIZE, - DIALECTS_WITH_MSSQL, + DIALECTS, limit, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: select`, () => { let ctx: TestContext diff --git a/test/node/src/set-operation.test.ts b/test/node/src/set-operation.test.ts index fe4da4d79..87b0ed8fa 100644 --- a/test/node/src/set-operation.test.ts +++ b/test/node/src/set-operation.test.ts @@ -7,10 +7,10 @@ import { expect, insertDefaultDataSet, NOT_SUPPORTED, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: set operations`, () => { let ctx: TestContext diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 87e634d23..790de8380 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -87,12 +87,7 @@ export type BuiltInDialect = 'postgres' | 'mysql' | 'mssql' | 'sqlite' export type PerDialect = Record export const DIALECTS: BuiltInDialect[] = ( - ['postgres', 'mysql', 'sqlite'] as const -).filter((d) => !process.env.DIALECT || d === process.env.DIALECT) - -// temporary, to slowly introduce mssql tests file by file. -export const DIALECTS_WITH_MSSQL: BuiltInDialect[] = ( - [...DIALECTS, 'mssql'] as const + ['postgres', 'mysql', 'mssql', 'sqlite'] as const ).filter((d) => !process.env.DIALECT || d === process.env.DIALECT) const TEST_INIT_TIMEOUT = 5 * 60 * 1000 diff --git a/test/node/src/transaction.test.ts b/test/node/src/transaction.test.ts index e04af1a50..656b3033d 100644 --- a/test/node/src/transaction.test.ts +++ b/test/node/src/transaction.test.ts @@ -10,10 +10,10 @@ import { expect, Database, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: transaction`, () => { let ctx: TestContext const executedQueries: CompiledQuery[] = [] diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index 163a6d1d3..7fd41a0ca 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -11,11 +11,11 @@ import { NOT_SUPPORTED, insertDefaultDataSet, DEFAULT_DATA_SET, - DIALECTS_WITH_MSSQL, + DIALECTS, limit, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: update`, () => { let ctx: TestContext diff --git a/test/node/src/where.test.ts b/test/node/src/where.test.ts index c9851be58..199af4b6a 100644 --- a/test/node/src/where.test.ts +++ b/test/node/src/where.test.ts @@ -8,11 +8,11 @@ import { testSql, expect, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, NOT_SUPPORTED, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: where`, () => { let ctx: TestContext diff --git a/test/node/src/with-schema.test.ts b/test/node/src/with-schema.test.ts index 43792d116..e634ed713 100644 --- a/test/node/src/with-schema.test.ts +++ b/test/node/src/with-schema.test.ts @@ -6,12 +6,12 @@ import { testSql, NOT_SUPPORTED, createTableWithId, - DIALECTS_WITH_MSSQL, + DIALECTS, insert, limit, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL.filter( +for (const dialect of DIALECTS.filter( (dialect) => dialect === 'postgres' || dialect === 'mssql' )) { describe(`${dialect}: with schema`, () => { diff --git a/test/node/src/with.test.ts b/test/node/src/with.test.ts index 8787c7e7a..139bec1b4 100644 --- a/test/node/src/with.test.ts +++ b/test/node/src/with.test.ts @@ -9,10 +9,10 @@ import { expect, NOT_SUPPORTED, insertDefaultDataSet, - DIALECTS_WITH_MSSQL, + DIALECTS, } from './test-setup.js' -for (const dialect of DIALECTS_WITH_MSSQL) { +for (const dialect of DIALECTS) { describe(`${dialect}: with`, () => { let ctx: TestContext From a00831be4fb653e4ce89535256020651f03a9d68 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 12 Aug 2023 23:40:28 +0300 Subject: [PATCH 63/65] misc adjustments and js docs. --- src/dialect/mssql/mssql-adapter.ts | 7 +- src/dialect/mssql/mssql-dialect-config.ts | 216 +++++++++++- src/dialect/mssql/mssql-dialect.ts | 39 +++ src/dialect/mssql/mssql-driver.ts | 327 +++++++++---------- src/query-compiler/default-query-compiler.ts | 4 + test/node/src/test-setup.ts | 9 +- test/node/src/update.test.ts | 1 - 7 files changed, 416 insertions(+), 187 deletions(-) diff --git a/src/dialect/mssql/mssql-adapter.ts b/src/dialect/mssql/mssql-adapter.ts index da2a01a1d..48b40f297 100644 --- a/src/dialect/mssql/mssql-adapter.ts +++ b/src/dialect/mssql/mssql-adapter.ts @@ -13,14 +13,15 @@ export class MssqlAdapter extends DialectAdapterBase { } get supportsReturning(): boolean { - // mssql supports returning with the output clause - return true + // mssql should support returning with the `output` clause. + // we need to figure this out when we'll introduce support for it. + return false } async acquireMigrationLock(db: Kysely): Promise { // Acquire a transaction-level exclusive lock on the migrations table. // https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql?view=sql-server-ver16 - const result = await sql`exec sp_getapplock @DbPrincipal = ${sql.lit( + await sql`exec sp_getapplock @DbPrincipal = ${sql.lit( 'dbo' )}, @Resource = ${sql.lit(DEFAULT_MIGRATION_TABLE)}, @LockMode = ${sql.lit( 'Exclusive' diff --git a/src/dialect/mssql/mssql-dialect-config.ts b/src/dialect/mssql/mssql-dialect-config.ts index 5e2bddc66..2e66e43f4 100644 --- a/src/dialect/mssql/mssql-dialect-config.ts +++ b/src/dialect/mssql/mssql-dialect-config.ts @@ -1,22 +1,208 @@ -import { Pool } from 'tarn' -import { Connection, ISOLATION_LEVEL, Request, TYPES } from 'tedious' - export interface MssqlDialectConfig { - connectionFactory: () => Connection | Promise - Tarn: Tarn - Tedious: Tedious + /** + * This dialect uses the `tarn` package to manage the connection pool to your + * database. To use it as a peer dependency and not bundle it with Kysely's code, + * you need to pass the `tarn` package itself. You also need to pass some pool options + * (excluding `create`, `destroy` and `validate` functions which are controlled by this dialect), + * `min` & `max` connections at the very least. + * + * Example: + * + * ```ts + * import * as Tarn from 'tarn' + * + * const dialect = new MssqlDialect({ + * // ... + * tarn: { + * ...Tarn, + * options: { + * // ... + * min: 0, + * max: 10, + * }, + * }, + * }) + * ``` + */ + tarn: Tarn + + /** + * This dialect uses the `tedious` package to communicate with your MS SQL Server + * database. To use it as a peer dependency and not bundle it with Kysely's code, + * you need to pass the `tedious` package itself. You also need to pass a factory + * function that creates new `tedious` `Connection` instances on demand. + * + * Example: + * + * ```ts + * import * as Tedious from 'tedious' + * + * const dialect = new MssqlDialect({ + * // ... + * tedious: { + * ...Tedious, + * connectionFactory: () => new Tedious.Connection({ ... }), + * }, + * }) + * ``` + */ + tedious: Tedious +} + +export interface Tedious { + connectionFactory: () => TediousConnection | Promise + ISOLATION_LEVEL: TediousIsolationLevel + Request: typeof TediousRequest + TYPES: TediousTypes +} + +export type TediousIsolationLevel = Record< + | 'NO_CHANGE' + | 'READ_UNCOMMITTED' + | 'READ_COMMITTED' + | 'REPEATABLE_READ' + | 'SERIALIZABLE' + | 'SNAPSHOT', + number +> + +export type TediousTypes = Record< + | 'BigInt' + | 'Binary' + | 'Bit' + | 'Char' + | 'Date' + | 'DateTime' + | 'DateTime2' + | 'DateTimeOffset' + | 'Decimal' + | 'Float' + | 'Image' + | 'Int' + | 'Money' + | 'NChar' + | 'NText' + | 'Null' + | 'Numeric' + | 'NVarChar' + | 'Real' + | 'SmallDateTime' + | 'SmallInt' + | 'SmallMoney' + | 'Text' + | 'Time' + | 'TinyInt' + | 'TVP' + | 'UDT' + | 'UniqueIdentifier' + | 'VarBinary' + | 'VarChar' + // TODO: uncomment once it is introduced in @types/tedious. See https://github.com/DefinitelyTyped/DefinitelyTyped/pull/66369 + // | 'Variant' + | 'Xml', + { name: string; type: string } +> + +export interface TediousType { + name: string + type: string +} + +export interface TediousConnection { + beginTransaction( + callback: (error?: Error) => void, + transactionId?: string | undefined, + isolationLevel?: number | undefined + ): void + cancel(): void + close(): void + commitTransaction(callback: (error?: Error) => void): void + connect(callback: (error?: Error) => void): void + execSql(request: TediousRequest): void + reset(callback: (error?: Error) => void): void + rollbackTransaction(callback: (error?: Error) => void): void + once(event: 'end', listener: () => void): void +} + +export declare class TediousRequest { + constructor( + sql: string, + callback: (error: Error, rowCount: number, rows: any[]) => void + ) + addParameter( + name: string, + type: TediousType, + value: any, + options?: { + length?: number | 'max' | undefined + precision?: number | undefined + scale?: number | undefined + } + ): void + off(event: 'row', listener: (...args: any[]) => void): void + on(event: 'row', listener: (columns: TediousColumnValue[]) => void): void + once(event: 'requestCompleted', listener: (...args: any[]) => void): void +} + +export interface TediousColumnValue { + metadata: { + colName: string + } + value: any } export interface Tarn { - Pool: typeof Pool - options: Omit< - ConstructorParameters[0], - 'create' | 'destroy' | 'validate' - > + /** + * Tarn.js' pool options, excluding `create`, `destroy` and `validate` functions, + * which must be implemented by this dialect. + */ + options: Omit, 'create' | 'destroy' | 'validate'> + + /** + * Tarn.js' Pool class. + * + * Example: + * + * ```ts + * import { Pool } from 'tarn' + * + * const dialect = new MssqlDialect({ + * // ... + * tarn: { + * // ... + * Pool, + * }, + * }) + * ``` + */ + Pool: typeof TarnPool } -export interface Tedious { - Request: typeof Request - ISOLATION_LEVEL: typeof ISOLATION_LEVEL - TYPES: typeof TYPES +export declare class TarnPool { + constructor(opt: TarnPoolOptions) + acquire(): TarnPendingRequest + destroy(): any + release(resource: R): void +} + +export interface TarnPoolOptions { + acquireTimeoutMillis?: number + create(cb: (err: Error | null, resource: R) => void): any | (() => Promise) + createRetryIntervalMillis?: number + createTimeoutMillis?: number + destroy(resource: R): any + destroyTimeoutMillis?: number + idleTimeoutMillis?: number + log?(msg: string): any + max: number + min: number + propagateCreateError?: boolean + reapIntervalMillis?: number + validate(resource: R): boolean +} + +export interface TarnPendingRequest { + promise: Promise + resolve: (resource: R) => void + reject: (err: Error) => void } diff --git a/src/dialect/mssql/mssql-dialect.ts b/src/dialect/mssql/mssql-dialect.ts index 26f92e284..f9ea61ad1 100644 --- a/src/dialect/mssql/mssql-dialect.ts +++ b/src/dialect/mssql/mssql-dialect.ts @@ -10,6 +10,45 @@ import { MssqlDriver } from './mssql-driver.js' import { MssqlIntrospector } from './mssql-introspector.js' import { MssqlQueryCompiler } from './mssql-query-compiler.js' +/** + * MS SQL Server dialect that uses the [tedious](https://tediousjs.github.io/tedious) + * library. + * + * The constructor takes an instance of {@link MssqlDialectConfig}. + * + * ```ts + * import * as Tedious from 'tedious' + * import * as Tarn from 'tarn' + * + * const dialect = new MssqlDialect({ + * tarn: { + * ...Tarn, + * options: { + * min: 0, + * max: 10, + * }, + * }, + * tedious: { + * ...Tedious, + * connectionFactory: () => new Tedious.Connection({ + * authentication: { + * options: { + * password: 'password', + * userName: 'username', + * }, + * type: 'default', + * }, + * options: { + * database: 'some_db', + * port: 1433, + * trustServerCertificate: true, + * }, + * server: 'localhost', + * }), + * }, + * }) + * ``` + */ export class MssqlDialect implements Dialect { readonly #config: MssqlDialectConfig diff --git a/src/dialect/mssql/mssql-driver.ts b/src/dialect/mssql/mssql-driver.ts index 6af478ca3..7a1d3edf1 100644 --- a/src/dialect/mssql/mssql-driver.ts +++ b/src/dialect/mssql/mssql-driver.ts @@ -1,5 +1,3 @@ -import { Pool } from 'tarn' -import { ColumnValue, Connection, Request } from 'tedious' import { DatabaseConnection, QueryResult, @@ -20,25 +18,33 @@ import { isString, isUndefined, } from '../../util/object-utils.js' -import { MssqlDialectConfig, Tedious } from './mssql-dialect-config.js' +import { + MssqlDialectConfig, + TarnPool, + Tedious, + TediousColumnValue, + TediousConnection, + TediousRequest, +} from './mssql-dialect-config.js' import { CompiledQuery } from '../../query-compiler/compiled-query.js' import { extendStackTrace } from '../../util/stack-trace-utils.js' import { randomString } from '../../util/random-string.js' +import { Deferred } from '../../util/deferred.js' const PRIVATE_RELEASE_METHOD = Symbol() const PRIVATE_DESTROY_METHOD = Symbol() export class MssqlDriver implements Driver { readonly #config: MssqlDialectConfig - readonly #pool: Pool + readonly #pool: TarnPool constructor(config: MssqlDialectConfig) { this.#config = freeze({ ...config }) - this.#pool = new this.#config.Tarn.Pool({ - ...this.#config.Tarn.options, + this.#pool = new this.#config.tarn.Pool({ + ...this.#config.tarn.options, create: async () => { - const connection = await this.#config.connectionFactory() + const connection = await this.#config.tedious.connectionFactory() await new Promise((resolve, reject) => connection.connect((error) => { @@ -47,12 +53,13 @@ export class MssqlDriver implements Driver { }) ) - return new MssqlConnection(connection, this.#config.Tedious) + return new MssqlConnection(connection, this.#config.tedious) }, destroy: async (connection) => { await connection[PRIVATE_DESTROY_METHOD]() }, - // @ts-ignore + // @ts-ignore `tarn` accepts a function that returns a promise here, but + // the types are not aligned and it type errors. validate: (connection) => connection.validate(), }) } @@ -91,10 +98,10 @@ export class MssqlDriver implements Driver { } class MssqlConnection implements DatabaseConnection { - readonly #connection: Connection + readonly #connection: TediousConnection readonly #tedious: Tedious - constructor(connection: Connection, tedious: Tedious) { + constructor(connection: TediousConnection, tedious: Tedious) { this.#connection = connection this.#tedious = tedious } @@ -127,15 +134,18 @@ class MssqlConnection implements DatabaseConnection { async executeQuery(compiledQuery: CompiledQuery): Promise> { try { - const { rowCount, rows } = await new Promise<{ - rows: O[] - rowCount: number - }>((resolve, reject) => - this.#connection.execSql( - this.#createTediousRequest(compiledQuery, { reject, resolve }) - ) + const deferred = new Deferred>() + + const request = new MssqlRequest( + this.#tedious, + compiledQuery, + deferred ) + this.#connection.execSql(request.request) + + const { rowCount, rows } = await deferred.promise + return { numAffectedRows: rowCount !== undefined ? BigInt(rowCount) : undefined, rows, @@ -162,13 +172,13 @@ class MssqlConnection implements DatabaseConnection { throw new Error('chunkSize must be a positive integer') } - const request = this.#createTediousRequest(compiledQuery) + const request = new MssqlRequest(this.#tedious, compiledQuery) - this.#connection.execSql(request) + this.#connection.execSql(request.request) try { while (true) { - const rows = await request.readChunk(chunkSize) + const rows = await request.readChunk(chunkSize) if (rows.length === 0) { break @@ -185,95 +195,134 @@ class MssqlConnection implements DatabaseConnection { } } - validate(): Promise { - return new Promise((resolve) => - this.#connection.execSql( - this.#createTediousRequest(CompiledQuery.raw('select 1'), { - reject: () => resolve(false), - resolve: () => resolve(true), - }) + async validate(): Promise { + try { + const deferred = new Deferred>() + + const request = new MssqlRequest( + this.#tedious, + CompiledQuery.raw('select 1'), + deferred ) - ) + + this.#connection.execSql(request.request) + + await deferred.promise + + return true + } catch { + return false + } } - #createTediousRequest( - compiledQuery: CompiledQuery, - promise?: { - reject: (reason?: any) => void - resolve: (value: any) => void + #getTediousIsolationLevel(isolationLevel: IsolationLevel) { + const { ISOLATION_LEVEL } = this.#tedious + + const mapper: Record< + IsolationLevel, + (typeof ISOLATION_LEVEL)[keyof typeof ISOLATION_LEVEL] + > = { + 'read committed': ISOLATION_LEVEL.READ_COMMITTED, + 'read uncommitted': ISOLATION_LEVEL.READ_UNCOMMITTED, + 'repeatable read': ISOLATION_LEVEL.REPEATABLE_READ, + serializable: ISOLATION_LEVEL.SERIALIZABLE, + snapshot: ISOLATION_LEVEL.SNAPSHOT, } - ): Request & { - readChunk: (chunkSize: number) => Promise - } { - const { parameters, sql } = compiledQuery - let promisedRowCount: number | undefined - let error: Error | any[] | undefined - const rows: Record[] = [] + const tediousIsolationLevel = mapper[isolationLevel] - const request = new this.#tedious.Request(sql, (err, rowCount) => { - if (err) { - error = err instanceof AggregateError ? err.errors : err - promise?.reject(error) - } else { - promisedRowCount = rowCount - } + if (tediousIsolationLevel === undefined) { + throw new Error(`Unknown isolation level: ${isolationLevel}`) + } + + return tediousIsolationLevel + } + + [PRIVATE_RELEASE_METHOD](): Promise { + return new Promise((resolve, reject) => { + this.#connection.reset((error) => { + if (error) reject(error) + else resolve(undefined) + }) }) + } - this.#addParametersToTediousRequest(request, parameters) + [PRIVATE_DESTROY_METHOD](): Promise { + return new Promise((resolve) => { + this.#connection.once('end', () => { + resolve(undefined) + }) - const rowListener = (columns: ColumnValue[]) => { - const row: Record = {} + this.#connection.close() + }) + } +} - for (const column of columns) { - row[column.metadata.colName] = column.value - } +interface OnDone { + rowCount: number | undefined + rows: O[] +} - rows.push(row) - } +interface PlainDeferred { + reject: (reason: any) => void + resolve: (value?: O) => void +} - request.on('row', rowListener) +class MssqlRequest { + readonly #request: TediousRequest + readonly #rows: O[] + readonly #tedious: Tedious + #completed: boolean + #error: Error | any[] | undefined + #rowCount: number | undefined - let requestCompleted = false + constructor( + tedious: Tedious, + compiledQuery: CompiledQuery, + onDone?: Deferred> | PlainDeferred> + ) { + this.#completed = false + this.#rows = [] + this.#tedious = tedious - request.once('requestCompleted', () => { - requestCompleted = true - request.off('row', rowListener) + const { parameters, sql } = compiledQuery - promise?.resolve({ - rowCount: promisedRowCount, - rows, - }) + this.#request = new this.#tedious.Request(sql, (err, rowCount) => { + if (err) { + this.#error = err instanceof AggregateError ? err.errors : err + onDone?.reject(this.#error) + } else { + this.#rowCount = rowCount + } }) - return Object.defineProperty(request, 'readChunk', { - configurable: false, - enumerable: false, - value: (chunkSize: number) => { - return new Promise((resolve, reject) => { - const interval = setInterval(() => { - if (error) { - clearInterval(interval) - reject(error) - } else if (requestCompleted || rows.length >= chunkSize) { - clearInterval(interval) - resolve(rows.splice(0, chunkSize)) - } - }, 0) - }) - }, - writable: false, - }) as any + this.#addParametersToRequest(parameters) + this.#attachListeners(onDone) } - #addParametersToTediousRequest( - request: Request, - parameters: readonly unknown[] - ): void { + get request(): TediousRequest { + return this.#request + } + + readChunk(chunkSize: number): Promise { + return new Promise((resolve, reject) => { + const interval = setInterval(() => { + if (this.#error) { + clearInterval(interval) + reject(this.#error) + } else if (this.#completed || this.#rows.length >= chunkSize) { + clearInterval(interval) + resolve(this.#rows.splice(0, chunkSize)) + } + }, 0) + }) + } + + #addParametersToRequest(parameters: readonly unknown[]): void { for (let i = 0; i < parameters.length; i++) { const parameter = parameters[i] - request.addParameter( + this.#request.addParameter( String(i + 1), this.#getTediousDataType(parameter), parameter @@ -281,6 +330,32 @@ class MssqlConnection implements DatabaseConnection { } } + #attachListeners( + onDone: Deferred> | PlainDeferred> | undefined + ): void { + const rowListener = (columns: TediousColumnValue[]) => { + const row: Record = {} + + for (const column of columns) { + row[column.metadata.colName] = column.value + } + + this.#rows.push(row as O) + } + + this.#request.on('row', rowListener) + + this.#request.once('requestCompleted', () => { + this.#completed = true + this.#request.off('row', rowListener) + + onDone?.resolve({ + rowCount: this.#rowCount, + rows: this.#rows, + }) + }) + } + #getTediousDataType(value: unknown): any { if (isNull(value) || isUndefined(value) || isString(value)) { return this.#tedious.TYPES.NVarChar @@ -312,80 +387,4 @@ class MssqlConnection implements DatabaseConnection { return this.#tedious.TYPES.NVarChar } - - #getTediousIsolationLevel(isolationLevel: IsolationLevel) { - const { ISOLATION_LEVEL } = this.#tedious - - const mapper: Record< - IsolationLevel, - (typeof ISOLATION_LEVEL)[keyof typeof ISOLATION_LEVEL] - > = { - [isolationLevel]: ISOLATION_LEVEL.NO_CHANGE, - 'read committed': ISOLATION_LEVEL.READ_COMMITTED, - 'read uncommitted': ISOLATION_LEVEL.READ_UNCOMMITTED, - 'repeatable read': ISOLATION_LEVEL.REPEATABLE_READ, - serializable: ISOLATION_LEVEL.SERIALIZABLE, - snapshot: ISOLATION_LEVEL.SNAPSHOT, - } - - return mapper[isolationLevel] || undefined - } - - [PRIVATE_RELEASE_METHOD](): Promise { - return new Promise((resolve, reject) => { - this.#connection.reset((error) => { - if (error) reject(error) - else resolve(undefined) - }) - }) - } - - [PRIVATE_DESTROY_METHOD](): Promise { - return new Promise((resolve) => { - this.#connection.once('end', () => { - resolve(undefined) - }) - - this.#connection.close() - }) - } } - -// class MssqlCursor { -// readonly #request: Request -// readonly #chunk: O[] = [] - -// constructor(request: Request) { -// this.#request = request -// } - -// async read(chunkSize: number): Promise { -// if (this.#chunk.length >= chunkSize) { -// return this.#chunk.splice(0, chunkSize) -// } - -// return new Promise((resolve, reject) => { -// const rowListener = (row: O) => { -// this.#chunk.push(row) - -// if (this.#chunk.length >= chunkSize) { -// this.#request.pause() -// this.#request.off('row', rowListener) -// resolve(this.#chunk.splice(0, chunkSize)) -// } -// } - -// this.#request.on('row', rowListener) - -// this.#request.once('error', reject) - -// this.#request.once('done', () => { -// if (this.#chunk.length < chunkSize) { -// resolve(this.#chunk) -// } -// }) - -// this.#request.resume() -// }) -// } -// } diff --git a/src/query-compiler/default-query-compiler.ts b/src/query-compiler/default-query-compiler.ts index f51995eaf..585652289 100644 --- a/src/query-compiler/default-query-compiler.ts +++ b/src/query-compiler/default-query-compiler.ts @@ -1497,6 +1497,10 @@ export class DefaultQueryCompiler this.compileList(columnAlterations) } + /** + * controls whether the dialect adds a "type" keyword before a column's new data + * type in an ALTER TABLE statement. + */ protected announcesNewColumnDataType(): boolean { return true } diff --git a/test/node/src/test-setup.ts b/test/node/src/test-setup.ts index 790de8380..28e506f2b 100644 --- a/test/node/src/test-setup.ts +++ b/test/node/src/test-setup.ts @@ -142,7 +142,6 @@ export const DIALECT_CONFIGS = { database: 'kysely_test', port: 21433, trustServerCertificate: true, - useUTC: true, }, server: 'localhost', } satisfies Tedious.ConnectionConfig, @@ -170,15 +169,17 @@ export const DB_CONFIGS: PerDialect = { mssql: { dialect: new MssqlDialect({ - connectionFactory: () => new Tedious.Connection(DIALECT_CONFIGS.mssql), - Tarn: { + tarn: { options: { max: POOL_SIZE, min: 0, }, ...Tarn, }, - Tedious, + tedious: { + ...Tedious, + connectionFactory: () => new Tedious.Connection(DIALECT_CONFIGS.mssql), + }, }), plugins: PLUGINS, }, diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index 7fd41a0ca..4e36ccf3c 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -1,7 +1,6 @@ import { UpdateResult, sql } from '../../../' import { - DIALECTS, clearDatabase, destroyTest, initTest, From 36c86e7333d54a5c511b8f806ce09aa5b64957c9 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sun, 13 Aug 2023 00:01:04 +0300 Subject: [PATCH 64/65] fix wording of `supportsCreateIfNotExists`. --- src/dialect/dialect-adapter.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dialect/dialect-adapter.ts b/src/dialect/dialect-adapter.ts index d1ddfe2bd..0a5a7a3ee 100644 --- a/src/dialect/dialect-adapter.ts +++ b/src/dialect/dialect-adapter.ts @@ -12,8 +12,9 @@ export interface DialectAdapter { /** * Whether or not this dialect supports `if not exists` in creation of tables/schemas/views/etc. * - * If this is false, tables and schemas are created without `if not exists` in - * migrations. This is not a problem if the dialect supports transactional DDL. + * If this is false, Kysely's internal migrations tables and schemas are created + * without `if not exists` in migrations. This is not a problem if the dialect + * supports transactional DDL. */ readonly supportsCreateIfNotExists: boolean From e883b2293e9850ce26833c9c73bd6bd2f6bf1297 Mon Sep 17 00:00:00 2001 From: igalklebanov Date: Sat, 9 Sep 2023 15:11:19 +0300 Subject: [PATCH 65/65] add mssql to key value set variant test case. --- test/node/src/update.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/node/src/update.test.ts b/test/node/src/update.test.ts index a1742f219..952594cc0 100644 --- a/test/node/src/update.test.ts +++ b/test/node/src/update.test.ts @@ -100,6 +100,10 @@ for (const dialect of DIALECTS) { sql: 'update `person` set `first_name` = ?, `last_name` = ?, `gender` = ? where `gender` = ?', parameters: ['Foo', 'Barson', 'other', 'female'], }, + mssql: { + sql: 'update "person" set "first_name" = @1, "last_name" = @2, "gender" = @3 where "gender" = @4', + parameters: ['Foo', 'Barson', 'other', 'female'], + }, sqlite: { sql: 'update "person" set "first_name" = ?, "last_name" = ?, "gender" = ? where "gender" = ?', parameters: ['Foo', 'Barson', 'other', 'female'],