Parse and unparse conflict clauses in table and column constraints.

This commit is contained in:
Sebastian Jeltsch
2025-03-26 10:22:51 +01:00
parent 814f628019
commit d189d04207
11 changed files with 569 additions and 134 deletions

349
pnpm-lock.yaml generated
View File

@@ -126,7 +126,7 @@ importers:
dependencies:
'@astrojs/tailwind':
specifier: ^5.1.5
version: 5.1.5(astro@5.5.4(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@5.8.2)(yaml@2.7.0))(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2)))(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2))
version: 5.1.5(astro@5.5.4(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@4.9.4)(yaml@2.7.0))(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4)))(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))
'@nanostores/persistent':
specifier: ^0.10.2
version: 0.10.2(nanostores@0.11.4)
@@ -135,7 +135,7 @@ importers:
version: 0.5.0(nanostores@0.11.4)(solid-js@1.9.5)
astro:
specifier: ^5.5.4
version: 5.5.4(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@5.8.2)(yaml@2.7.0)
version: 5.5.4(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@4.9.4)(yaml@2.7.0)
astro-icon:
specifier: ^1.1.5
version: 1.1.5
@@ -154,7 +154,7 @@ importers:
devDependencies:
'@astrojs/solid-js':
specifier: ^5.0.5
version: 5.0.5(@testing-library/jest-dom@6.6.3)(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(solid-devtools@0.30.1(solid-js@1.9.5)(vite@5.4.14(@types/node@22.13.11)(lightningcss@1.29.1)))(solid-js@1.9.5)(yaml@2.7.0)
version: 5.0.5(@testing-library/jest-dom@6.6.3)(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(solid-devtools@0.30.1(solid-js@1.9.5)(vite@5.4.14(@types/node@16.18.126)(lightningcss@1.29.1)))(solid-js@1.9.5)(yaml@2.7.0)
'@eslint/js':
specifier: ^9.23.0
version: 9.23.0
@@ -163,7 +163,7 @@ importers:
version: 1.2.17
'@tailwindcss/typography':
specifier: ^0.5.16
version: 0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2)))
version: 0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4)))
'@types/dateformat':
specifier: ^5.0.3
version: 5.0.3
@@ -175,10 +175,10 @@ importers:
version: 1.3.1(eslint@9.23.0(jiti@2.4.2))
eslint-plugin-solid:
specifier: ^0.14.5
version: 0.14.5(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
version: 0.14.5(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
eslint-plugin-tailwindcss:
specifier: ^3.18.0
version: 3.18.0(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2)))
version: 3.18.0(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4)))
globals:
specifier: ^16.0.0
version: 16.0.0
@@ -196,10 +196,10 @@ importers:
version: 0.33.5
tailwindcss:
specifier: ^3.4.17
version: 3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2))
version: 3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))
typescript-eslint:
specifier: ^8.27.0
version: 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
version: 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
examples/coffee-vector-search:
dependencies:
@@ -6067,6 +6067,28 @@ snapshots:
stream-replace-string: 2.0.0
zod: 3.24.2
'@astrojs/solid-js@5.0.5(@testing-library/jest-dom@6.6.3)(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(solid-devtools@0.30.1(solid-js@1.9.5)(vite@5.4.14(@types/node@16.18.126)(lightningcss@1.29.1)))(solid-js@1.9.5)(yaml@2.7.0)':
dependencies:
solid-js: 1.9.5
vite: 6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
vite-plugin-solid: 2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.5)(vite@6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0))
optionalDependencies:
solid-devtools: 0.30.1(solid-js@1.9.5)(vite@5.4.14(@types/node@16.18.126)(lightningcss@1.29.1))
transitivePeerDependencies:
- '@testing-library/jest-dom'
- '@types/node'
- jiti
- less
- lightningcss
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- yaml
'@astrojs/solid-js@5.0.5(@testing-library/jest-dom@6.6.3)(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(solid-devtools@0.30.1(solid-js@1.9.5)(vite@5.4.14(@types/node@22.13.11)(lightningcss@1.29.1)))(solid-js@1.9.5)(yaml@2.7.0)':
dependencies:
solid-js: 1.9.5
@@ -6148,6 +6170,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@astrojs/tailwind@5.1.5(astro@5.5.4(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@4.9.4)(yaml@2.7.0))(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4)))(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))':
dependencies:
astro: 5.5.4(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@4.9.4)(yaml@2.7.0)
autoprefixer: 10.4.21(postcss@8.5.3)
postcss: 8.5.3
postcss-load-config: 4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))
tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))
transitivePeerDependencies:
- ts-node
'@astrojs/tailwind@5.1.5(astro@5.5.4(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@5.8.2)(yaml@2.7.0))(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2)))(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2))':
dependencies:
astro: 5.5.4(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@5.8.2)(yaml@2.7.0)
@@ -7339,6 +7371,14 @@ snapshots:
dependencies:
tslib: 2.8.1
'@tailwindcss/typography@0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4)))':
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))
'@tailwindcss/typography@0.5.16(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2)))':
dependencies:
lodash.castarray: 4.4.0
@@ -7564,6 +7604,23 @@ snapshots:
'@types/node': 22.13.11
optional: true
'@typescript-eslint/eslint-plugin@8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4))(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
'@typescript-eslint/scope-manager': 8.27.0
'@typescript-eslint/type-utils': 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
'@typescript-eslint/utils': 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
'@typescript-eslint/visitor-keys': 8.27.0
eslint: 9.23.0(jiti@2.4.2)
graphemer: 1.4.0
ignore: 5.3.2
natural-compare: 1.4.0
ts-api-utils: 2.1.0(typescript@4.9.4)
typescript: 4.9.4
transitivePeerDependencies:
- supports-color
'@typescript-eslint/eslint-plugin@8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
dependencies:
'@eslint-community/regexpp': 4.12.1
@@ -7581,6 +7638,18 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)':
dependencies:
'@typescript-eslint/scope-manager': 8.27.0
'@typescript-eslint/types': 8.27.0
'@typescript-eslint/typescript-estree': 8.27.0(typescript@4.9.4)
'@typescript-eslint/visitor-keys': 8.27.0
debug: 4.4.0
eslint: 9.23.0(jiti@2.4.2)
typescript: 4.9.4
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
dependencies:
'@typescript-eslint/scope-manager': 8.27.0
@@ -7598,6 +7667,17 @@ snapshots:
'@typescript-eslint/types': 8.27.0
'@typescript-eslint/visitor-keys': 8.27.0
'@typescript-eslint/type-utils@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)':
dependencies:
'@typescript-eslint/typescript-estree': 8.27.0(typescript@4.9.4)
'@typescript-eslint/utils': 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
debug: 4.4.0
eslint: 9.23.0(jiti@2.4.2)
ts-api-utils: 2.1.0(typescript@4.9.4)
typescript: 4.9.4
transitivePeerDependencies:
- supports-color
'@typescript-eslint/type-utils@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
dependencies:
'@typescript-eslint/typescript-estree': 8.27.0(typescript@5.8.2)
@@ -7611,6 +7691,20 @@ snapshots:
'@typescript-eslint/types@8.27.0': {}
'@typescript-eslint/typescript-estree@8.27.0(typescript@4.9.4)':
dependencies:
'@typescript-eslint/types': 8.27.0
'@typescript-eslint/visitor-keys': 8.27.0
debug: 4.4.0
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.7.1
ts-api-utils: 2.1.0(typescript@4.9.4)
typescript: 4.9.4
transitivePeerDependencies:
- supports-color
'@typescript-eslint/typescript-estree@8.27.0(typescript@5.8.2)':
dependencies:
'@typescript-eslint/types': 8.27.0
@@ -7625,6 +7719,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)':
dependencies:
'@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
'@typescript-eslint/scope-manager': 8.27.0
'@typescript-eslint/types': 8.27.0
'@typescript-eslint/typescript-estree': 8.27.0(typescript@4.9.4)
eslint: 9.23.0(jiti@2.4.2)
typescript: 4.9.4
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)':
dependencies:
'@eslint-community/eslint-utils': 4.5.1(eslint@9.23.0(jiti@2.4.2))
@@ -7928,6 +8033,101 @@ snapshots:
valid-filename: 4.0.0
zod: 3.24.2
astro@5.5.4(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@4.9.4)(yaml@2.7.0):
dependencies:
'@astrojs/compiler': 2.11.0
'@astrojs/internal-helpers': 0.6.1
'@astrojs/markdown-remark': 6.3.1
'@astrojs/telemetry': 3.2.0
'@oslojs/encoding': 1.1.0
'@rollup/pluginutils': 5.1.4(rollup@4.36.0)
acorn: 8.14.1
aria-query: 5.3.2
axobject-query: 4.1.0
boxen: 8.0.1
ci-info: 4.2.0
clsx: 2.1.1
common-ancestor-path: 1.0.1
cookie: 1.0.2
cssesc: 3.0.0
debug: 4.4.0
deterministic-object-hash: 2.0.2
devalue: 5.1.1
diff: 5.2.0
dlv: 1.1.3
dset: 3.1.4
es-module-lexer: 1.6.0
esbuild: 0.25.1
estree-walker: 3.0.3
flattie: 1.1.1
github-slugger: 2.0.0
html-escaper: 3.0.3
http-cache-semantics: 4.1.1
js-yaml: 4.1.0
kleur: 4.1.5
magic-string: 0.30.17
magicast: 0.3.5
mrmime: 2.0.1
neotraverse: 0.6.18
p-limit: 6.2.0
p-queue: 8.1.0
package-manager-detector: 1.1.0
picomatch: 4.0.2
prompts: 2.4.2
rehype: 13.0.2
semver: 7.7.1
shiki: 3.2.1
tinyexec: 0.3.2
tinyglobby: 0.2.12
tsconfck: 3.1.5(typescript@4.9.4)
ultrahtml: 1.5.3
unist-util-visit: 5.0.0
unstorage: 1.15.0
vfile: 6.0.3
vite: 6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
vitefu: 1.0.6(vite@6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0))
xxhash-wasm: 1.1.0
yargs-parser: 21.1.1
yocto-spinner: 0.2.1
zod: 3.24.2
zod-to-json-schema: 3.24.5(zod@3.24.2)
zod-to-ts: 1.2.0(typescript@4.9.4)(zod@3.24.2)
optionalDependencies:
sharp: 0.33.5
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
- '@azure/data-tables'
- '@azure/identity'
- '@azure/keyvault-secrets'
- '@azure/storage-blob'
- '@capacitor/preferences'
- '@deno/kv'
- '@netlify/blobs'
- '@planetscale/database'
- '@types/node'
- '@upstash/redis'
- '@vercel/blob'
- '@vercel/kv'
- aws4fetch
- db0
- idb-keyval
- ioredis
- jiti
- less
- lightningcss
- rollup
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- typescript
- uploadthing
- yaml
astro@5.5.4(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(rollup@4.36.0)(typescript@5.8.2)(yaml@2.7.0):
dependencies:
'@astrojs/compiler': 2.11.0
@@ -8717,6 +8917,19 @@ snapshots:
dependencies:
eslint: 9.23.0(jiti@2.4.2)
eslint-plugin-solid@0.14.5(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4):
dependencies:
'@typescript-eslint/utils': 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
eslint: 9.23.0(jiti@2.4.2)
estraverse: 5.3.0
is-html: 2.0.0
kebab-case: 1.0.2
known-css-properties: 0.30.0
style-to-object: 1.0.8
typescript: 4.9.4
transitivePeerDependencies:
- supports-color
eslint-plugin-solid@0.14.5(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2):
dependencies:
'@typescript-eslint/utils': 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
@@ -8730,6 +8943,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-plugin-tailwindcss@3.18.0(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))):
dependencies:
fast-glob: 3.3.3
postcss: 8.5.3
tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))
eslint-plugin-tailwindcss@3.18.0(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2))):
dependencies:
fast-glob: 3.3.3
@@ -10560,6 +10779,14 @@ snapshots:
camelcase-css: 2.0.1
postcss: 8.5.3
postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4)):
dependencies:
lilconfig: 3.1.3
yaml: 2.7.0
optionalDependencies:
postcss: 8.5.3
ts-node: 10.9.2(@types/node@16.18.126)(typescript@4.9.4)
postcss-load-config@4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2)):
dependencies:
lilconfig: 3.1.3
@@ -11237,6 +11464,20 @@ snapshots:
smol-toml@1.3.1: {}
solid-devtools@0.30.1(solid-js@1.9.5)(vite@5.4.14(@types/node@16.18.126)(lightningcss@1.29.1)):
dependencies:
'@babel/core': 7.26.10
'@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.10)
'@babel/types': 7.26.10
'@solid-devtools/debugger': 0.23.4(solid-js@1.9.5)
'@solid-devtools/shared': 0.13.2(solid-js@1.9.5)
solid-js: 1.9.5
optionalDependencies:
vite: 5.4.14(@types/node@16.18.126)(lightningcss@1.29.1)
transitivePeerDependencies:
- supports-color
optional: true
solid-devtools@0.30.1(solid-js@1.9.5)(vite@5.4.14(@types/node@22.13.11)(lightningcss@1.29.1)):
dependencies:
'@babel/core': 7.26.10
@@ -11445,6 +11686,33 @@ snapshots:
dependencies:
tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2))
tailwindcss@3.4.17(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4)):
dependencies:
'@alloc/quick-lru': 5.2.0
arg: 5.0.2
chokidar: 3.6.0
didyoumean: 1.2.2
dlv: 1.1.3
fast-glob: 3.3.3
glob-parent: 6.0.2
is-glob: 4.0.3
jiti: 1.21.7
lilconfig: 3.1.3
micromatch: 4.0.8
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.1.1
postcss: 8.5.3
postcss-import: 15.1.0(postcss@8.5.3)
postcss-js: 4.0.1(postcss@8.5.3)
postcss-load-config: 4.0.2(postcss@8.5.3)(ts-node@10.9.2(@types/node@16.18.126)(typescript@4.9.4))
postcss-nested: 6.2.0(postcss@8.5.3)
postcss-selector-parser: 6.1.2
resolve: 1.22.10
sucrase: 3.35.0
transitivePeerDependencies:
- ts-node
tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.13.11)(typescript@5.8.2)):
dependencies:
'@alloc/quick-lru': 5.2.0
@@ -11541,6 +11809,10 @@ snapshots:
trough@2.2.0: {}
ts-api-utils@2.1.0(typescript@4.9.4):
dependencies:
typescript: 4.9.4
ts-api-utils@2.1.0(typescript@5.8.2):
dependencies:
typescript: 5.8.2
@@ -11598,6 +11870,10 @@ snapshots:
ts-poet: 6.11.0
ts-proto-descriptors: 2.0.0
tsconfck@3.1.5(typescript@4.9.4):
optionalDependencies:
typescript: 4.9.4
tsconfck@3.1.5(typescript@5.8.2):
optionalDependencies:
typescript: 5.8.2
@@ -11627,6 +11903,16 @@ snapshots:
dependencies:
semver: 7.7.1
typescript-eslint@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4):
dependencies:
'@typescript-eslint/eslint-plugin': 8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4))(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
'@typescript-eslint/parser': 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
'@typescript-eslint/utils': 8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@4.9.4)
eslint: 9.23.0(jiti@2.4.2)
typescript: 4.9.4
transitivePeerDependencies:
- supports-color
typescript-eslint@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2):
dependencies:
'@typescript-eslint/eslint-plugin': 8.27.0(@typescript-eslint/parser@8.27.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2)
@@ -11831,6 +12117,21 @@ snapshots:
rollup: 2.79.2
vite: 6.2.2(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.5)(vite@6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)):
dependencies:
'@babel/core': 7.26.10
'@types/babel__core': 7.20.5
babel-preset-solid: 1.9.5(@babel/core@7.26.10)
merge-anything: 5.1.7
solid-js: 1.9.5
solid-refresh: 0.6.3(solid-js@1.9.5)
vite: 6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
vitefu: 1.0.6(vite@6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0))
optionalDependencies:
'@testing-library/jest-dom': 6.6.3
transitivePeerDependencies:
- supports-color
vite-plugin-solid@2.11.6(@testing-library/jest-dom@6.6.3)(solid-js@1.9.5)(vite@6.2.2(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)):
dependencies:
'@babel/core': 7.26.10
@@ -11857,6 +12158,17 @@ snapshots:
- supports-color
- typescript
vite@5.4.14(@types/node@16.18.126)(lightningcss@1.29.1):
dependencies:
esbuild: 0.21.5
postcss: 8.5.3
rollup: 4.36.0
optionalDependencies:
'@types/node': 16.18.126
fsevents: 2.3.3
lightningcss: 1.29.1
optional: true
vite@5.4.14(@types/node@22.13.11)(lightningcss@1.29.1):
dependencies:
esbuild: 0.21.5
@@ -11868,6 +12180,18 @@ snapshots:
lightningcss: 1.29.1
optional: true
vite@6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0):
dependencies:
esbuild: 0.25.1
postcss: 8.5.3
rollup: 4.36.0
optionalDependencies:
'@types/node': 16.18.126
fsevents: 2.3.3
jiti: 2.4.2
lightningcss: 1.29.1
yaml: 2.7.0
vite@6.2.2(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0):
dependencies:
esbuild: 0.25.1
@@ -11880,6 +12204,10 @@ snapshots:
lightningcss: 1.29.1
yaml: 2.7.0
vitefu@1.0.6(vite@6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)):
optionalDependencies:
vite: 6.2.2(@types/node@16.18.126)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
vitefu@1.0.6(vite@6.2.2(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)):
optionalDependencies:
vite: 6.2.2(@types/node@22.13.11)(jiti@2.4.2)(lightningcss@1.29.1)(yaml@2.7.0)
@@ -12173,6 +12501,11 @@ snapshots:
dependencies:
zod: 3.24.2
zod-to-ts@1.2.0(typescript@4.9.4)(zod@3.24.2):
dependencies:
typescript: 4.9.4
zod: 3.24.2
zod-to-ts@1.2.0(typescript@5.8.2)(zod@3.24.2):
dependencies:
typescript: 5.8.2

View File

@@ -1,5 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ConflictResolution } from "./ConflictResolution";
import type { GeneratedExpressionMode } from "./GeneratedExpressionMode";
import type { ReferentialAction } from "./ReferentialAction";
export type ColumnOption = "Null" | "NotNull" | { "Default": string } | { "Unique": { is_primary: boolean, } } | { "ForeignKey": { foreign_table: string, referred_columns: Array<string>, on_delete: ReferentialAction | null, on_update: ReferentialAction | null, } } | { "Check": string } | { "OnUpdate": string } | { "Generated": { expr: string, mode: GeneratedExpressionMode | null, } };
export type ColumnOption = "Null" | "NotNull" | { "Default": string } | { "Unique": { is_primary: boolean, conflict_clause: ConflictResolution | null, } } | { "ForeignKey": { foreign_table: string, referred_columns: Array<string>, on_delete: ReferentialAction | null, on_update: ReferentialAction | null, } } | { "Check": string } | { "OnUpdate": string } | { "Generated": { expr: string, mode: GeneratedExpressionMode | null, } };

View File

@@ -0,0 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Conflict resolution types
*/
export type ConflictResolution = "Rollback" | "Abort" | "Fail" | "Ignore" | "Replace";

View File

@@ -1,8 +1,11 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ConflictResolution } from "./ConflictResolution";
export type UniqueConstraint = { name: string | null,
/**
* Identifiers of the columns that are unique.
* TODO: Should be indexed/ordered column.
*
* TODO: Should be indexed/ordered column, e.g. ASC/DESC:
* https://www.sqlite.org/syntax/indexed-column.html
*/
columns: Array<string>, };
columns: Array<string>, conflict_clause: ConflictResolution | null, };

View File

@@ -415,7 +415,9 @@ function ColumnOptionsFields(props: {
props.onChange(
setUnique(
props.value,
value ? { is_primary: false } : undefined,
value
? { is_primary: false, conflict_clause: null }
: undefined,
),
);
}}
@@ -724,7 +726,7 @@ export const primaryKeyPresets: [string, (colName: string) => Preset][] = [
return {
data_type: "Blob",
options: [
{ Unique: { is_primary: true } },
{ Unique: { is_primary: true, conflict_clause: null } },
{ Check: `is_uuid_v7(${colName})` },
{ Default: "(uuid_v7())" },
"NotNull",
@@ -737,7 +739,10 @@ export const primaryKeyPresets: [string, (colName: string) => Preset][] = [
(_colName: string) => {
return {
data_type: "Integer",
options: [{ Unique: { is_primary: true } }, "NotNull"],
options: [
{ Unique: { is_primary: true, conflict_clause: null } },
"NotNull",
],
};
},
],

View File

@@ -1,6 +1,7 @@
import type { Column } from "@bindings/Column";
import type { ColumnOption } from "@bindings/ColumnOption";
import type { ReferentialAction } from "@bindings/ReferentialAction";
import type { ConflictResolution } from "@bindings/ConflictResolution";
import type { Table } from "@bindings/Table";
import type { View } from "@bindings/View";
@@ -116,7 +117,10 @@ export function setForeignKey(
return newOpts;
}
export type Unique = { is_primary: boolean };
export type Unique = {
is_primary: boolean;
conflict_clause: ConflictResolution | null;
};
export function getUnique(options: ColumnOption[]): Unique | undefined {
return options.reduce<Unique | undefined>((acc, cur: ColumnOption) => {

View File

@@ -133,7 +133,10 @@ mod tests {
name: pk_col.clone(),
data_type: ColumnDataType::Blob,
options: vec![
ColumnOption::Unique { is_primary: true },
ColumnOption::Unique {
is_primary: true,
conflict_clause: None,
},
ColumnOption::Check(format!("(is_uuid_v7({pk_col}))")),
ColumnOption::Default("(uuid_v7())".to_string()),
],

View File

@@ -154,7 +154,10 @@ mod tests {
columns: vec![Column {
name: pk_col.clone(),
data_type: ColumnDataType::Blob,
options: vec![ColumnOption::Unique { is_primary: true }],
options: vec![ColumnOption::Unique {
is_primary: true,
conflict_clause: None,
}],
}],
foreign_keys: vec![],
unique: vec![],

View File

@@ -662,7 +662,7 @@ impl InsertQueryBuilder {
let query = if !column_names.is_empty() {
format!(
r#"INSERT {conflict_clause} INTO "{table_name}" ({col_names}) VALUES ({placeholders}) {return_fragment}"#,
col_names = Self::build_col_names(column_names),
col_names = crate::schema::quote(column_names),
placeholders = params.placeholders(),
)
} else {
@@ -673,21 +673,6 @@ impl InsertQueryBuilder {
return Ok((query, params.named_params, params.files));
}
#[inline]
fn build_col_names(column_names: &[String]) -> String {
let mut s = String::new();
for (i, name) in column_names.iter().enumerate() {
if i > 0 {
s.push_str(", \"");
} else {
s.push('"');
}
s.push_str(name);
s.push('"');
}
return s;
}
#[inline]
fn conflict_resolution_clause(config: ConflictResolutionStrategy) -> &'static str {
type C = ConflictResolutionStrategy;

View File

@@ -2,8 +2,8 @@ use itertools::Itertools;
use log::*;
use serde::{Deserialize, Serialize};
use sqlite3_parser::ast::{
fmt::ToTokens, ColumnDefinition, CreateTableBody, Expr, FromClause, Literal, Name, SelectTable,
Stmt, TableConstraint, TableOptions,
fmt::ToTokens, ColumnDefinition, CreateTableBody, Expr, FromClause, Literal, Name, QualifiedName,
SelectTable, Stmt, TableConstraint, TableOptions,
};
use std::collections::HashMap;
use thiserror::Error;
@@ -36,33 +36,38 @@ pub struct ForeignKey {
pub columns: Vec<String>,
pub foreign_table: String,
pub referred_columns: Vec<String>,
// Only "ON DELETE" and "ON UPDATE" are supported in foreign key clause, i.e. no "ON INSERT":
// https://www.sqlite.org/syntax/foreign-key-clause.html
pub on_delete: Option<ReferentialAction>,
pub on_update: Option<ReferentialAction>,
// TODO: Missing DEFERRABLE.
}
impl ForeignKey {
fn to_fragment(&self) -> String {
return format!(
"{name} FOREIGN KEY ({cols}) REFERENCES '{foreign_table}'{ref_col} {on_delete} {on_update}",
name = self
.name
.as_ref()
.map_or_else(|| "".to_string(), |n| format!("CONSTRAINT {n}")),
cols = quote(&self.columns),
foreign_table = self.foreign_table,
ref_col = match self.referred_columns.len() {
0 => "".to_string(),
_ => format!("({})", quote(&self.referred_columns)),
},
on_delete = self.on_delete.as_ref().map_or_else(
|| "".to_string(),
|action| format!("ON DELETE {}", action.to_fragment())
),
on_update = self.on_update.as_ref().map_or_else(
|| "".to_string(),
|action| format!("ON UPDATE {}", action.to_fragment())
),
let cols = quote(&self.columns);
let foreign_table = &self.foreign_table;
let ref_col = match self.referred_columns.len() {
0 => "".to_string(),
_ => format!("({})", quote(&self.referred_columns)),
};
let on_delete = self.on_delete.as_ref().map_or_else(
|| "".to_string(),
|action| format!("ON DELETE {}", action.to_fragment()),
);
let on_update = self.on_update.as_ref().map_or_else(
|| "".to_string(),
|action| format!("ON UPDATE {}", action.to_fragment()),
);
return if let Some(ref name) = self.name {
format!(
"CONSTRAINT '{name}' FOREIGN KEY ({cols}) REFERENCES '{foreign_table}'{ref_col} {on_delete} {on_update}")
} else {
format!("FOREIGN KEY ({cols}) REFERENCES '{foreign_table}'{ref_col} {on_delete} {on_update}")
};
}
}
@@ -71,22 +76,31 @@ impl ForeignKey {
#[derive(Clone, Debug, Serialize, Deserialize, TS, PartialEq)]
pub struct UniqueConstraint {
pub name: Option<String>,
/// Identifiers of the columns that are unique.
/// TODO: Should be indexed/ordered column.
///
/// TODO: Should be indexed/ordered column, e.g. ASC/DESC:
/// https://www.sqlite.org/syntax/indexed-column.html
pub columns: Vec<String>,
pub conflict_clause: Option<ConflictResolution>,
}
impl UniqueConstraint {
fn to_fragment(&self) -> String {
return format!(
"{name}UNIQUE ({cols}) {conflict_clause}",
name = self
.name
.as_ref()
.map_or_else(|| "".to_string(), |n| format!("CONSTRAINT '{n}' ")),
cols = quote(&self.columns),
conflict_clause = "",
);
let cols = quote(&self.columns);
return match (self.name.as_ref(), &self.conflict_clause.as_ref()) {
(Some(name), Some(resolution)) => format!(
"CONSTRAINT '{name}' UNIQUE ({cols}) ON CONFLICT {}",
resolution.to_fragment()
),
(Some(name), None) => format!("CONSTRAINT '{name}' UNIQUE ({cols})"),
(None, Some(resolution)) => {
format!("UNIQUE ({cols}) ON CONFLICT {}", resolution.to_fragment())
}
(None, None) => format!("UNIQUE ({cols})"),
};
}
}
@@ -97,6 +111,47 @@ pub struct ColumnOrder {
pub nulls_first: Option<bool>,
}
/// Conflict resolution types
#[derive(Clone, Debug, Serialize, Deserialize, TS, PartialEq)]
pub enum ConflictResolution {
/// `ROLLBACK`
Rollback,
/// `ABORT`
Abort, // default
/// `FAIL`
Fail,
/// `IGNORE`
Ignore,
/// `REPLACE`
Replace,
}
impl From<sqlite3_parser::ast::ResolveType> for ConflictResolution {
fn from(res: sqlite3_parser::ast::ResolveType) -> Self {
use sqlite3_parser::ast::ResolveType;
match res {
ResolveType::Rollback => ConflictResolution::Rollback,
ResolveType::Abort => ConflictResolution::Abort,
ResolveType::Fail => ConflictResolution::Fail,
ResolveType::Ignore => ConflictResolution::Ignore,
ResolveType::Replace => ConflictResolution::Replace,
}
}
}
impl ConflictResolution {
// https://www.sqlite.org/syntax/conflict-clause.html
fn to_fragment(&self) -> &'static str {
return match self {
Self::Rollback => "ROLLBACK",
Self::Abort => "ABORT",
Self::Fail => "FAIL",
Self::Ignore => "IGNORE",
Self::Replace => "REPLACE",
};
}
}
#[derive(Clone, Debug, Serialize, Deserialize, TS, PartialEq)]
pub enum ReferentialAction {
Restrict,
@@ -146,6 +201,8 @@ pub enum ColumnOption {
// NOTE: Unique { is_primary: true} means PrimaryKey.
Unique {
is_primary: bool,
conflict_clause: Option<ConflictResolution>,
// TODO: Missing ASC/DESC & AUTOINCREMENT for PK.
},
ForeignKey {
foreign_table: String,
@@ -167,13 +224,15 @@ impl ColumnOption {
Self::Null => "NULL".to_string(),
Self::NotNull => "NOT NULL".to_string(),
Self::Default(v) => format!("DEFAULT {v}"),
Self::Unique { is_primary } => {
if *is_primary {
"PRIMARY KEY".to_string()
} else {
"UNIQUE".to_string()
}
}
Self::Unique {
is_primary,
conflict_clause,
} => match (*is_primary, conflict_clause.as_ref()) {
(true, Some(res)) => format!("PRIMARY KEY ON CONFLICT {}", res.to_fragment()),
(true, None) => "PRIMARY KEY".to_string(),
(false, Some(res)) => format!("UNIQUE ON CONFLICT {}", res.to_fragment()),
(false, None) => "UNIQUE".to_string(),
},
Self::ForeignKey {
foreign_table,
referred_columns,
@@ -185,7 +244,6 @@ impl ColumnOption {
ref_col = match referred_columns.len() {
0 => "".to_string(),
_ => format!("({})", quote(referred_columns)),
//_ => format!("({})", referred_columns.join(", ")),
},
on_delete = on_delete.as_ref().map_or_else(
|| "".to_string(),
@@ -349,10 +407,9 @@ impl Column {
impl Column {
pub fn is_primary(&self) -> bool {
self
.options
.iter()
.any(|opt| matches!(opt, ColumnOption::Unique { is_primary } if *is_primary ))
self.options.iter().any(
|opt| matches!(opt, ColumnOption::Unique { is_primary, conflict_clause: _ } if *is_primary ),
)
}
}
@@ -514,10 +571,16 @@ impl TryFrom<sqlite3_parser::ast::Stmt> for Table {
TableConstraint::ForeignKey {
columns,
clause,
deref_clause: _,
deref_clause,
} => {
if let Some(ref clause) = deref_clause {
// TOOD: Parse DEFERRABLE.
log::warn!("Unsupported DEFERRABLE in FK clause: {clause:?}");
}
let mut on_delete: Option<ReferentialAction> = None;
let mut on_update: Option<ReferentialAction> = None;
for arg in &clause.args {
use sqlite3_parser::ast::RefArg;
@@ -528,21 +591,31 @@ impl TryFrom<sqlite3_parser::ast::Stmt> for Table {
RefArg::OnUpdate(action) => {
on_update = Some((*action).into());
}
_ => {}
RefArg::OnInsert(action) => {
log::error!("Unexpected ON INSERT in FK clause: {action:?}");
}
RefArg::Match(name) => {
// SQL supports FK MATCH clause, which is *not* supported by sqlite:
// https://www.sqlite.org/foreignkeys.html#fk_unsupported
log::warn!("Unsupported MATCH in FK clause: {name:?}");
}
}
}
Some(ForeignKey {
name: constraint.name.as_ref().map(|name| unquote(name.clone())),
foreign_table: unquote(clause.tbl_name.clone()),
name: constraint
.name
.as_ref()
.map(|name| unquote_name(name.clone())),
foreign_table: unquote_name(clause.tbl_name.clone()),
columns: columns
.iter()
.map(|c| unquote(c.col_name.clone()))
.map(|c| unquote_name(c.col_name.clone()))
.collect(),
referred_columns: clause.columns.as_ref().map_or_else(Vec::new, |columns| {
columns
.iter()
.map(|c| unquote(c.col_name.clone()))
.map(|c| unquote_name(c.col_name.clone()))
.collect()
}),
on_update,
@@ -558,13 +631,17 @@ impl TryFrom<sqlite3_parser::ast::Stmt> for Table {
.filter_map(|constraint| match &constraint.constraint {
TableConstraint::Unique {
columns,
conflict_clause: _,
conflict_clause,
} => Some(UniqueConstraint {
name: constraint.name.as_ref().map(|name| unquote(name.clone())),
name: constraint
.name
.as_ref()
.map(|name| unquote_name(name.clone())),
columns: columns
.iter()
.map(|c| unquote_expr(c.expr.clone()))
.collect(),
conflict_clause: conflict_clause.map(|c| c.into()),
}),
_ => None,
})
@@ -584,7 +661,7 @@ impl TryFrom<sqlite3_parser::ast::Stmt> for Table {
} = def;
assert_eq!(name, col_name);
let name = unquote(col_name);
let name = unquote_name(col_name);
assert!(!name.is_empty());
let data_type: ColumnDataType = match col_type {
@@ -605,21 +682,8 @@ impl TryFrom<sqlite3_parser::ast::Stmt> for Table {
})
.collect();
// WARN: SQLite escaping is weird, altering a table adds double quote escaping and
// sqlite3_parser unlike sqlparser, doesn't parse out the escaping.
//
// sqlite> CREATE TABLE foo (x text);
// sqlite> SELECT sql FROM main.sqlite_schema;
// CREATE TABLE foo (x text)
// sqlite> ALTER TABLE foo RENAME TO bar
// sqlite> SELECT sql FROM main.sqlite_schema;
// CREATE TABLE "bar" (x text)
//
// TODO: factor out QualifiedNamed conversion.
let table_name = unquote(tbl_name.name);
Ok(Table {
name: table_name,
name: unquote_qualified(tbl_name),
strict: options.contains(TableOptions::STRICT),
columns,
foreign_keys,
@@ -633,7 +697,7 @@ impl TryFrom<sqlite3_parser::ast::Stmt> for Table {
args: _args,
..
} => Ok(Table {
name: unquote(tbl_name.name),
name: unquote_qualified(tbl_name),
strict: false,
columns: vec![],
foreign_keys: vec![],
@@ -660,9 +724,17 @@ impl From<sqlite3_parser::ast::ColumnConstraint> for ColumnOption {
return match constraint {
Constraint::PrimaryKey {
conflict_clause: _, ..
} => ColumnOption::Unique { is_primary: true },
Constraint::Unique(_) => ColumnOption::Unique { is_primary: false },
conflict_clause,
order: _,
auto_increment: _,
} => ColumnOption::Unique {
is_primary: true,
conflict_clause: conflict_clause.map(|c| c.into()),
},
Constraint::Unique(conflict_clause) => ColumnOption::Unique {
is_primary: false,
conflict_clause: conflict_clause.map(|c| c.into()),
},
Constraint::Check(expr) => {
// NOTE: This is not using unquote on purpose, since this is not an identifier.
ColumnOption::Check(expr.to_string())
@@ -671,8 +743,11 @@ impl From<sqlite3_parser::ast::ColumnConstraint> for ColumnOption {
let columns = clause.columns.unwrap_or(vec![]);
ColumnOption::ForeignKey {
foreign_table: unquote(clause.tbl_name),
referred_columns: columns.into_iter().map(|c| unquote(c.col_name)).collect(),
foreign_table: unquote_name(clause.tbl_name),
referred_columns: columns
.into_iter()
.map(|c| unquote_name(c.col_name))
.collect(),
on_delete: None,
on_update: None,
}
@@ -714,8 +789,8 @@ impl TryFrom<sqlite3_parser::ast::Stmt> for TableIndex {
columns,
where_clause,
} => Ok(TableIndex {
name: unquote(idx_name.name),
table_name: unquote(tbl_name),
name: unquote_name(idx_name.name),
table_name: unquote_name(tbl_name),
columns: columns
.into_iter()
.map(|order_expr| ColumnOrder {
@@ -774,7 +849,7 @@ impl View {
};
Ok(View {
name: unquote(view_name.name),
name: unquote_qualified(view_name),
columns,
query: SelectFormatter(*select).to_string(),
temporary,
@@ -788,15 +863,12 @@ impl View {
}
}
fn to_entry(
qn: sqlite3_parser::ast::QualifiedName,
alias: Option<sqlite3_parser::ast::As>,
) -> (String, String) {
fn to_entry(qn: QualifiedName, alias: Option<sqlite3_parser::ast::As>) -> (String, String) {
return (
alias
.and_then(|alias| {
if let sqlite3_parser::ast::As::As(name) = alias {
return Some(unquote(name));
return Some(unquote_name(name));
}
None
})
@@ -917,7 +989,7 @@ fn try_extract_column_mapping(
}
}
ResultColumn::TableStar(name) => {
let name = unquote(name);
let name = unquote_name(name);
let Some(table_name) = table_names.get(&name) else {
return Err(SchemaError::Precondition(
format!("Missing alias: {name}").into(),
@@ -947,7 +1019,7 @@ fn try_extract_column_mapping(
let name = alias
.and_then(|alias| {
if let sqlite3_parser::ast::As::As(name) = alias {
return Some(unquote(name));
return Some(unquote_name(name));
}
None
})
@@ -966,8 +1038,8 @@ fn try_extract_column_mapping(
});
}
Expr::Qualified(qualifier, name) => {
let qualifier = unquote(qualifier);
let col_name = unquote(name);
let qualifier = unquote_name(qualifier);
let col_name = unquote_name(name);
let Some(table_name) = table_names.get(&qualifier) else {
return Err(SchemaError::Precondition(
@@ -985,7 +1057,7 @@ fn try_extract_column_mapping(
let name = alias
.and_then(|alias| {
if let sqlite3_parser::ast::As::As(name) = alias {
return Some(unquote(name));
return Some(unquote_name(name));
}
None
})
@@ -1017,7 +1089,7 @@ fn try_extract_column_mapping(
let Some(name) = alias.and_then(|alias| {
if let sqlite3_parser::ast::As::As(name) = alias {
return Some(unquote(name));
return Some(unquote_name(name));
}
None
}) else {
@@ -1047,7 +1119,8 @@ fn try_extract_column_mapping(
return Ok(Some(mapping));
}
fn quote(column_names: &[String]) -> String {
#[inline]
pub(crate) fn quote(column_names: &[String]) -> String {
let mut s = String::new();
for (i, name) in column_names.iter().enumerate() {
if i > 0 {
@@ -1077,17 +1150,22 @@ fn unquote_string(s: String) -> String {
};
}
fn unquote(name: Name) -> String {
fn unquote_name(name: Name) -> String {
return unquote_string(name.0);
}
fn unquote_qualified(name: QualifiedName) -> String {
// FIXME: unquoting of qualified name.
return unquote_name(name.name);
}
fn unquote_id(id: sqlite3_parser::ast::Id) -> String {
return unquote_string(id.0);
}
fn unquote_expr(expr: Expr) -> String {
return match expr {
Expr::Name(n) => unquote(n),
Expr::Name(n) => unquote_name(n),
Expr::Id(id) => unquote_id(id),
Expr::Literal(Literal::String(s)) => unquote_string(s),
x => x.to_string(),
@@ -1099,11 +1177,18 @@ mod tests {
use super::*;
use crate::table_metadata::sqlite3_parse_into_statement;
#[test]
fn test_quote() {
assert_eq!("", quote(&vec![]));
assert_eq!("''", quote(&vec!["".to_string()]));
assert_eq!("'foo', ''", quote(&vec!["foo".to_string(), "".to_string()]));
}
#[test]
fn test_unquote() {
assert_eq!(unquote(Name("".to_string())), "");
assert_eq!(unquote(Name("['``']".to_string())), "'``'");
assert_eq!(unquote(Name("\"[]\"".to_string())), "[]");
assert_eq!(unquote_name(Name("".to_string())), "");
assert_eq!(unquote_name(Name("['``']".to_string())), "'``'");
assert_eq!(unquote_name(Name("\"[]\"".to_string())), "[]");
}
#[test]
@@ -1142,7 +1227,7 @@ mod tests {
user_id BLOB,
email TEXT NOT NULL,
email_visibility INTEGER DEFAULT FALSE NOT NULL,
username TEXT,
username TEXT UNIQUE ON CONFLICT ABORT,
age INTEGER,
double_age INTEGER GENERATED ALWAYS AS (2 * 'age') VIRTUAL,
triple_age INTEGER AS (3 * age) STORED,
@@ -1150,8 +1235,9 @@ mod tests {
[index] TEXT,
UNIQUE (email),
UNIQUE ([index]) ON CONFLICT FAIL,
CONSTRAINT optional_tbl_constraint_name CHECK(username != ''),
-- optional constraint name:
CONSTRAINT `unique` UNIQUE ([index]) ON CONFLICT FAIL,
CHECK(username != ''),
FOREIGN KEY(user_id) REFERENCES 'table'('index') ON DELETE CASCADE
) STRICT;
"#
@@ -1243,7 +1329,10 @@ mod tests {
name: "user".to_string(),
data_type: ColumnDataType::Blob,
options: vec![
ColumnOption::Unique { is_primary: true },
ColumnOption::Unique {
is_primary: true,
conflict_clause: None,
},
ColumnOption::ForeignKey {
foreign_table: "_user".to_string(),
referred_columns: vec!["id".to_string()],
@@ -1270,7 +1359,10 @@ mod tests {
Column {
name: "id".to_string(),
data_type: ColumnDataType::Blob,
options: vec![ColumnOption::Unique { is_primary: true }],
options: vec![ColumnOption::Unique {
is_primary: true,
conflict_clause: None,
}],
},
Column {
name: "author".to_string(),

View File

@@ -386,7 +386,7 @@ fn find_user_id_foreign_key_columns(columns: &[Column]) -> Vec<usize> {
fn find_record_pk_column_index(columns: &[Column], tables: &[Table]) -> Option<usize> {
let primary_key_col_index = columns.iter().position(|col| {
for opt in &col.options {
if let ColumnOption::Unique { is_primary } = opt {
if let ColumnOption::Unique { is_primary, .. } = opt {
return *is_primary;
}
}
@@ -435,7 +435,7 @@ fn find_record_pk_column_index(columns: &[Column], tables: &[Table]) -> Option<u
ColumnOption::Check(expr) if UUID_V7_RE.is_match(expr) => {
return Some(index);
}
ColumnOption::Unique { is_primary } if *is_primary => {
ColumnOption::Unique { is_primary, .. } if *is_primary => {
is_pk = true;
}
_ => {}
@@ -823,7 +823,7 @@ pub(crate) fn build_json_schema_recursive(
}
}
}
ColumnOption::Unique { is_primary } => {
ColumnOption::Unique { is_primary, .. } => {
// According to the SQL standard, PRIMARY KEY should always imply NOT NULL.
// Unfortunately, due to a bug in some early versions, this is not the case in SQLite.
// Unless the column is an INTEGER PRIMARY KEY or the table is a WITHOUT ROWID table or a