diff --git a/.env.example b/.env.example index 4881a20d..3101deed 100644 --- a/.env.example +++ b/.env.example @@ -9,10 +9,3 @@ PLANETSCALE_PASSWORD=password # Cloudflare Hyperdrive WRANGLER_HYPERDRIVE_LOCAL_CONNECTION_STRING_POSTGRESQL=postgresql://test:test@localhost:5432/db0 WRANGLER_HYPERDRIVE_LOCAL_CONNECTION_STRING_MYSQL=mysql://test:test@localhost:3306/db0 - -# MSSQL -MSSQL_HOST=localhost -MSSQL_DB_NAME=TestDB -MSSQL_PORT=1433 -MSSQL_USERNAME=sa -MSSQL_PASSWORD=MyStrong!Passw0rd \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5572090..bff6bcaf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,3 @@ jobs: - run: pnpm build - run: pnpm test:types - run: pnpm vitest - - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/docker-compose.yaml b/docker-compose.yaml index 2341cf09..40dd3898 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -16,11 +16,3 @@ services: MYSQL_DATABASE: db0 MYSQL_USER: test MYSQL_PASSWORD: test - mssql: - # https://hub.docker.com/_/microsoft-mssql-server - image: mcr.microsoft.com/mssql/server:2022-latest - ports: ["1433:1433"] - environment: - ACCEPT_EULA: "Y" - MSSQL_SA_PASSWORD: "MyStrong!Passw0rd" - MSSQL_PID: "Developer" diff --git a/docs/2.connectors/1.index.md b/docs/2.connectors/1.index.md index 447ba5e4..f993a907 100644 --- a/docs/2.connectors/1.index.md +++ b/docs/2.connectors/1.index.md @@ -15,7 +15,6 @@ Currently supported connectors: - [PostgreSQL](/connectors/postgresql) - [MySQL](/connectors/mysql) - [SQLite](/connectors/sqlite) -- [MSSQL](/connectors/mssql) ::read-more{to="https://github.com/unjs/db0/issues/32"} See [unjs/db0#32](https://github.com/unjs/db0/issues/32) for the list of upcoming connectors. diff --git a/docs/2.connectors/mssql.md b/docs/2.connectors/mssql.md deleted file mode 100644 index 090c96c3..00000000 --- a/docs/2.connectors/mssql.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -icon: devicon-plain:microsoftsqlserver ---- - -# MSSQL - -> Connect DB0 to MSSQL Database using `tedious` - -## Usage - -For this connector, you need to install [`tedious`](https://www.npmjs.com/package/tedious) dependency: - -:pm-install{name="tedious"} - -Use `mssql` connector: - -```js -import { createDatabase } from "db0"; -import mssql from "db0/connectors/mssql"; - -const db = createDatabase( - mssql({ - /* options */ - }), -); -``` - -## Options - -:read-more{to="https://tediousjs.github.io/tedious/api-connection.html#function_newConnection"} diff --git a/package.json b/package.json index 1a6a873e..9c3cb54e 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "pg": "^8.17.2", "prettier": "^3.8.1", "scule": "^1.3.0", - "tedious": "^19.1.3", "typescript": "^5.9.3", "vitest": "^4.0.17", "wrangler": "^4.60.0" @@ -77,8 +76,7 @@ "better-sqlite3": "*", "drizzle-orm": "*", "mysql2": "*", - "sqlite3": "*", - "tedious": "*" + "sqlite3": "*" }, "peerDependenciesMeta": { "@libsql/client": { @@ -93,9 +91,6 @@ "mysql2": { "optional": true }, - "tedious": { - "optional": true - }, "@electric-sql/pglite": { "optional": true }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f4ba90b4..6174e96f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,9 +84,6 @@ importers: scule: specifier: ^1.3.0 version: 1.3.0 - tedious: - specifier: ^19.1.3 - version: 19.2.0 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -114,74 +111,6 @@ importers: packages: - '@azure-rest/core-client@2.5.1': - resolution: {integrity: sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==} - engines: {node: '>=20.0.0'} - - '@azure/abort-controller@2.1.2': - resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} - engines: {node: '>=18.0.0'} - - '@azure/core-auth@1.10.1': - resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==} - engines: {node: '>=20.0.0'} - - '@azure/core-client@1.10.1': - resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==} - engines: {node: '>=20.0.0'} - - '@azure/core-http-compat@2.3.1': - resolution: {integrity: sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==} - engines: {node: '>=20.0.0'} - - '@azure/core-lro@2.7.2': - resolution: {integrity: sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==} - engines: {node: '>=18.0.0'} - - '@azure/core-paging@1.6.2': - resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==} - engines: {node: '>=18.0.0'} - - '@azure/core-rest-pipeline@1.22.2': - resolution: {integrity: sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==} - engines: {node: '>=20.0.0'} - - '@azure/core-tracing@1.3.1': - resolution: {integrity: sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==} - engines: {node: '>=20.0.0'} - - '@azure/core-util@1.13.1': - resolution: {integrity: sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==} - engines: {node: '>=20.0.0'} - - '@azure/identity@4.13.0': - resolution: {integrity: sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==} - engines: {node: '>=20.0.0'} - - '@azure/keyvault-common@2.0.0': - resolution: {integrity: sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==} - engines: {node: '>=18.0.0'} - - '@azure/keyvault-keys@4.10.0': - resolution: {integrity: sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==} - engines: {node: '>=18.0.0'} - - '@azure/logger@1.3.0': - resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} - engines: {node: '>=20.0.0'} - - '@azure/msal-browser@4.28.1': - resolution: {integrity: sha512-al2u2fTchbClq3L4C1NlqLm+vwKfhYCPtZN2LR/9xJVaQ4Mnrwf5vANvuyPSJHcGvw50UBmhuVmYUAhTEetTpA==} - engines: {node: '>=0.8.0'} - - '@azure/msal-common@15.14.1': - resolution: {integrity: sha512-IkzF7Pywt6QKTS0kwdCv/XV8x8JXknZDvSjj/IccooxnP373T5jaadO3FnOrbWo3S0UqkfIDyZNTaQ/oAgRdXw==} - engines: {node: '>=0.8.0'} - - '@azure/msal-node@3.8.6': - resolution: {integrity: sha512-XTmhdItcBckcVVTy65Xp+42xG4LX5GK+9AqAsXPXk4IqUNv+LyQo5TMwNjuFYBfAB2GTG9iSQGk+QLc03vhf3w==} - engines: {node: '>=16'} - '@babel/generator@8.0.0-beta.4': resolution: {integrity: sha512-5xRfRZk6wx1BRu2XnTE8cTh2mx1ixrZ3/vpn7p/RCJpgctL6pexVVHE3eqtwlYvHhPAuOYCAlnsAyXpBdmfh5Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1106,9 +1035,6 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@js-joda/core@5.7.0': - resolution: {integrity: sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg==} - '@libsql/client@0.17.0': resolution: {integrity: sha512-TLjSU9Otdpq0SpKHl1tD1Nc9MKhrsZbCFGot3EbCxRa8m1E5R1mMwoOjKMMM31IyF7fr+hPNHLpYfwbMKNusmg==} @@ -1549,9 +1475,6 @@ packages: '@types/react@19.1.13': resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==} - '@types/readable-stream@4.0.23': - resolution: {integrity: sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==} - '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -1656,10 +1579,6 @@ packages: resolution: {integrity: sha512-/G74DRDNrq2pkBjne3aGzycdy2bKa2Hs4DrkLMRq+cDODjyqvHJIZV7hjRRYph3cvGnd0bFd5G6LE5zNp+ap8w==} hasBin: true - '@typespec/ts-http-runtime@0.3.2': - resolution: {integrity: sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==} - engines: {node: '>=20.0.0'} - '@vitest/coverage-v8@4.0.17': resolution: {integrity: sha512-/6zU2FLGg0jsd+ePZcwHRy3+WpNTBBhDY56P4JTRqUN/Dp6CvOEa9HrikcQ4KfV2b2kAHUFB4dl1SuocWXSFEw==} peerDependencies: @@ -1701,10 +1620,6 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1719,10 +1634,6 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} @@ -1795,9 +1706,6 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - bl@6.1.6: - resolution: {integrity: sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==} - blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} @@ -1816,18 +1724,12 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - builtin-modules@5.0.0: resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} engines: {node: '>=18.20'} @@ -2274,9 +2176,6 @@ packages: oxc-resolver: optional: true - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - electron-to-chromium@1.5.277: resolution: {integrity: sha512-wKXFZw4erWmmOz5N/grBoJ2XrNJGDFMu2+W5ACHza5rHtvsqrK4gb6rnLC7XxKB9WlJ+RmyQatuEXmtm86xbnw==} @@ -2425,14 +2324,6 @@ packages: event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -2601,18 +2492,10 @@ packages: resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} engines: {node: '>= 6'} - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - https-proxy-agent@5.0.1: resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} engines: {node: '>= 6'} - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} @@ -2741,9 +2624,6 @@ packages: js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} - js-md4@0.3.2: - resolution: {integrity: sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==} - js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} @@ -2769,16 +2649,6 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - jsonwebtoken@9.0.3: - resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} - engines: {node: '>=12', npm: '>=6'} - - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -2805,30 +2675,9 @@ packages: lodash.deburr@4.1.0: resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} - lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - - lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - - lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - - lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - - lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - lodash.throttle@4.1.1: resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} @@ -3096,9 +2945,6 @@ packages: napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - native-duplexpair@1.0.0: - resolution: {integrity: sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -3317,10 +3163,6 @@ packages: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - promise-inflight@1.0.1: resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} peerDependencies: @@ -3354,10 +3196,6 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -3497,9 +3335,6 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - sqlite3@5.1.7: resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} @@ -3563,10 +3398,6 @@ packages: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - tedious@19.2.0: - resolution: {integrity: sha512-2dDjX0KP54riDvJPiiIozv0WRS/giJb3/JG2lWpa2dgM0Gha7mLAxbTR3ltPkGzfoS6M3oDnhYnWuzeaZibHuQ==} - engines: {node: '>=18.17'} - timers-ext@0.1.8: resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} engines: {node: '>=0.12'} @@ -3674,10 +3505,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3861,151 +3688,6 @@ packages: snapshots: - '@azure-rest/core-client@2.5.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/core-tracing': 1.3.1 - '@typespec/ts-http-runtime': 0.3.2 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/abort-controller@2.1.2': - dependencies: - tslib: 2.8.1 - - '@azure/core-auth@1.10.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-util': 1.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-client@1.10.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-http-compat@2.3.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - transitivePeerDependencies: - - supports-color - - '@azure/core-lro@2.7.2': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-paging@1.6.2': - dependencies: - tslib: 2.8.1 - - '@azure/core-rest-pipeline@1.22.2': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - '@typespec/ts-http-runtime': 0.3.2 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-tracing@1.3.1': - dependencies: - tslib: 2.8.1 - - '@azure/core-util@1.13.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@typespec/ts-http-runtime': 0.3.2 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/identity@4.13.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - '@azure/msal-browser': 4.28.1 - '@azure/msal-node': 3.8.6 - open: 10.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/keyvault-common@2.0.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/keyvault-keys@4.10.0': - dependencies: - '@azure-rest/core-client': 2.5.1 - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-http-compat': 2.3.1 - '@azure/core-lro': 2.7.2 - '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.22.2 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/keyvault-common': 2.0.0 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/logger@1.3.0': - dependencies: - '@typespec/ts-http-runtime': 0.3.2 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/msal-browser@4.28.1': - dependencies: - '@azure/msal-common': 15.14.1 - - '@azure/msal-common@15.14.1': {} - - '@azure/msal-node@3.8.6': - dependencies: - '@azure/msal-common': 15.14.1 - jsonwebtoken: 9.0.3 - uuid: 8.3.2 - '@babel/generator@8.0.0-beta.4': dependencies: '@babel/parser': 8.0.0-beta.4 @@ -4591,8 +4273,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@js-joda/core@5.7.0': {} - '@libsql/client@0.17.0(encoding@0.1.13)': dependencies: '@libsql/core': 0.17.0 @@ -4945,10 +4625,6 @@ snapshots: csstype: 3.2.3 optional: true - '@types/readable-stream@4.0.23': - dependencies: - '@types/node': 25.0.10 - '@types/unist@3.0.3': {} '@types/ws@8.18.1': @@ -5077,14 +4753,6 @@ snapshots: '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260122.3 '@typescript/native-preview-win32-x64': 7.0.0-dev.20260122.3 - '@typespec/ts-http-runtime@0.3.2': - dependencies: - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - '@vitest/coverage-v8@4.0.17(vitest@4.0.17(@types/node@25.0.10)(jiti@2.6.1)(yaml@2.7.1))': dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -5141,10 +4809,6 @@ snapshots: abbrev@1.1.1: optional: true - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -5158,8 +4822,6 @@ snapshots: - supports-color optional: true - agent-base@7.1.4: {} - agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 @@ -5257,13 +4919,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - bl@6.1.6: - dependencies: - '@types/readable-stream': 4.0.23 - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 4.7.0 - blake3-wasm@2.1.5: {} brace-expansion@1.1.12: @@ -5287,8 +4942,6 @@ snapshots: node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) - buffer-equal-constant-time@1.0.1: {} - buffer-from@1.1.2: {} buffer@5.7.1: @@ -5296,11 +4949,6 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - builtin-modules@5.0.0: {} bun-types@1.3.6: @@ -5631,10 +5279,6 @@ snapshots: dts-resolver@2.1.3: {} - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - electron-to-chromium@1.5.277: {} emoji-regex@8.0.0: @@ -5928,10 +5572,6 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 - event-target-shim@5.0.1: {} - - events@3.3.0: {} - expand-template@2.0.3: {} expect-type@1.3.0: {} @@ -6096,13 +5736,6 @@ snapshots: - supports-color optional: true - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 @@ -6111,13 +5744,6 @@ snapshots: - supports-color optional: true - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - humanize-ms@1.2.1: dependencies: ms: 2.1.3 @@ -6220,8 +5846,6 @@ snapshots: js-base64@3.7.8: {} - js-md4@0.3.2: {} - js-tokens@9.0.1: {} js-yaml@4.1.1: @@ -6242,30 +5866,6 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} - jsonwebtoken@9.0.3: - dependencies: - jws: 4.0.1 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.3 - semver: 7.7.3 - - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -6300,22 +5900,8 @@ snapshots: lodash.deburr@4.1.0: {} - lodash.includes@4.3.0: {} - - lodash.isboolean@3.0.3: {} - - lodash.isinteger@4.0.4: {} - - lodash.isnumber@3.0.3: {} - - lodash.isplainobject@4.0.6: {} - - lodash.isstring@4.0.1: {} - lodash.merge@4.6.2: {} - lodash.once@4.1.1: {} - lodash.throttle@4.1.1: {} long@5.3.2: {} @@ -6813,8 +6399,6 @@ snapshots: napi-build-utils@2.0.0: {} - native-duplexpair@1.0.0: {} - natural-compare@1.4.0: {} negotiator@0.6.4: @@ -7058,8 +6642,6 @@ snapshots: pretty-bytes@7.1.0: {} - process@0.11.10: {} - promise-inflight@1.0.1: optional: true @@ -7096,14 +6678,6 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - readdirp@4.1.2: {} readdirp@5.0.0: {} @@ -7289,8 +6863,6 @@ snapshots: split2@4.2.0: {} - sprintf-js@1.1.3: {} - sqlite3@5.1.7: dependencies: bindings: 1.5.0 @@ -7370,21 +6942,6 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - tedious@19.2.0: - dependencies: - '@azure/core-auth': 1.10.1 - '@azure/identity': 4.13.0 - '@azure/keyvault-keys': 4.10.0 - '@js-joda/core': 5.7.0 - '@types/node': 25.0.10 - bl: 6.1.6 - iconv-lite: 0.7.2 - js-md4: 0.3.2 - native-duplexpair: 1.0.0 - sprintf-js: 1.1.3 - transitivePeerDependencies: - - supports-color - timers-ext@0.1.8: dependencies: es5-ext: 0.10.64 @@ -7411,7 +6968,8 @@ snapshots: dependencies: typescript: 5.9.3 - tslib@2.8.1: {} + tslib@2.8.1: + optional: true tunnel-agent@0.6.0: dependencies: @@ -7498,8 +7056,6 @@ snapshots: util-deprecate@1.0.2: {} - uuid@8.3.2: {} - vite@7.3.1(@types/node@25.0.10)(jiti@2.6.1)(yaml@2.7.1): dependencies: esbuild: 0.27.2 diff --git a/scripts/gen-connectors.ts b/scripts/gen-connectors.ts index 8fb6095e..be734deb 100644 --- a/scripts/gen-connectors.ts +++ b/scripts/gen-connectors.ts @@ -50,10 +50,6 @@ const connectors: { optionsTName?: string; }[] = []; -const connectorOptionsNameAliases: Record = { - "mssql": "MSSQL" -}; - for (const entry of connectorEntries) { const pathName = entry.replace(/\.ts$/, ""); const name = pathName.replace(/\/|\\/g, "-"); @@ -71,7 +67,7 @@ for (const entry of connectorEntries) { const names = [...new Set([name, ...alternativeNames])]; - const optionsTName = (connectorOptionsNameAliases[name] || upperFirst(safeName)) + "Options"; + const optionsTName = upperFirst(safeName) + "Options"; connectors.push({ name, diff --git a/src/_connectors.ts b/src/_connectors.ts index e09db472..e0171c60 100644 --- a/src/_connectors.ts +++ b/src/_connectors.ts @@ -9,7 +9,6 @@ import type { ConnectorOptions as LibSQLCoreOptions } from "db0/connectors/libsq import type { ConnectorOptions as LibSQLHttpOptions } from "db0/connectors/libsql/http"; import type { ConnectorOptions as LibSQLNodeOptions } from "db0/connectors/libsql/node"; import type { ConnectorOptions as LibSQLWebOptions } from "db0/connectors/libsql/web"; -import type { ConnectorOptions as MSSQLOptions } from "db0/connectors/mssql"; import type { ConnectorOptions as MySQL2Options } from "db0/connectors/mysql2"; import type { ConnectorOptions as NodeSQLiteOptions } from "db0/connectors/node-sqlite"; import type { ConnectorOptions as PgliteOptions } from "db0/connectors/pglite"; @@ -17,7 +16,7 @@ import type { ConnectorOptions as PlanetscaleOptions } from "db0/connectors/plan import type { ConnectorOptions as PostgreSQLOptions } from "db0/connectors/postgresql"; import type { ConnectorOptions as SQLite3Options } from "db0/connectors/sqlite3"; -export type ConnectorName = "better-sqlite3" | "bun-sqlite" | "bun" | "cloudflare-d1" | "cloudflare-hyperdrive-mysql" | "cloudflare-hyperdrive-postgresql" | "libsql-core" | "libsql-http" | "libsql-node" | "libsql" | "libsql-web" | "mssql" | "mysql2" | "node-sqlite" | "sqlite" | "pglite" | "planetscale" | "postgresql" | "sqlite3"; +export type ConnectorName = "better-sqlite3" | "bun-sqlite" | "bun" | "cloudflare-d1" | "cloudflare-hyperdrive-mysql" | "cloudflare-hyperdrive-postgresql" | "libsql-core" | "libsql-http" | "libsql-node" | "libsql" | "libsql-web" | "mysql2" | "node-sqlite" | "sqlite" | "pglite" | "planetscale" | "postgresql" | "sqlite3"; export type ConnectorOptions = { "better-sqlite3": BetterSQLite3Options; @@ -33,7 +32,6 @@ export type ConnectorOptions = { /** alias of libsql-node */ "libsql": LibSQLNodeOptions; "libsql-web": LibSQLWebOptions; - "mssql": MSSQLOptions; "mysql2": MySQL2Options; "node-sqlite": NodeSQLiteOptions; /** alias of node-sqlite */ @@ -58,7 +56,6 @@ export const connectors: Record = Object.freeze({ /** alias of libsql-node */ "libsql": "db0/connectors/libsql/node", "libsql-web": "db0/connectors/libsql/web", - "mssql": "db0/connectors/mssql", "mysql2": "db0/connectors/mysql2", "node-sqlite": "db0/connectors/node-sqlite", /** alias of node-sqlite */ diff --git a/src/connectors/mssql.ts b/src/connectors/mssql.ts deleted file mode 100644 index 246c0c70..00000000 --- a/src/connectors/mssql.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { - Connection, - Request, - Connection as TediousConnection, - type ConnectionConfiguration, - TYPES, -} from "tedious"; - -import type { Connector, Statement, Primitive } from "db0"; - -// Type for tedious DataType -type DataType = (typeof TYPES)[keyof typeof TYPES]; - -export type ConnectorOptions = ConnectionConfiguration; - -export default function mssqlConnector(opts: ConnectorOptions) { - let _client: undefined | TediousConnection; - async function getClient(): Promise { - if (_client && _client.state === _client.STATE.LOGGED_IN) { - return _client; - } - - return new Promise((resolve, reject) => { - const client = new Connection(opts); - client.connect((error) => { - if (error) { - reject(error); - } - - _client = client; - }); - - client.on("connect", () => { - if (_client) { - resolve(_client); - } - }); - client.on("error", reject); - }); - } - - async function _run(sql: string, parameters?: unknown[]) { - if (!sql) { - throw new Error("SQL query must be provided"); - } - - const connection = await getClient(); - const { sql: _sql, parameters: _parameters } = prepareSqlParameters( - sql, - parameters, - ); - - const query = new Promise<{ rows: unknown[]; success: boolean }>( - (resolve, reject) => { - let success = false; - const request = new Request(_sql, (error) => { - if (error) { - reject(error); - } else { - success = true; - } - }); - - const parameterKeys = Object.keys(_parameters); - for (const key of parameterKeys) { - const parameter = _parameters[key]; - - request.addParameter(parameter.name, parameter.type, parameter.value); - } - - const rows: unknown[] = []; - request.on("row", (columns = []) => { - const currentRow: Record = {}; - for (const column of columns) { - const { value, metadata } = column; - const { colName } = metadata; - - currentRow[colName] = value; - } - - rows.push(currentRow); - }); - - request.on("requestCompleted", () => { - connection.close(); - resolve({ rows, success }); - }); - - request.on("error", (error) => { - connection.close(); - reject(error); - }); - - connection.execSql(request); - }, - ); - - try { - const { rows, success } = await query; - - return { - rows, - success, - }; - } catch (error: any) { - error.sql = _sql; - error.parameters = parameters; - throw error; - } - } - - return >{ - name: "mssql", - dialect: "mssql", - getInstance: () => getClient(), - exec(sql: string) { - return _run(sql, []); - }, - prepare(sql: string) { - const _sql = sql; - let _params: Primitive[] = []; - - const statement: Statement = { - bind(...params: Primitive[]) { - if (params.length > 0) { - _params = params; - } - return statement; - }, - async all(...params: Primitive[]) { - const { rows } = await _run( - _sql, - params.length > 0 ? params : _params, - ); - return rows; - }, - async run(...params: Primitive[]) { - const { success = false } = - (await _run(_sql, params.length > 0 ? params : _params)) || {}; - return { - success, - }; - }, - async get(...params: Primitive[]) { - const { - rows: [row], - } = await _run(_sql, params.length > 0 ? params : _params); - return row; - }, - }; - - return statement; - }, - }; -} - -// taken from the `kysely` library: https://github.com/kysely-org/kysely/blob/413a88516c20be42dc8cbebade68c27669a3ac1a/src/dialect/mssql/mssql-driver.ts#L440 -export function getTediousDataType(value: unknown): DataType { - if (value === null || value === undefined || typeof value === "string") { - return TYPES.NVarChar; - } - - if ( - typeof value === "bigint" || - (typeof value === "number" && value % 1 === 0) - ) { - return value < -2_147_483_648 || value > 2_147_483_647 - ? TYPES.BigInt - : TYPES.Int; - } - - if (typeof value === "number") { - return TYPES.Float; - } - - if (typeof value === "boolean") { - return TYPES.Bit; - } - - if (value instanceof Date) { - return TYPES.DateTime2; - } - - if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) { - return TYPES.VarBinary; - } - - return TYPES.NVarChar; -} - -// replace `?` placeholders with `@1`, `@2`, etc. -export function prepareSqlParameters( - sql: string, - parameters: unknown[] = [], -): { - sql: string; - parameters: Record; -} { - const parameterIndexes: number[] = []; - const tokens = [...sql]; - - // find all `?` placeholders in the SQL string - for (const [i, token] of tokens.entries()) { - if (token === "?") { - parameterIndexes.push(i); - } - } - - const namedParameters: Record< - string, - { name: string; type: DataType; value: unknown } - > = {}; - for (const [index, parameterIndex] of parameterIndexes.entries()) { - const incrementedIndex = index + 1; - // replace `?` placeholder with index-based parameter name - tokens[parameterIndex] = `@${incrementedIndex}`; - // store the parameter value and type with the index-based parameter name - namedParameters[`@${incrementedIndex}`] = { - name: String(incrementedIndex), - type: getTediousDataType(parameters[index]), - value: parameters[index], - }; - } - - return { - sql: tokens.join(""), // join the tokens back into a SQL string - parameters: namedParameters, - }; -} diff --git a/src/types.ts b/src/types.ts index 2a67bf5f..4897e0f0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -3,7 +3,7 @@ */ export type Primitive = string | number | boolean | undefined | null; -export type SQLDialect = "mysql" | "postgresql" | "sqlite" | "libsql" | "mssql"; +export type SQLDialect = "mysql" | "postgresql" | "sqlite" | "libsql"; export type Statement = { /** diff --git a/test/connectors/_tests.ts b/test/connectors/_tests.ts index 0535a135..7f42d784 100644 --- a/test/connectors/_tests.ts +++ b/test/connectors/_tests.ts @@ -44,15 +44,6 @@ export function testConnector(opts: { await db.sql`CREATE TABLE users (\`id\` VARCHAR(4) PRIMARY KEY, \`firstName\` TEXT, \`lastName\` TEXT, \`email\` TEXT)`; break; } - case "mssql": { - await db.sql`CREATE TABLE users ( - [id] NVARCHAR(4) PRIMARY KEY, - [firstName] NVARCHAR(255), - [lastName] NVARCHAR(255), - [email] NVARCHAR(255) - )`; - break; - } default: { await db.sql`CREATE TABLE users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`; break; @@ -66,10 +57,6 @@ export function testConnector(opts: { await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; break; } - case "mssql": { - await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; - break; - } default: { const { rows } = await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '') RETURNING *`; diff --git a/test/connectors/mssql.test.ts b/test/connectors/mssql.test.ts deleted file mode 100644 index 1eec6d80..00000000 --- a/test/connectors/mssql.test.ts +++ /dev/null @@ -1,894 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll } from "vitest"; -import { TYPES } from "tedious"; - -import { - getTediousDataType, - prepareSqlParameters, -} from "../../src/connectors/mssql.js"; -import connector from "../../src/connectors/mssql.js"; -import { testConnector } from "./_tests.js"; -import { createDatabase } from "../../src/index.js"; - -// Helper function to create connection configuration -function createConnectionConfig(database: string = process.env.MSSQL_DB_NAME!) { - return { - server: process.env.MSSQL_HOST!, - authentication: { - type: "default" as const, - options: { - userName: process.env.MSSQL_USERNAME!, - password: process.env.MSSQL_PASSWORD!, - }, - }, - options: { - database, - port: Number.parseInt(process.env.MSSQL_PORT || "1433", 10), - trustServerCertificate: true, - encrypt: false, - }, - }; -} - -describe.runIf( - process.env.MSSQL_HOST && - process.env.MSSQL_DB_NAME && - process.env.MSSQL_USERNAME && - process.env.MSSQL_PASSWORD, -)("connectors: mssql.test", () => { - testConnector({ - dialect: "mssql", - connector: connector(createConnectionConfig()), - }); -}); - -describe.runIf( - process.env.MSSQL_HOST && - process.env.MSSQL_DB_NAME && - process.env.MSSQL_USERNAME && - process.env.MSSQL_PASSWORD, -)("callProcedure", () => { - const db = createDatabase(connector(createConnectionConfig())); - - beforeAll(async () => { - // Drop procedure if it exists - await db.sql` - IF OBJECT_ID('dbo.GetUserCount', 'P') IS NOT NULL - DROP PROCEDURE dbo.GetUserCount - `; - - // Drop procedure if it exists - await db.sql` - IF OBJECT_ID('dbo.AddNumbers', 'P') IS NOT NULL - DROP PROCEDURE dbo.AddNumbers - `; - - // Drop procedure if it exists - await db.sql` - IF OBJECT_ID('dbo.ProcessUserData', 'P') IS NOT NULL - DROP PROCEDURE dbo.ProcessUserData - `; - - // Create a simple stored procedure that returns user count - await db.sql` - CREATE PROCEDURE dbo.GetUserCount - @minAge INT - AS - BEGIN - SELECT COUNT(*) as userCount - FROM (VALUES (1, 25), (2, 30), (3, 35)) AS Users(id, age) - WHERE age >= @minAge - END - `; - - // Create a stored procedure that adds two numbers - await db.sql` - CREATE PROCEDURE dbo.AddNumbers - @a INT, - @b INT - AS - BEGIN - SELECT (@a + @b) as result - END - `; - - // Create a stored procedure that accepts JSON data - await db.sql` - CREATE PROCEDURE dbo.ProcessUserData - @jsonData NVARCHAR(MAX) - AS - BEGIN - -- Parse JSON and return the data - SELECT - JSON_VALUE(@jsonData, '$.name') as name, - JSON_VALUE(@jsonData, '$.email') as email, - JSON_VALUE(@jsonData, '$.age') as age, - (SELECT * FROM OPENJSON(@jsonData, '$.hobbies') WITH (hobby NVARCHAR(100) '$') FOR JSON PATH) as hobbies - END - `; - }); - - afterAll(async () => { - // Clean up procedures - await db.sql` - IF OBJECT_ID('dbo.GetUserCount', 'P') IS NOT NULL - DROP PROCEDURE dbo.GetUserCount - `; - await db.sql` - IF OBJECT_ID('dbo.AddNumbers', 'P') IS NOT NULL - DROP PROCEDURE dbo.AddNumbers - `; - await db.sql` - IF OBJECT_ID('dbo.ProcessUserData', 'P') IS NOT NULL - DROP PROCEDURE dbo.ProcessUserData - `; - await db.dispose(); - }); - - it("should call a stored procedure with parameters", async () => { - const stmt = db.prepare("EXEC dbo.GetUserCount @minAge = ?"); - const rows = await stmt.all(30); - expect(rows).toBeDefined(); - expect(rows.length).toBe(1); - expect(rows[0]).toHaveProperty("userCount"); - expect((rows[0] as { userCount: number }).userCount).toBe(2); - }); - - it("should call a stored procedure with multiple parameters", async () => { - const stmt = db.prepare("EXEC dbo.AddNumbers @a = ?, @b = ?"); - const rows = await stmt.all(10, 20); - expect(rows).toBeDefined(); - expect(rows.length).toBe(1); - expect(rows[0]).toHaveProperty("result"); - expect((rows[0] as { result: number }).result).toBe(30); - }); - - it("should call a stored procedure using prepare", async () => { - const stmt = db.prepare("EXEC dbo.AddNumbers @a = ?, @b = ?"); - const rows = await stmt.all(5, 15); - expect(rows).toBeDefined(); - expect(rows.length).toBe(1); - expect(rows[0]).toHaveProperty("result"); - expect((rows[0] as { result: number }).result).toBe(20); - }); - - it("should return JSON data using FOR JSON PATH", async () => { - const stmt = db.prepare(` - SELECT - id, - firstName, - lastName, - email - FROM ( - VALUES - (1, 'John', 'Doe', 'john@example.com'), - (2, 'Jane', 'Smith', 'jane@example.com') - ) AS Users(id, firstName, lastName, email) - FOR JSON PATH - `); - const rows = await stmt.all(); - expect(rows).toBeDefined(); - expect(rows.length).toBeGreaterThan(0); - - // SQL Server returns JSON as a single column result - // The JSON data is in the first column (usually named "JSON_F52E2B61-18A1-11d1-B105-00805F49916B") - const jsonColumn = Object.keys(rows[0] as object)[0]!; - const jsonString = (rows[0] as Record)[jsonColumn]!; - - expect(jsonString).toBeDefined(); - const jsonData = JSON.parse(jsonString); - expect(Array.isArray(jsonData)).toBe(true); - expect(jsonData.length).toBe(2); - expect(jsonData[0]).toMatchObject({ - id: 1, - firstName: "John", - lastName: "Doe", - email: "john@example.com", - }); - expect(jsonData[1]).toMatchObject({ - id: 2, - firstName: "Jane", - lastName: "Smith", - email: "jane@example.com", - }); - }); - - it("should return JSON data with nested structure using FOR JSON PATH", async () => { - const stmt = db.prepare(` - SELECT - id, - firstName, - lastName, - ( - SELECT email, phone - FROM (VALUES ('john@example.com', '555-1234')) AS Contact(email, phone) - FOR JSON PATH - ) AS contact - FROM (VALUES (1, 'John', 'Doe')) AS Users(id, firstName, lastName) - FOR JSON PATH - `); - const rows = await stmt.all(); - expect(rows).toBeDefined(); - - const jsonColumn = Object.keys(rows[0] as object)[0]!; - const jsonString = (rows[0] as Record)[jsonColumn]!; - const jsonData = JSON.parse(jsonString); - - expect(Array.isArray(jsonData)).toBe(true); - expect(jsonData[0]).toHaveProperty("id", 1); - expect(jsonData[0]).toHaveProperty("firstName", "John"); - expect(jsonData[0]).toHaveProperty("contact"); - - // The nested contact is already a JSON string that needs to be parsed - const contactData = jsonData[0].contact; - const contact = - typeof contactData === "string" ? JSON.parse(contactData) : contactData; - expect(Array.isArray(contact)).toBe(true); - expect(contact[0]).toMatchObject({ - email: "john@example.com", - phone: "555-1234", - }); - }); - - it("should return single JSON object using FOR JSON PATH, WITHOUT_ARRAY_WRAPPER", async () => { - const stmt = db.prepare(` - SELECT - id, - firstName, - lastName, - email, - age - FROM ( - VALUES (1, 'John', 'Doe', 'john@example.com', 30) - ) AS Users(id, firstName, lastName, email, age) - FOR JSON PATH, WITHOUT_ARRAY_WRAPPER - `); - const rows = await stmt.all(); - expect(rows).toBeDefined(); - expect(rows.length).toBeGreaterThan(0); - - // SQL Server returns JSON as a single column result - const jsonColumn = Object.keys(rows[0] as object)[0]!; - const jsonString = (rows[0] as Record)[jsonColumn]!; - - expect(jsonString).toBeDefined(); - const jsonData = JSON.parse(jsonString); - - // WITHOUT_ARRAY_WRAPPER returns a single object, not an array - expect(Array.isArray(jsonData)).toBe(false); - expect(jsonData).toMatchObject({ - id: 1, - firstName: "John", - lastName: "Doe", - email: "john@example.com", - age: 30, - }); - }); - - it("should call a stored procedure with JSON parameter", async () => { - const userData = { - name: "Alice Johnson", - email: "alice@example.com", - age: "28", - hobbies: ["reading", "hiking", "photography"], - }; - - const jsonString = JSON.stringify(userData); - const stmt = db.prepare("EXEC dbo.ProcessUserData @jsonData = ?"); - const rows = await stmt.all(jsonString); - - expect(rows).toBeDefined(); - expect(rows.length).toBe(1); - expect(rows[0]).toHaveProperty("name", "Alice Johnson"); - expect(rows[0]).toHaveProperty("email", "alice@example.com"); - expect(rows[0]).toHaveProperty("age", "28"); - expect(rows[0]).toHaveProperty("hobbies"); - - // Parse the hobbies JSON array - const hobbiesData = (rows[0] as Record).hobbies; - if (hobbiesData) { - const hobbies = JSON.parse(hobbiesData); - expect(Array.isArray(hobbies)).toBe(true); - expect(hobbies.length).toBe(3); - expect(hobbies[0]).toHaveProperty("hobby", "reading"); - expect(hobbies[1]).toHaveProperty("hobby", "hiking"); - expect(hobbies[2]).toHaveProperty("hobby", "photography"); - } - }); - - it("should call a stored procedure with complex JSON parameter", async () => { - const complexData = { - name: "Bob Smith", - email: "bob@example.com", - age: "35", - hobbies: ["gaming", "cooking"], - }; - - const jsonString = JSON.stringify(complexData); - const stmt = db.prepare("EXEC dbo.ProcessUserData @jsonData = ?"); - const rows = await stmt.all(jsonString); - - expect(rows).toBeDefined(); - expect(rows.length).toBe(1); - - const result = rows[0] as Record; - expect(result.name).toBe("Bob Smith"); - expect(result.email).toBe("bob@example.com"); - expect(result.age).toBe("35"); - - // Verify hobbies array - if (result.hobbies) { - const hobbies = JSON.parse(result.hobbies); - expect(Array.isArray(hobbies)).toBe(true); - expect(hobbies.length).toBe(2); - } - }); -}); - -describe.runIf( - process.env.MSSQL_HOST && - process.env.MSSQL_USERNAME && - process.env.MSSQL_PASSWORD, -)("createDatabase", () => { - const testDbName = "TestDB_CreateTest"; - let db: ReturnType; - - beforeAll(() => { - // Connect to master database to create/drop test database - db = createDatabase(connector(createConnectionConfig("master"))); - }); - - afterAll(async () => { - // Clean up: drop the test database if it exists - // try { - // await db.exec(` - // IF EXISTS (SELECT * FROM sys.databases WHERE name = '${testDbName}') - // BEGIN - // ALTER DATABASE [${testDbName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; - // DROP DATABASE [${testDbName}]; - // END - // `); - // } catch (error) { - // // Ignore errors if database doesn't exist - // } - await db.dispose(); - }); - - it("should create a new database", async () => { - // Drop database if it exists from previous failed test - try { - await db.exec(` - IF EXISTS (SELECT * FROM sys.databases WHERE name = '${testDbName}') - BEGIN - ALTER DATABASE [${testDbName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; - DROP DATABASE [${testDbName}]; - END - `); - } catch { - // Ignore errors if database doesn't exist - } - - // Create the database - await db.exec(`CREATE DATABASE [${testDbName}]`); - - // Verify the database exists - const stmt = db.prepare("SELECT name FROM sys.databases WHERE name = ?"); - const rows = await stmt.all(testDbName); - expect(rows).toBeDefined(); - expect(rows.length).toBe(1); - expect((rows[0] as { name: string }).name).toBe(testDbName); - }); - - it("should check if database exists", async () => { - const stmt = db.prepare(` - SELECT CASE - WHEN EXISTS (SELECT * FROM sys.databases WHERE name = ?) - THEN 1 - ELSE 0 - END as dbExists - `); - const rows = await stmt.all(testDbName); - expect(rows).toBeDefined(); - expect(rows.length).toBe(1); - expect((rows[0] as { dbExists: number }).dbExists).toBe(1); - }); - - it.skip("should drop an existing database", async () => { - // Drop the database - await db.exec(` - ALTER DATABASE [${testDbName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; - DROP DATABASE [${testDbName}]; - `); - - // Verify the database no longer exists - const stmt = db.prepare("SELECT name FROM sys.databases WHERE name = ?"); - const rows = await stmt.all(testDbName); - expect(rows).toBeDefined(); - expect(rows.length).toBe(0); - }); -}); - -describe("getTediousDataType", () => { - it("should return NVarChar for null", () => { - expect(getTediousDataType(null)).toBe(TYPES.NVarChar); - }); - - it("should return NVarChar for undefined", () => { - expect(getTediousDataType(undefined)).toBe(TYPES.NVarChar); - }); - - it("should return NVarChar for strings", () => { - expect(getTediousDataType("test")).toBe(TYPES.NVarChar); - }); - - it("should return Int for numbers", () => { - expect(getTediousDataType(123)).toBe(TYPES.Int); - }); - - it("should return BigInt for large integer numbers", () => { - expect(getTediousDataType(2_147_483_648)).toBe(TYPES.BigInt); - }); - - it("should return Float for floating point numbers", () => { - expect(getTediousDataType(123.45)).toBe(TYPES.Float); - }); - - it("should return Bit for boolean values", () => { - expect(getTediousDataType(true)).toBe(TYPES.Bit); - }); - - it("should return DateTime for Date objects", () => { - expect(getTediousDataType(new Date())).toBe(TYPES.DateTime2); - }); - - it("should return VarBinary for Buffer objects", () => { - expect(getTediousDataType(Buffer.from("test"))).toBe(TYPES.VarBinary); - }); - - it("should return NVarChar by default for other types", () => { - expect(getTediousDataType({})).toBe(TYPES.NVarChar); - }); -}); - -describe("prepareSqlParameters", () => { - it("should replace ? with @1, @2, etc.", () => { - const sql = "SELECT * FROM users WHERE id = ? AND name = ?"; - const parameters = [1, "John"]; - const result = prepareSqlParameters(sql, parameters); - expect(result.sql).toBe("SELECT * FROM users WHERE id = @1 AND name = @2"); - expect(result.parameters).toEqual({ - "@1": { name: "1", type: TYPES.Int, value: 1 }, - "@2": { name: "2", type: TYPES.NVarChar, value: "John" }, - }); - }); - - it("should handle no parameters", () => { - const sql = "SELECT * FROM users"; - const parameters: unknown[] = []; - const result = prepareSqlParameters(sql, parameters); - expect(result.sql).toBe("SELECT * FROM users"); - expect(result.parameters).toEqual({}); - }); - - it("should handle multiple parameters of different types", () => { - const sql = "SELECT * FROM users WHERE id = ? AND age = ? AND active = ?"; - const parameters = [1, 30, true]; - const result = prepareSqlParameters(sql, parameters); - expect(result.sql).toBe( - "SELECT * FROM users WHERE id = @1 AND age = @2 AND active = @3", - ); - expect(result.parameters).toEqual({ - "@1": { name: "1", type: TYPES.Int, value: 1 }, - "@2": { name: "2", type: TYPES.Int, value: 30 }, - "@3": { name: "3", type: TYPES.Bit, value: true }, - }); - }); - - it("should handle null and undefined parameters", () => { - const sql = "SELECT * FROM users WHERE name = ? AND email = ?"; - - const parameters = [null, undefined]; - const result = prepareSqlParameters(sql, parameters); - expect(result.sql).toBe( - "SELECT * FROM users WHERE name = @1 AND email = @2", - ); - expect(result.parameters).toEqual({ - "@1": { name: "1", type: TYPES.NVarChar, value: null }, - "@2": { name: "2", type: TYPES.NVarChar, value: undefined }, - }); - }); -}); - -// Error Handling Tests -describe.runIf( - process.env.MSSQL_HOST && - process.env.MSSQL_DB_NAME && - process.env.MSSQL_USERNAME && - process.env.MSSQL_PASSWORD, -)("error handling", () => { - const db = createDatabase(connector(createConnectionConfig())); - - afterAll(async () => { - await db.dispose(); - }); - - it("should handle invalid SQL syntax", async () => { - await expect(async () => { - await db.exec("SELECT * FORM invalid_table"); - }).rejects.toThrow(); - }); - - it("should handle non-existent table", async () => { - await expect(async () => { - await db.sql`SELECT * FROM non_existent_table_12345`; - }).rejects.toThrow(); - }); - - it("should handle parameter count mismatch", async () => { - const stmt = db.prepare( - "INSERT INTO sys.tables (name, object_id) VALUES (?, ?)", - ); - // Providing only one parameter when two are expected - should fail - await expect(async () => { - await stmt.all("test"); - }).rejects.toThrow(); - }); - - it("should handle empty SQL query", async () => { - await expect(async () => { - await db.exec(""); - }).rejects.toThrow("SQL query must be provided"); - }); - - it("should provide error context with SQL and parameters", async () => { - try { - const stmt = db.prepare("SELECT * FROM invalid_table WHERE id = ?"); - await stmt.all(123); - expect.fail("Should have thrown an error"); - } catch (error: any) { - expect(error.sql).toBeDefined(); - expect(error.parameters).toBeDefined(); - } - }); - - it("should handle type conversion errors gracefully", async () => { - // Test that tedious converts string numbers to integers automatically - const stmt = db.prepare("SELECT CAST(? AS INT) as result"); - const rows = await stmt.all("42"); - expect(rows.length).toBe(1); - expect((rows[0] as any).result).toBe(42); - }); -}); - -// Transaction Tests -describe.runIf( - process.env.MSSQL_HOST && - process.env.MSSQL_DB_NAME && - process.env.MSSQL_USERNAME && - process.env.MSSQL_PASSWORD, -)("transactions", () => { - const db = createDatabase(connector(createConnectionConfig())); - - beforeAll(async () => { - await db.exec(` - IF OBJECT_ID('dbo.test_transactions', 'U') IS NOT NULL - DROP TABLE dbo.test_transactions; - CREATE TABLE dbo.test_transactions ( - id INT PRIMARY KEY, - value NVARCHAR(100) - ); - `); - }); - - afterAll(async () => { - await db.exec("DROP TABLE IF EXISTS dbo.test_transactions"); - await db.dispose(); - }); - - it("should commit a transaction successfully", async () => { - // Use single connection for transaction operations - await db.exec(` - BEGIN TRANSACTION; - INSERT INTO dbo.test_transactions (id, value) VALUES (1, 'test1'); - COMMIT TRANSACTION; - `); - - const stmt = db.prepare("SELECT * FROM dbo.test_transactions WHERE id = ?"); - const rows = await stmt.all(1); - expect(rows.length).toBe(1); - expect((rows[0] as any).value).toBe("test1"); - }); - - it("should rollback a transaction", async () => { - // Use single connection for transaction operations - await db.exec(` - BEGIN TRANSACTION; - INSERT INTO dbo.test_transactions (id, value) VALUES (2, 'test2'); - ROLLBACK TRANSACTION; - `); - - const stmt = db.prepare("SELECT * FROM dbo.test_transactions WHERE id = ?"); - const rows = await stmt.all(2); - expect(rows.length).toBe(0); - }); - - it("should handle nested transactions with savepoints", async () => { - await db.exec(` - BEGIN TRANSACTION; - INSERT INTO dbo.test_transactions (id, value) VALUES (3, 'outer'); - SAVE TRANSACTION savepoint1; - INSERT INTO dbo.test_transactions (id, value) VALUES (4, 'inner'); - ROLLBACK TRANSACTION savepoint1; - COMMIT TRANSACTION; - `); - - const stmt = db.prepare( - "SELECT * FROM dbo.test_transactions WHERE id IN (?, ?)", - ); - const rows = await stmt.all(3, 4); - - expect(rows.length).toBe(1); - expect((rows[0] as any).id).toBe(3); - }); - - it("should handle transaction isolation levels", async () => { - await db.exec(` - SET TRANSACTION ISOLATION LEVEL READ COMMITTED; - BEGIN TRANSACTION; - INSERT INTO dbo.test_transactions (id, value) VALUES (5, 'isolation_test'); - COMMIT TRANSACTION; - `); - - const stmt = db.prepare("SELECT * FROM dbo.test_transactions WHERE id = ?"); - const rows = await stmt.all(5); - expect(rows.length).toBe(1); - }); -}); - -// Batch Operations and Advanced Tests -describe.runIf( - process.env.MSSQL_HOST && - process.env.MSSQL_DB_NAME && - process.env.MSSQL_USERNAME && - process.env.MSSQL_PASSWORD, -)("batch operations", () => { - const db = createDatabase(connector(createConnectionConfig())); - - beforeAll(async () => { - await db.exec(` - IF OBJECT_ID('dbo.test_batch', 'U') IS NOT NULL - DROP TABLE dbo.test_batch; - CREATE TABLE dbo.test_batch ( - id INT PRIMARY KEY, - name NVARCHAR(100), - data VARBINARY(MAX) - ); - - IF OBJECT_ID('dbo.GetUserWithOutput', 'P') IS NOT NULL - DROP PROCEDURE dbo.GetUserWithOutput; - `); - - await db.exec(` - CREATE PROCEDURE dbo.GetUserWithOutput - @userId INT, - @userName NVARCHAR(100) OUTPUT - AS - BEGIN - SET @userName = 'User_' + CAST(@userId AS NVARCHAR); - SELECT @userId as id, @userName as name; - END - `); - }); - - afterAll(async () => { - await db.exec("DROP TABLE IF EXISTS dbo.test_batch"); - await db.exec("DROP PROCEDURE IF EXISTS dbo.GetUserWithOutput"); - await db.dispose(); - }); - - it("should handle multiple inserts in batch", async () => { - const stmt = db.prepare( - "INSERT INTO dbo.test_batch (id, name) VALUES (?, ?)", - ); - - await stmt.run(1, "Alice"); - await stmt.run(2, "Bob"); - await stmt.run(3, "Charlie"); - - const selectStmt = db.prepare( - "SELECT COUNT(*) as count FROM dbo.test_batch", - ); - const rows = await selectStmt.all(); - expect((rows[0] as any).count).toBe(3); - }); - - it("should handle binary data (BLOB)", async () => { - const binaryData = Buffer.from("Hello, World!", "utf8"); - const stmt = db.prepare( - "INSERT INTO dbo.test_batch (id, name, data) VALUES (?, ?, ?);", - ); - await stmt.run(10, "binary_test", binaryData as any); - - const selectStmt = db.prepare("SELECT * FROM dbo.test_batch WHERE id = ?"); - const rows = await selectStmt.all(10); - expect(rows.length).toBe(1); - expect(Buffer.isBuffer((rows[0] as any).data)).toBe(true); - expect((rows[0] as any).data.toString("utf8")).toBe("Hello, World!"); - }); - - it("should handle special characters in parameters", async () => { - const specialText = - "Test with 'quotes', \"double quotes\", and\nnewlines\ttabs"; - const stmt = db.prepare( - "INSERT INTO dbo.test_batch (id, name) VALUES (?, ?)", - ); - await stmt.run(20, specialText); - - const selectStmt = db.prepare("SELECT * FROM dbo.test_batch WHERE id = ?"); - const rows = await selectStmt.all(20); - expect((rows[0] as any).name).toBe(specialText); - }); - - it("should handle unicode characters", async () => { - const unicodeText = "Hello 世界 🌍 مرحبا"; - const stmt = db.prepare( - "INSERT INTO dbo.test_batch (id, name) VALUES (?, ?)", - ); - await stmt.run(30, unicodeText); - - const selectStmt = db.prepare("SELECT * FROM dbo.test_batch WHERE id = ?"); - const rows = await selectStmt.all(30); - expect((rows[0] as any).name).toBe(unicodeText); - }); - - it("should handle stored procedure with output parameters", async () => { - // Note: Current implementation doesn't support OUTPUT parameters directly - // This test calls the procedure and gets result set instead (returns 2 rows) - const stmt = db.prepare( - "DECLARE @name NVARCHAR(100); EXEC dbo.GetUserWithOutput @userId = ?, @userName = @name OUTPUT; SELECT @name as userName", - ); - const rows = await stmt.all(42); - // Procedure returns result set + our SELECT statement = 2 rows - expect(rows.length).toBeGreaterThanOrEqual(1); - // Check the last row for our output - const lastRow = rows.at(-1); - expect((lastRow as any).userName).toBe("User_42"); - }); - - it("should handle empty result sets", async () => { - const stmt = db.prepare("SELECT * FROM dbo.test_batch WHERE id = ?"); - const rows = await stmt.all(99_999); - expect(rows.length).toBe(0); - }); - - it("should handle NULL values correctly", async () => { - const stmt = db.prepare( - "INSERT INTO dbo.test_batch (id, name) VALUES (?, ?)", - ); - - await stmt.run(40, null); - - const selectStmt = db.prepare("SELECT * FROM dbo.test_batch WHERE id = ?"); - const rows = await selectStmt.all(40); - expect(rows.length).toBe(1); - expect((rows[0] as any).name).toBeNull(); - }); -}); - -// Performance Tests -describe.runIf( - process.env.MSSQL_HOST && - process.env.MSSQL_DB_NAME && - process.env.MSSQL_USERNAME && - process.env.MSSQL_PASSWORD, -)("performance", () => { - const db = createDatabase(connector(createConnectionConfig())); - - beforeAll(async () => { - await db.exec(` - IF OBJECT_ID('dbo.test_performance', 'U') IS NOT NULL - DROP TABLE dbo.test_performance; - CREATE TABLE dbo.test_performance ( - id INT PRIMARY KEY, - data NVARCHAR(1000) - ); - `); - }); - - afterAll(async () => { - await db.exec("DROP TABLE IF EXISTS dbo.test_performance"); - await db.dispose(); - }); - - it("should handle multiple sequential queries efficiently", async () => { - const start = Date.now(); - const stmt = db.prepare("SELECT ?"); - - for (let i = 0; i < 10; i++) { - await stmt.all(i); - } - - const duration = Date.now() - start; - // Should complete 10 queries in reasonable time (less than 5 seconds) - expect(duration).toBeLessThan(5000); - }); - - it("should handle large result sets", async () => { - // Insert 100 rows - const insertStmt = db.prepare( - "INSERT INTO dbo.test_performance (id, data) VALUES (?, ?)", - ); - for (let i = 1; i <= 100; i++) { - await insertStmt.run(i, `Data for row ${i}`); - } - - const start = Date.now(); - const selectStmt = db.prepare("SELECT * FROM dbo.test_performance"); - const rows = await selectStmt.all(); - const duration = Date.now() - start; - - expect(rows.length).toBe(100); - // Should fetch 100 rows in reasonable time (less than 2 seconds) - expect(duration).toBeLessThan(2000); - }); - - it("should handle prepared statement reuse", async () => { - const stmt = db.prepare("SELECT * FROM dbo.test_performance WHERE id = ?"); - - const start = Date.now(); - for (let i = 1; i <= 20; i++) { - await stmt.all(i); - } - const duration = Date.now() - start; - - // Reusing prepared statement should be efficient (less than 3 seconds) - expect(duration).toBeLessThan(3000); - }); - - it("should handle concurrent query execution", async () => { - const stmt = db.prepare("SELECT ?"); - - const start = Date.now(); - // Note: Current implementation may execute these sequentially due to connection management - const promises = []; - for (let i = 0; i < 5; i++) { - promises.push(stmt.all(i)); - } - await Promise.all(promises); - const duration = Date.now() - start; - - // Should handle 5 queries efficiently - expect(duration).toBeLessThan(5000); - }); - - it("should handle large text data", async () => { - const largeText = "a".repeat(900); // Just under 1000 char limit - const stmt = db.prepare( - "INSERT INTO dbo.test_performance (id, data) VALUES (?, ?)", - ); - - const start = Date.now(); - await stmt.run(1000, largeText); - const duration = Date.now() - start; - - expect(duration).toBeLessThan(2000); - - const selectStmt = db.prepare( - "SELECT * FROM dbo.test_performance WHERE id = ?", - ); - const rows = await selectStmt.all(1000); - expect((rows[0] as any).data).toBe(largeText); - }); - - it("should handle query timeout scenarios", async () => { - // Test a long-running query - const stmt = db.prepare("WAITFOR DELAY '00:00:01'; SELECT 1 as result"); - - const start = Date.now(); - const rows = await stmt.all(); - const duration = Date.now() - start; - - expect(rows.length).toBe(1); - expect(duration).toBeGreaterThanOrEqual(1000); - expect(duration).toBeLessThan(3000); - }); -}); diff --git a/vitest.config.ts b/vitest.config.ts index 35705232..cec357c9 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ test: { setupFiles: ["dotenv/config"], coverage: { - reporter: ["text", "clover", "json", "html"], + reporter: ["text", "clover", "json"], include: ["src/**/*.ts"], }, },