d1s 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.next/.next/BUILD_ID +1 -1
- package/dist/.next/.next/app-build-manifest.json +33 -33
- package/dist/.next/.next/app-path-routes-manifest.json +1 -1
- package/dist/.next/.next/build-manifest.json +3 -3
- package/dist/.next/.next/cache/.rscinfo +1 -1
- package/dist/.next/.next/cache/.tsbuildinfo +1 -1
- package/dist/.next/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/1.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/10.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/11.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/12.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/2.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/3.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/4.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/5.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/6.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/7.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/8.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/9.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/dist/.next/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/1.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/.next/.next/cache/webpack/server-production/index.pack.old +0 -0
- package/dist/.next/.next/next-minimal-server.js.nft.json +1 -1
- package/dist/.next/.next/next-server.js.nft.json +1 -1
- package/dist/.next/.next/prerender-manifest.json +1 -1
- package/dist/.next/.next/required-server-files.json +1 -1
- package/dist/.next/.next/server/app/_not-found/page.js +1 -1
- package/dist/.next/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/.next/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/_not-found.html +1 -1
- package/dist/.next/.next/server/app/_not-found.rsc +4 -4
- package/dist/.next/.next/server/app/api/cloudflare/route.js +1 -1
- package/dist/.next/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/databases/route.js +1 -1
- package/dist/.next/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/migrations/sync/route.js +5 -5
- package/dist/.next/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/migrations/sync-files/route.js +3 -3
- package/dist/.next/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/pull-data/confirm/route.js +1 -1
- package/dist/.next/.next/server/app/api/pull-data/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route.js +2 -2
- package/dist/.next/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/pull-remote/preview/route.js +4 -4
- package/dist/.next/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/push-remote/confirm/route.js +5 -5
- package/dist/.next/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/push-remote/preview/route.js +4 -4
- package/dist/.next/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/query/route.js +3 -3
- package/dist/.next/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/tables/[name]/route.js +5 -5
- package/dist/.next/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/tables/route.js +2 -2
- package/dist/.next/.next/server/app/api/tables/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/api/update/route.js +1 -1
- package/dist/.next/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app/index.html +3 -3
- package/dist/.next/.next/server/app/index.rsc +5 -5
- package/dist/.next/.next/server/app/page.js +11 -13
- package/dist/.next/.next/server/app/page.js.nft.json +1 -1
- package/dist/.next/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/.next/.next/server/app-paths-manifest.json +3 -3
- package/dist/.next/.next/server/chunks/226.js +12 -12
- package/dist/.next/.next/server/chunks/391.js +5 -5
- package/dist/.next/.next/server/chunks/452.js +1 -1
- package/dist/.next/.next/server/chunks/572.js +1 -0
- package/dist/.next/.next/server/chunks/627.js +3 -3
- package/dist/.next/.next/server/chunks/638.js +3 -3
- package/dist/.next/.next/server/chunks/992.js +3 -3
- package/dist/.next/.next/server/middleware-build-manifest.js +1 -1
- package/dist/.next/.next/server/next-font-manifest.js +1 -1
- package/dist/.next/.next/server/next-font-manifest.json +1 -1
- package/dist/.next/.next/server/pages/404.html +1 -1
- package/dist/.next/.next/server/pages/500.html +1 -1
- package/dist/.next/.next/server/pages/_app.js +1 -1
- package/dist/.next/.next/server/pages/_document.js +1 -1
- package/dist/.next/.next/server/pages/_error.js +1 -1
- package/dist/.next/.next/server/server-reference-manifest.json +1 -1
- package/dist/.next/.next/static/chunks/576-18a2209e1ed26aad.js +1 -0
- package/dist/.next/.next/static/chunks/{92-c4466e0f1ca8a3a8.js → 92-5f7522943fcb72a8.js} +1 -1
- package/dist/.next/.next/static/chunks/app/{layout-be6ee67cf0c92654.js → layout-40ec07d04681ba81.js} +1 -1
- package/dist/.next/.next/static/chunks/app/page-f05cd0fc436bf9bc.js +1 -0
- package/dist/.next/.next/static/chunks/{main-app-fa35024de9bd9284.js → main-app-4506e88db72db4b7.js} +1 -1
- package/dist/.next/.next/static/css/7c16f57d325d0d7d.css +3 -0
- package/dist/.next/.next/static/gABGrAoO8IPArDo_Ns_7g/_buildManifest.js +1 -0
- package/dist/.next/.next/static/gABGrAoO8IPArDo_Ns_7g/_ssgManifest.js +1 -0
- package/dist/.next/.next/static/gh-QfIUVo4FTTduYCVKT6/_buildManifest.js +1 -0
- package/dist/.next/.next/static/gh-QfIUVo4FTTduYCVKT6/_ssgManifest.js +1 -0
- package/dist/.next/.next/static/media/8d697b304b401681-s.woff2 +0 -0
- package/dist/.next/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
- package/dist/.next/.next/static/ybCx6L6gHqtODbKmlhU-d/_buildManifest.js +1 -0
- package/dist/.next/.next/static/ybCx6L6gHqtODbKmlhU-d/_ssgManifest.js +1 -0
- package/dist/.next/.next/trace +3 -3
- package/dist/.next/.next/types/app/api/cloudflare/route.ts +1 -1
- package/dist/.next/.next/types/app/api/databases/route.ts +1 -1
- package/dist/.next/.next/types/app/api/migrations/sync/route.ts +1 -1
- package/dist/.next/.next/types/app/api/migrations/sync-files/route.ts +1 -1
- package/dist/.next/.next/types/app/api/pull-data/confirm/route.ts +1 -1
- package/dist/.next/.next/types/app/api/pull-remote/confirm/route.ts +1 -1
- package/dist/.next/.next/types/app/api/pull-remote/preview/route.ts +1 -1
- package/dist/.next/.next/types/app/api/push-remote/confirm/route.ts +1 -1
- package/dist/.next/.next/types/app/api/push-remote/preview/route.ts +1 -1
- package/dist/.next/.next/types/app/api/query/route.ts +1 -1
- package/dist/.next/.next/types/app/api/tables/[name]/route.ts +1 -1
- package/dist/.next/.next/types/app/api/tables/route.ts +1 -1
- package/dist/.next/.next/types/app/api/update/route.ts +1 -1
- package/dist/.next/.next/types/app/page.ts +1 -1
- package/dist/.next/BUILD_ID +1 -1
- package/dist/.next/app-build-manifest.json +32 -32
- package/dist/.next/app-path-routes-manifest.json +1 -1
- package/dist/.next/build-manifest.json +3 -3
- package/dist/.next/cache/.rscinfo +1 -1
- package/dist/.next/cache/.tsbuildinfo +1 -1
- package/dist/.next/cache/webpack/client-production/0.pack +0 -0
- package/dist/.next/cache/webpack/client-production/1.pack +0 -0
- package/dist/.next/cache/webpack/client-production/2.pack +0 -0
- package/dist/.next/cache/webpack/client-production/index.pack +0 -0
- package/dist/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/dist/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/dist/.next/cache/webpack/server-production/0.pack +0 -0
- package/dist/.next/cache/webpack/server-production/index.pack +0 -0
- package/dist/.next/next-minimal-server.js.nft.json +1 -1
- package/dist/.next/next-server.js.nft.json +1 -1
- package/dist/.next/prerender-manifest.json +1 -1
- package/dist/.next/required-server-files.json +1 -1
- package/dist/.next/server/app/_not-found/page.js +1 -1
- package/dist/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/_not-found.html +1 -1
- package/dist/.next/server/app/_not-found.rsc +4 -4
- package/dist/.next/server/app/api/cloudflare/route.js +1 -1
- package/dist/.next/server/app/api/cloudflare/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/databases/route.js +1 -1
- package/dist/.next/server/app/api/databases/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/migrations/sync/route.js +5 -5
- package/dist/.next/server/app/api/migrations/sync/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/migrations/sync-files/route.js +3 -3
- package/dist/.next/server/app/api/migrations/sync-files/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/pull-data/confirm/route.js +1 -1
- package/dist/.next/server/app/api/pull-data/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/pull-remote/confirm/route.js +2 -2
- package/dist/.next/server/app/api/pull-remote/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/pull-remote/preview/route.js +4 -4
- package/dist/.next/server/app/api/pull-remote/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/push-remote/confirm/route.js +5 -5
- package/dist/.next/server/app/api/push-remote/confirm/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/push-remote/preview/route.js +4 -4
- package/dist/.next/server/app/api/push-remote/preview/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/query/route.js +3 -3
- package/dist/.next/server/app/api/query/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/tables/[name]/route.js +5 -5
- package/dist/.next/server/app/api/tables/[name]/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/tables/route.js +2 -2
- package/dist/.next/server/app/api/tables/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/api/update/route.js +1 -1
- package/dist/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/.next/server/app/index.html +3 -3
- package/dist/.next/server/app/index.rsc +5 -5
- package/dist/.next/server/app/page.js +11 -13
- package/dist/.next/server/app/page.js.nft.json +1 -1
- package/dist/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/.next/server/app-paths-manifest.json +2 -2
- package/dist/.next/server/chunks/226.js +12 -12
- package/dist/.next/server/chunks/391.js +5 -5
- package/dist/.next/server/chunks/452.js +1 -1
- package/dist/.next/server/chunks/572.js +1 -0
- package/dist/.next/server/chunks/627.js +3 -3
- package/dist/.next/server/chunks/638.js +3 -3
- package/dist/.next/server/chunks/992.js +3 -3
- package/dist/.next/server/middleware-build-manifest.js +1 -1
- package/dist/.next/server/next-font-manifest.js +1 -1
- package/dist/.next/server/next-font-manifest.json +1 -1
- package/dist/.next/server/pages/404.html +1 -1
- package/dist/.next/server/pages/500.html +1 -1
- package/dist/.next/server/pages/_app.js +1 -1
- package/dist/.next/server/pages/_document.js +1 -1
- package/dist/.next/server/pages/_error.js +1 -1
- package/dist/.next/server/server-reference-manifest.json +1 -1
- package/dist/.next/static/IX-QLbm7scYnyqWeeimal/_buildManifest.js +1 -0
- package/dist/.next/static/IX-QLbm7scYnyqWeeimal/_ssgManifest.js +1 -0
- package/dist/.next/static/chunks/576-18a2209e1ed26aad.js +1 -0
- package/dist/.next/static/chunks/{92-c4466e0f1ca8a3a8.js → 92-5f7522943fcb72a8.js} +1 -1
- package/dist/.next/static/chunks/app/{layout-be6ee67cf0c92654.js → layout-40ec07d04681ba81.js} +1 -1
- package/dist/.next/static/chunks/app/page-f05cd0fc436bf9bc.js +1 -0
- package/dist/.next/static/chunks/{main-app-fa35024de9bd9284.js → main-app-4506e88db72db4b7.js} +1 -1
- package/dist/.next/static/css/7c16f57d325d0d7d.css +3 -0
- package/dist/.next/static/media/8d697b304b401681-s.woff2 +0 -0
- package/dist/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
- package/dist/.next/trace +3 -16
- package/dist/.next/types/app/api/cloudflare/route.ts +1 -1
- package/dist/.next/types/app/api/databases/route.ts +1 -1
- package/dist/.next/types/app/api/migrations/sync/route.ts +1 -1
- package/dist/.next/types/app/api/migrations/sync-files/route.ts +1 -1
- package/dist/.next/types/app/api/pull-data/confirm/route.ts +1 -1
- package/dist/.next/types/app/api/pull-remote/confirm/route.ts +1 -1
- package/dist/.next/types/app/api/pull-remote/preview/route.ts +1 -1
- package/dist/.next/types/app/api/push-remote/confirm/route.ts +1 -1
- package/dist/.next/types/app/api/push-remote/preview/route.ts +1 -1
- package/dist/.next/types/app/api/query/route.ts +1 -1
- package/dist/.next/types/app/api/tables/[name]/route.ts +1 -1
- package/dist/.next/types/app/api/tables/route.ts +1 -1
- package/dist/.next/types/app/api/update/route.ts +1 -1
- package/dist/.next/types/app/page.ts +1 -1
- package/dist/app/api/tables/[name]/route.js +22 -10
- package/dist/app/globals.css +39 -0
- package/dist/app/page.jsx +13 -23
- package/dist/d1-studio.js +34 -4
- package/dist/lib/data-type-handlers.js +426 -0
- package/dist/lib/sql-operations.js +32 -8
- package/dist/lib/sql-sanitizer.js +233 -0
- package/package.json +3 -9
- package/dist/.next/.next/cache/webpack/server-production/2.pack +0 -0
- package/dist/.next/.next/server/chunks/90.js +0 -1
- package/dist/.next/.next/static/chunks/700-dc8f9f08b166c5e6.js +0 -1
- package/dist/.next/.next/static/chunks/app/page-16ec561fc8bebb5d.js +0 -1
- package/dist/.next/.next/static/css/0c512cea5f3d317a.css +0 -3
- package/dist/.next/server/chunks/90.js +0 -1
- package/dist/.next/static/chunks/700-dc8f9f08b166c5e6.js +0 -1
- package/dist/.next/static/chunks/app/page-16ec561fc8bebb5d.js +0 -1
- package/dist/.next/static/css/0c512cea5f3d317a.css +0 -3
- package/dist/app/app/api/cloudflare/route.js +0 -150
- package/dist/app/app/api/databases/route.js +0 -71
- package/dist/app/app/api/migrations/sync/route.js +0 -174
- package/dist/app/app/api/migrations/sync-files/route.js +0 -172
- package/dist/app/app/api/pull-data/confirm/route.js +0 -230
- package/dist/app/app/api/pull-remote/confirm/route.js +0 -278
- package/dist/app/app/api/pull-remote/preview/route.js +0 -144
- package/dist/app/app/api/push-remote/confirm/route.js +0 -196
- package/dist/app/app/api/push-remote/preview/route.js +0 -179
- package/dist/app/app/api/query/route.js +0 -168
- package/dist/app/app/api/tables/[name]/route.js +0 -174
- package/dist/app/app/api/tables/route.js +0 -178
- package/dist/app/app/api/update/route.js +0 -45
- package/dist/app/app/globals.css +0 -93
- package/dist/app/app/layout.jsx +0 -35
- package/dist/app/app/page.jsx +0 -44
- package/next.config.mjs +0 -18
- package/postcss.config.mjs +0 -8
- package/scripts/studio.js +0 -169
- package/scripts/test.js +0 -366
- package/src/app/api/cloudflare/route.js +0 -150
- package/src/app/api/databases/route.js +0 -71
- package/src/app/api/migrations/sync/route.js +0 -174
- package/src/app/api/migrations/sync-files/route.js +0 -172
- package/src/app/api/pull-data/confirm/route.js +0 -230
- package/src/app/api/pull-remote/confirm/route.js +0 -278
- package/src/app/api/pull-remote/preview/route.js +0 -144
- package/src/app/api/push-remote/confirm/route.js +0 -196
- package/src/app/api/push-remote/preview/route.js +0 -179
- package/src/app/api/query/route.js +0 -168
- package/src/app/api/tables/[name]/route.js +0 -174
- package/src/app/api/tables/route.js +0 -178
- package/src/app/api/update/route.js +0 -45
- package/src/app/globals.css +0 -93
- package/src/app/layout.jsx +0 -35
- package/src/app/page.jsx +0 -44
- package/src/components/cloudflare-credentials-modal.jsx +0 -94
- package/src/components/column-selector.jsx +0 -133
- package/src/components/column-sorting.jsx +0 -65
- package/src/components/delete-records-dialog.jsx +0 -64
- package/src/components/download-button.jsx +0 -205
- package/src/components/edit-error-alert.jsx +0 -30
- package/src/components/editable-cell.jsx +0 -163
- package/src/components/editable-row.jsx +0 -169
- package/src/components/environment-handler.jsx +0 -332
- package/src/components/environment-select.jsx +0 -62
- package/src/components/pull-preview-toast.jsx +0 -215
- package/src/components/push-error-alert.jsx +0 -72
- package/src/components/push-preview-toast.jsx +0 -183
- package/src/components/query-results.jsx +0 -112
- package/src/components/refresh-button.jsx +0 -61
- package/src/components/resize-columns.jsx +0 -101
- package/src/components/sidebar.jsx +0 -254
- package/src/components/sql-editor.jsx +0 -411
- package/src/components/sql-error-display.jsx +0 -167
- package/src/components/sync-migrations-button.jsx +0 -87
- package/src/components/table-error-dialog.jsx +0 -65
- package/src/components/table-toolbar.jsx +0 -285
- package/src/components/table-view.jsx +0 -843
- package/src/components/ui/alert.jsx +0 -47
- package/src/components/ui/button.jsx +0 -48
- package/src/components/ui/card.jsx +0 -50
- package/src/components/ui/checkbox.jsx +0 -24
- package/src/components/ui/collapsible.jsx +0 -11
- package/src/components/ui/dialog.jsx +0 -96
- package/src/components/ui/dropdown-menu.jsx +0 -158
- package/src/components/ui/input.jsx +0 -19
- package/src/components/ui/scroll-area.jsx +0 -40
- package/src/components/ui/select.jsx +0 -121
- package/src/components/ui/separator.jsx +0 -25
- package/src/components/ui/sheet.jsx +0 -109
- package/src/components/ui/sidebar.jsx +0 -620
- package/src/components/ui/skeleton.jsx +0 -14
- package/src/components/ui/table.jsx +0 -86
- package/src/components/ui/tabs.jsx +0 -43
- package/src/components/ui/toast.jsx +0 -86
- package/src/components/ui/toaster.jsx +0 -35
- package/src/components/ui/tooltip.jsx +0 -28
- package/src/components/update-banner.jsx +0 -93
- package/src/context/database-context.js +0 -510
- package/src/hooks/use-mobile.jsx +0 -19
- package/src/hooks/use-sql-error.ts +0 -23
- package/src/hooks/use-toast.js +0 -155
- package/src/lib/database-queue.js +0 -37
- package/src/lib/error.js +0 -20
- package/src/lib/find-wrangler-config.js +0 -74
- package/src/lib/license.js +0 -99
- package/src/lib/logger.js +0 -58
- package/src/lib/migrations.js +0 -415
- package/src/lib/parse-sql-error.js +0 -200
- package/src/lib/request-deduplication.js +0 -24
- package/src/lib/schema-operations.js +0 -106
- package/src/lib/sql-error-types.js +0 -14
- package/src/lib/sql-operations.js +0 -647
- package/src/lib/utils.js +0 -6
- package/src/lib/version-utils.js +0 -34
- /package/dist/.next/.next/static/{5dBGGUmK5q7of4h5Rahrg → Y9mYu4WBghepP5oT_3OdR}/_buildManifest.js +0 -0
- /package/dist/.next/.next/static/{5dBGGUmK5q7of4h5Rahrg → Y9mYu4WBghepP5oT_3OdR}/_ssgManifest.js +0 -0
- /package/dist/.next/.next/static/{PflNFnmFsNegSqhzjI6lA → ae3gNmoSGm-R5LJC4UagI}/_buildManifest.js +0 -0
- /package/dist/.next/.next/static/{PflNFnmFsNegSqhzjI6lA → ae3gNmoSGm-R5LJC4UagI}/_ssgManifest.js +0 -0
- /package/dist/.next/.next/static/{p8875OZM4iDNsPzmoyLe7 → bKfUJeuvAQlBmR3TjrfkH}/_buildManifest.js +0 -0
- /package/dist/.next/.next/static/{p8875OZM4iDNsPzmoyLe7 → bKfUJeuvAQlBmR3TjrfkH}/_ssgManifest.js +0 -0
- /package/dist/.next/{static 2/5dBGGUmK5q7of4h5Rahrg → .next/static/ejYPqJ2kVicVp5EFWOZIC}/_buildManifest.js +0 -0
- /package/dist/.next/{static 2/5dBGGUmK5q7of4h5Rahrg → .next/static/ejYPqJ2kVicVp5EFWOZIC}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Type Handlers for Enhanced Database Operations
|
|
3
|
+
* Provides validation, formatting, and display logic for various data types
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* UUID validation regex - supports all UUID versions (1-5)
|
|
8
|
+
*/
|
|
9
|
+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Data type handlers with validation, formatting, and display logic
|
|
13
|
+
*/
|
|
14
|
+
export const DataTypeHandlers = {
|
|
15
|
+
uuid: {
|
|
16
|
+
validate: (value) => {
|
|
17
|
+
if (!value || typeof value !== 'string') return false;
|
|
18
|
+
return UUID_REGEX.test(value);
|
|
19
|
+
},
|
|
20
|
+
format: (value) => {
|
|
21
|
+
if (!value) return null;
|
|
22
|
+
return value.toLowerCase().trim();
|
|
23
|
+
},
|
|
24
|
+
display: (value) => {
|
|
25
|
+
if (!value) return '';
|
|
26
|
+
if (value.length <= 16) return value;
|
|
27
|
+
return value.substring(0, 8) + '...' + value.substring(value.length - 8);
|
|
28
|
+
},
|
|
29
|
+
editComponent: 'input',
|
|
30
|
+
editProps: {
|
|
31
|
+
type: 'text',
|
|
32
|
+
placeholder: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
|
|
33
|
+
pattern: UUID_REGEX.source,
|
|
34
|
+
title: 'Enter a valid UUID (e.g., 123e4567-e89b-12d3-a456-426614174000)'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
text: {
|
|
39
|
+
validate: (value) => {
|
|
40
|
+
return typeof value === 'string' || value === null || value === undefined;
|
|
41
|
+
},
|
|
42
|
+
format: (value) => {
|
|
43
|
+
if (value === null || value === undefined) return null;
|
|
44
|
+
return String(value);
|
|
45
|
+
},
|
|
46
|
+
display: (value) => {
|
|
47
|
+
if (!value) return '';
|
|
48
|
+
if (value.length <= 50) return value;
|
|
49
|
+
return value.substring(0, 50) + '...';
|
|
50
|
+
},
|
|
51
|
+
editComponent: 'input',
|
|
52
|
+
editProps: { type: 'text' }
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
longText: {
|
|
56
|
+
validate: (value) => {
|
|
57
|
+
return typeof value === 'string' || value === null || value === undefined;
|
|
58
|
+
},
|
|
59
|
+
format: (value) => {
|
|
60
|
+
if (value === null || value === undefined) return null;
|
|
61
|
+
return String(value);
|
|
62
|
+
},
|
|
63
|
+
display: (value) => {
|
|
64
|
+
if (!value) return '';
|
|
65
|
+
if (value.length <= 100) return value;
|
|
66
|
+
return value.substring(0, 100) + '...';
|
|
67
|
+
},
|
|
68
|
+
editComponent: 'textarea',
|
|
69
|
+
editProps: {
|
|
70
|
+
rows: 4,
|
|
71
|
+
placeholder: 'Enter long text content...'
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
json: {
|
|
76
|
+
validate: (value) => {
|
|
77
|
+
if (value === null || value === undefined) return true;
|
|
78
|
+
if (typeof value === 'string') {
|
|
79
|
+
try {
|
|
80
|
+
JSON.parse(value);
|
|
81
|
+
return true;
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return typeof value === 'object';
|
|
87
|
+
},
|
|
88
|
+
format: (value) => {
|
|
89
|
+
if (value === null || value === undefined) return null;
|
|
90
|
+
if (typeof value === 'string') {
|
|
91
|
+
try {
|
|
92
|
+
// Validate JSON by parsing, then return original string
|
|
93
|
+
JSON.parse(value);
|
|
94
|
+
return value;
|
|
95
|
+
} catch {
|
|
96
|
+
throw new Error('Invalid JSON format');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return JSON.stringify(value);
|
|
100
|
+
},
|
|
101
|
+
display: (value) => {
|
|
102
|
+
if (!value) return '';
|
|
103
|
+
const formatted = typeof value === 'string' ? value : JSON.stringify(value);
|
|
104
|
+
if (formatted.length <= 50) return formatted;
|
|
105
|
+
return formatted.substring(0, 50) + '...';
|
|
106
|
+
},
|
|
107
|
+
editComponent: 'textarea',
|
|
108
|
+
editProps: {
|
|
109
|
+
rows: 6,
|
|
110
|
+
placeholder: 'Enter valid JSON...',
|
|
111
|
+
className: 'font-mono'
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
integer: {
|
|
116
|
+
validate: (value) => {
|
|
117
|
+
if (value === null || value === undefined) return true;
|
|
118
|
+
return Number.isInteger(Number(value));
|
|
119
|
+
},
|
|
120
|
+
format: (value) => {
|
|
121
|
+
if (value === null || value === undefined) return null;
|
|
122
|
+
const num = Number(value);
|
|
123
|
+
if (!Number.isInteger(num)) {
|
|
124
|
+
throw new Error('Value must be an integer');
|
|
125
|
+
}
|
|
126
|
+
return num;
|
|
127
|
+
},
|
|
128
|
+
display: (value) => {
|
|
129
|
+
if (value === null || value === undefined) return '';
|
|
130
|
+
return String(value);
|
|
131
|
+
},
|
|
132
|
+
editComponent: 'input',
|
|
133
|
+
editProps: {
|
|
134
|
+
type: 'number',
|
|
135
|
+
step: '1',
|
|
136
|
+
pattern: '[0-9-]+'
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
real: {
|
|
141
|
+
validate: (value) => {
|
|
142
|
+
if (value === null || value === undefined) return true;
|
|
143
|
+
return !isNaN(Number(value)) && isFinite(Number(value));
|
|
144
|
+
},
|
|
145
|
+
format: (value) => {
|
|
146
|
+
if (value === null || value === undefined) return null;
|
|
147
|
+
const num = Number(value);
|
|
148
|
+
if (isNaN(num) || !isFinite(num)) {
|
|
149
|
+
throw new Error('Value must be a valid number');
|
|
150
|
+
}
|
|
151
|
+
return num;
|
|
152
|
+
},
|
|
153
|
+
display: (value) => {
|
|
154
|
+
if (value === null || value === undefined) return '';
|
|
155
|
+
return String(value);
|
|
156
|
+
},
|
|
157
|
+
editComponent: 'input',
|
|
158
|
+
editProps: {
|
|
159
|
+
type: 'number',
|
|
160
|
+
step: 'any'
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
boolean: {
|
|
165
|
+
validate: (value) => {
|
|
166
|
+
if (value === null || value === undefined) return true;
|
|
167
|
+
return typeof value === 'boolean' || value === 'true' || value === 'false' || value === 1 || value === 0;
|
|
168
|
+
},
|
|
169
|
+
format: (value) => {
|
|
170
|
+
if (value === null || value === undefined) return null;
|
|
171
|
+
if (typeof value === 'boolean') return value;
|
|
172
|
+
if (value === 'true' || value === 1) return true;
|
|
173
|
+
if (value === 'false' || value === 0) return false;
|
|
174
|
+
throw new Error('Value must be a boolean');
|
|
175
|
+
},
|
|
176
|
+
display: (value) => {
|
|
177
|
+
if (value === null || value === undefined) return '';
|
|
178
|
+
return value ? 'true' : 'false';
|
|
179
|
+
},
|
|
180
|
+
editComponent: 'select',
|
|
181
|
+
editProps: {
|
|
182
|
+
options: [
|
|
183
|
+
{ value: '', label: 'NULL' },
|
|
184
|
+
{ value: 'true', label: 'True' },
|
|
185
|
+
{ value: 'false', label: 'False' }
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
date: {
|
|
191
|
+
validate: (value) => {
|
|
192
|
+
if (value === null || value === undefined) return true;
|
|
193
|
+
return !isNaN(Date.parse(value));
|
|
194
|
+
},
|
|
195
|
+
format: (value) => {
|
|
196
|
+
if (value === null || value === undefined) return null;
|
|
197
|
+
const date = new Date(value);
|
|
198
|
+
if (isNaN(date.getTime())) {
|
|
199
|
+
throw new Error('Value must be a valid date');
|
|
200
|
+
}
|
|
201
|
+
return date.toISOString().split('T')[0];
|
|
202
|
+
},
|
|
203
|
+
display: (value) => {
|
|
204
|
+
if (!value) return '';
|
|
205
|
+
return value;
|
|
206
|
+
},
|
|
207
|
+
editComponent: 'input',
|
|
208
|
+
editProps: { type: 'date' }
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
datetime: {
|
|
212
|
+
validate: (value) => {
|
|
213
|
+
if (value === null || value === undefined) return true;
|
|
214
|
+
return !isNaN(Date.parse(value));
|
|
215
|
+
},
|
|
216
|
+
format: (value) => {
|
|
217
|
+
if (value === null || value === undefined) return null;
|
|
218
|
+
const date = new Date(value);
|
|
219
|
+
if (isNaN(date.getTime())) {
|
|
220
|
+
throw new Error('Value must be a valid datetime');
|
|
221
|
+
}
|
|
222
|
+
return date.toISOString();
|
|
223
|
+
},
|
|
224
|
+
display: (value) => {
|
|
225
|
+
if (!value) return '';
|
|
226
|
+
return new Date(value).toLocaleString();
|
|
227
|
+
},
|
|
228
|
+
editComponent: 'input',
|
|
229
|
+
editProps: { type: 'datetime-local' }
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
blob: {
|
|
233
|
+
validate: (value) => {
|
|
234
|
+
// For blob data, we typically handle as base64 strings or Buffer objects
|
|
235
|
+
if (value === null || value === undefined) return true;
|
|
236
|
+
return typeof value === 'string' || Buffer.isBuffer(value);
|
|
237
|
+
},
|
|
238
|
+
format: (value) => {
|
|
239
|
+
if (value === null || value === undefined) return null;
|
|
240
|
+
return value; // Keep as-is for blob data
|
|
241
|
+
},
|
|
242
|
+
display: (value) => {
|
|
243
|
+
if (!value) return '';
|
|
244
|
+
if (typeof value === 'string') {
|
|
245
|
+
return `<BLOB: ${value.length} chars>`;
|
|
246
|
+
}
|
|
247
|
+
if (Buffer.isBuffer(value)) {
|
|
248
|
+
return `<BLOB: ${value.length} bytes>`;
|
|
249
|
+
}
|
|
250
|
+
return '<BLOB>';
|
|
251
|
+
},
|
|
252
|
+
editComponent: 'input',
|
|
253
|
+
editProps: {
|
|
254
|
+
type: 'file',
|
|
255
|
+
accept: '*/*'
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Detects the data type of a value based on its content
|
|
262
|
+
* @param {any} value - The value to analyze
|
|
263
|
+
* @returns {string} - The detected data type
|
|
264
|
+
*/
|
|
265
|
+
export function detectDataType(value) {
|
|
266
|
+
if (value === null || value === undefined) return 'text';
|
|
267
|
+
|
|
268
|
+
if (typeof value === 'string') {
|
|
269
|
+
// Check for UUID pattern
|
|
270
|
+
if (UUID_REGEX.test(value)) return 'uuid';
|
|
271
|
+
|
|
272
|
+
// Check for JSON
|
|
273
|
+
try {
|
|
274
|
+
JSON.parse(value);
|
|
275
|
+
return 'json';
|
|
276
|
+
} catch {
|
|
277
|
+
// Not JSON, continue checking
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Check for date/datetime
|
|
281
|
+
if (!isNaN(Date.parse(value))) {
|
|
282
|
+
// Simple heuristic: if it looks like a full datetime, classify as datetime
|
|
283
|
+
if (value.includes('T') || value.includes(' ')) return 'datetime';
|
|
284
|
+
return 'date';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Check for boolean
|
|
288
|
+
if (value === 'true' || value === 'false') return 'boolean';
|
|
289
|
+
|
|
290
|
+
// Check for number
|
|
291
|
+
if (!isNaN(Number(value)) && isFinite(Number(value))) {
|
|
292
|
+
return Number.isInteger(Number(value)) ? 'integer' : 'real';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Long text heuristic
|
|
296
|
+
if (value.length > 255) return 'longText';
|
|
297
|
+
|
|
298
|
+
return 'text';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (typeof value === 'number') {
|
|
302
|
+
return Number.isInteger(value) ? 'integer' : 'real';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (typeof value === 'boolean') return 'boolean';
|
|
306
|
+
|
|
307
|
+
if (typeof value === 'object') return 'json';
|
|
308
|
+
|
|
309
|
+
if (Buffer.isBuffer(value)) return 'blob';
|
|
310
|
+
|
|
311
|
+
return 'text';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Gets the appropriate data type handler for a value
|
|
316
|
+
* @param {any} value - The value to get a handler for
|
|
317
|
+
* @returns {Object} - The data type handler
|
|
318
|
+
*/
|
|
319
|
+
export function getDataTypeHandler(value) {
|
|
320
|
+
const type = detectDataType(value);
|
|
321
|
+
return DataTypeHandlers[type] || DataTypeHandlers.text;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Validates a value against its detected or specified data type
|
|
326
|
+
* @param {any} value - The value to validate
|
|
327
|
+
* @param {string} dataType - Optional explicit data type
|
|
328
|
+
* @returns {boolean} - True if valid
|
|
329
|
+
*/
|
|
330
|
+
export function validateValue(value, dataType = null) {
|
|
331
|
+
const type = dataType || detectDataType(value);
|
|
332
|
+
const handler = DataTypeHandlers[type];
|
|
333
|
+
|
|
334
|
+
if (!handler) {
|
|
335
|
+
throw new Error(`Unknown data type: ${type}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return handler.validate(value);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Formats a value according to its data type
|
|
343
|
+
* @param {any} value - The value to format
|
|
344
|
+
* @param {string} dataType - Optional explicit data type
|
|
345
|
+
* @returns {any} - The formatted value
|
|
346
|
+
*/
|
|
347
|
+
export function formatValue(value, dataType = null) {
|
|
348
|
+
const type = dataType || detectDataType(value);
|
|
349
|
+
const handler = DataTypeHandlers[type];
|
|
350
|
+
|
|
351
|
+
if (!handler) {
|
|
352
|
+
throw new Error(`Unknown data type: ${type}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return handler.format(value);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Gets display representation of a value
|
|
360
|
+
* @param {any} value - The value to display
|
|
361
|
+
* @param {string} dataType - Optional explicit data type
|
|
362
|
+
* @returns {string} - The display string
|
|
363
|
+
*/
|
|
364
|
+
export function getDisplayValue(value, dataType = null) {
|
|
365
|
+
const type = dataType || detectDataType(value);
|
|
366
|
+
const handler = DataTypeHandlers[type];
|
|
367
|
+
|
|
368
|
+
if (!handler) {
|
|
369
|
+
return String(value || '');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return handler.display(value);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Enhanced WHERE clause builder with UUID support
|
|
377
|
+
* @param {Object} conditions - Conditions object
|
|
378
|
+
* @param {Object} options - Options for comparison operators
|
|
379
|
+
* @returns {Object} - WHERE clause and parameters
|
|
380
|
+
*/
|
|
381
|
+
export function buildEnhancedWhereClause(conditions, options = {}) {
|
|
382
|
+
if (!conditions || typeof conditions !== 'object') {
|
|
383
|
+
throw new Error('Conditions must be an object');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const entries = Object.entries(conditions);
|
|
387
|
+
if (entries.length === 0) {
|
|
388
|
+
return { whereClause: '', params: [] };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const clauses = [];
|
|
392
|
+
const params = [];
|
|
393
|
+
|
|
394
|
+
for (const [column, value] of entries) {
|
|
395
|
+
const operator = options.operators?.[column] || '=';
|
|
396
|
+
const dataType = options.dataTypes?.[column] || detectDataType(value);
|
|
397
|
+
|
|
398
|
+
// Handle different operators
|
|
399
|
+
if (operator === 'LIKE') {
|
|
400
|
+
clauses.push(`${column} LIKE ?`);
|
|
401
|
+
params.push(`%${value}%`);
|
|
402
|
+
} else if (operator === 'IN') {
|
|
403
|
+
if (!Array.isArray(value)) {
|
|
404
|
+
throw new Error(`IN operator requires an array value for column ${column}`);
|
|
405
|
+
}
|
|
406
|
+
const placeholders = value.map(() => '?').join(', ');
|
|
407
|
+
clauses.push(`${column} IN (${placeholders})`);
|
|
408
|
+
params.push(...value);
|
|
409
|
+
} else if (operator === 'BETWEEN') {
|
|
410
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
411
|
+
throw new Error(`BETWEEN operator requires an array of 2 values for column ${column}`);
|
|
412
|
+
}
|
|
413
|
+
clauses.push(`${column} BETWEEN ? AND ?`);
|
|
414
|
+
params.push(value[0], value[1]);
|
|
415
|
+
} else {
|
|
416
|
+
// Standard operators: =, !=, <, >, <=, >=
|
|
417
|
+
clauses.push(`${column} ${operator} ?`);
|
|
418
|
+
params.push(formatValue(value, dataType));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
whereClause: clauses.join(' AND '),
|
|
424
|
+
params: params
|
|
425
|
+
};
|
|
426
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { promisify } from 'util';
|
|
4
4
|
import { exec } from 'child_process';
|
|
5
|
+
import { sanitizeTableName, sanitizeColumnName, createParameterizedQuery } from './sql-sanitizer.js';
|
|
5
6
|
import logger from '@/lib/logger';
|
|
6
7
|
|
|
7
8
|
const execAsync = promisify(exec);
|
|
@@ -226,8 +227,16 @@ export function removeSQLComments(sql) {
|
|
|
226
227
|
* @returns {Promise<boolean>} True if the table exists; otherwise, false.
|
|
227
228
|
*/
|
|
228
229
|
export async function tableExists(projectDir, databaseName, env, tableName, remote = false) {
|
|
229
|
-
//
|
|
230
|
-
|
|
230
|
+
// Validate and sanitize table name to prevent SQL injection
|
|
231
|
+
try {
|
|
232
|
+
sanitizeTableName(tableName); // This will throw if invalid
|
|
233
|
+
} catch (error) {
|
|
234
|
+
logger.error('Invalid table name:', tableName, error.message);
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Query the sqlite_master table for the given table name using proper escaping
|
|
239
|
+
const query = `SELECT name FROM sqlite_master WHERE type='table' AND name = '${tableName.replace(/'/g, "''")}' COLLATE NOCASE;`;
|
|
231
240
|
|
|
232
241
|
const result = await executeSQL(projectDir, databaseName, query, env, remote);
|
|
233
242
|
try {
|
|
@@ -282,10 +291,18 @@ export async function executeSQL(projectDir, databaseName, sql, env = {}, remote
|
|
|
282
291
|
}
|
|
283
292
|
}
|
|
284
293
|
|
|
294
|
+
// Validate database name to prevent command injection
|
|
295
|
+
if (!databaseName || typeof databaseName !== 'string' || !/^[a-zA-Z0-9_-]+$/.test(databaseName)) {
|
|
296
|
+
throw new Error('Invalid database name for security reasons');
|
|
297
|
+
}
|
|
298
|
+
|
|
285
299
|
const isWindows = process.platform === 'win32';
|
|
286
300
|
const npxCommand = isWindows ? 'npx.cmd' : 'npx';
|
|
287
301
|
const executionFlag = remote ? '--remote' : '--local';
|
|
288
|
-
|
|
302
|
+
|
|
303
|
+
// Additional SQL escape for shell command execution
|
|
304
|
+
const shellEscapedSQL = cleanSQL.replace(/"/g, '\\"').replace(/\$/g, '\\$');
|
|
305
|
+
const command = `${npxCommand} wrangler d1 execute ${databaseName} ${executionFlag} --command="${shellEscapedSQL}"`;
|
|
289
306
|
|
|
290
307
|
// Only log in development
|
|
291
308
|
if (logger.shouldLog('debug')) {
|
|
@@ -357,8 +374,12 @@ export async function fetchRemoteTableData(
|
|
|
357
374
|
limit,
|
|
358
375
|
orderingColumn = "id"
|
|
359
376
|
) {
|
|
360
|
-
//
|
|
361
|
-
const
|
|
377
|
+
// Validate and sanitize inputs to prevent SQL injection
|
|
378
|
+
const sanitizedTableName = sanitizeTableName(tableName);
|
|
379
|
+
const sanitizedColumn = sanitizeColumnName(orderingColumn);
|
|
380
|
+
const safeLimit = Math.max(1, Math.min(parseInt(limit) || 50, 1000)); // Cap limit to prevent large queries
|
|
381
|
+
|
|
382
|
+
const query = `SELECT * FROM ${sanitizedTableName} ORDER BY ${sanitizedColumn} DESC LIMIT ${safeLimit};`;
|
|
362
383
|
try {
|
|
363
384
|
// Execute the SQL command on the remote database.
|
|
364
385
|
const result = await executeSQL(projectDir, databaseName, query, env, true);
|
|
@@ -393,9 +414,12 @@ export async function fetchRemoteTableData(
|
|
|
393
414
|
export function generateBulkInsertSQL(tableName, rows) {
|
|
394
415
|
if (!rows || rows.length === 0) return '';
|
|
395
416
|
|
|
396
|
-
//
|
|
417
|
+
// Validate and sanitize table name
|
|
418
|
+
const sanitizedTableName = sanitizeTableName(tableName);
|
|
419
|
+
|
|
420
|
+
// Get the list of columns from the first row object and sanitize them
|
|
397
421
|
const columns = Object.keys(rows[0]);
|
|
398
|
-
const columnList = columns.map(col =>
|
|
422
|
+
const columnList = columns.map(col => sanitizeColumnName(col)).join(', ');
|
|
399
423
|
|
|
400
424
|
// Build the values list for each row.
|
|
401
425
|
const valuesList = rows.map(row => {
|
|
@@ -408,7 +432,7 @@ export function generateBulkInsertSQL(tableName, rows) {
|
|
|
408
432
|
return `(${values})`;
|
|
409
433
|
}).join(', ');
|
|
410
434
|
|
|
411
|
-
return `INSERT INTO
|
|
435
|
+
return `INSERT INTO ${sanitizedTableName} (${columnList}) VALUES ${valuesList};`;
|
|
412
436
|
}
|
|
413
437
|
|
|
414
438
|
// ---------------------------------------------------------------------
|