effect-qb 0.15.0 → 0.17.0

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.
Files changed (88) hide show
  1. package/dist/mysql.js +1957 -595
  2. package/dist/postgres/metadata.js +2507 -182
  3. package/dist/postgres.js +9587 -8201
  4. package/dist/sqlite.js +8360 -0
  5. package/package.json +7 -2
  6. package/src/internal/column-state.ts +7 -0
  7. package/src/internal/column.ts +22 -0
  8. package/src/internal/derived-table.ts +29 -3
  9. package/src/internal/dialect.ts +14 -1
  10. package/src/internal/dsl-mutation-runtime.ts +173 -4
  11. package/src/internal/dsl-plan-runtime.ts +165 -20
  12. package/src/internal/dsl-query-runtime.ts +60 -6
  13. package/src/internal/dsl-transaction-ddl-runtime.ts +72 -2
  14. package/src/internal/executor.ts +62 -13
  15. package/src/internal/expression-ast.ts +3 -2
  16. package/src/internal/grouping-key.ts +141 -1
  17. package/src/internal/implication-runtime.ts +2 -1
  18. package/src/internal/json/types.ts +155 -40
  19. package/src/internal/predicate/analysis.ts +103 -1
  20. package/src/internal/predicate/atom.ts +7 -0
  21. package/src/internal/predicate/context.ts +170 -17
  22. package/src/internal/predicate/key.ts +64 -2
  23. package/src/internal/predicate/normalize.ts +115 -34
  24. package/src/internal/predicate/runtime.ts +144 -13
  25. package/src/internal/query.ts +563 -103
  26. package/src/internal/renderer.ts +39 -2
  27. package/src/internal/runtime/driver-value-mapping.ts +244 -0
  28. package/src/internal/runtime/normalize.ts +62 -38
  29. package/src/internal/runtime/schema.ts +5 -3
  30. package/src/internal/runtime/value.ts +153 -30
  31. package/src/internal/scalar.ts +11 -0
  32. package/src/internal/table-options.ts +108 -1
  33. package/src/internal/table.ts +87 -29
  34. package/src/mysql/column.ts +19 -2
  35. package/src/mysql/datatypes/index.ts +21 -0
  36. package/src/mysql/errors/catalog.ts +5 -5
  37. package/src/mysql/errors/normalize.ts +2 -2
  38. package/src/mysql/executor.ts +20 -5
  39. package/src/mysql/internal/dialect.ts +12 -6
  40. package/src/mysql/internal/dsl.ts +995 -263
  41. package/src/mysql/internal/renderer.ts +13 -3
  42. package/src/mysql/internal/sql-expression-renderer.ts +530 -128
  43. package/src/mysql/query.ts +9 -2
  44. package/src/mysql/renderer.ts +7 -2
  45. package/src/mysql/table.ts +38 -12
  46. package/src/postgres/cast.ts +22 -7
  47. package/src/postgres/column.ts +5 -2
  48. package/src/postgres/errors/normalize.ts +2 -2
  49. package/src/postgres/executor.ts +68 -10
  50. package/src/postgres/function/core.ts +19 -1
  51. package/src/postgres/internal/dialect.ts +12 -6
  52. package/src/postgres/internal/dsl.ts +958 -288
  53. package/src/postgres/internal/renderer.ts +13 -3
  54. package/src/postgres/internal/schema-ddl.ts +2 -1
  55. package/src/postgres/internal/schema-model.ts +6 -3
  56. package/src/postgres/internal/sql-expression-renderer.ts +477 -96
  57. package/src/postgres/json.ts +57 -17
  58. package/src/postgres/query.ts +9 -2
  59. package/src/postgres/renderer.ts +7 -2
  60. package/src/postgres/schema-management.ts +91 -4
  61. package/src/postgres/schema.ts +1 -1
  62. package/src/postgres/table.ts +189 -53
  63. package/src/postgres/type.ts +4 -0
  64. package/src/sqlite/column.ts +128 -0
  65. package/src/sqlite/datatypes/index.ts +79 -0
  66. package/src/sqlite/datatypes/spec.ts +98 -0
  67. package/src/sqlite/errors/catalog.ts +103 -0
  68. package/src/sqlite/errors/fields.ts +19 -0
  69. package/src/sqlite/errors/index.ts +19 -0
  70. package/src/sqlite/errors/normalize.ts +229 -0
  71. package/src/sqlite/errors/requirements.ts +71 -0
  72. package/src/sqlite/errors/types.ts +29 -0
  73. package/src/sqlite/executor.ts +227 -0
  74. package/src/sqlite/function/aggregate.ts +2 -0
  75. package/src/sqlite/function/core.ts +2 -0
  76. package/src/sqlite/function/index.ts +19 -0
  77. package/src/sqlite/function/string.ts +2 -0
  78. package/src/sqlite/function/temporal.ts +100 -0
  79. package/src/sqlite/function/window.ts +2 -0
  80. package/src/sqlite/internal/dialect.ts +37 -0
  81. package/src/sqlite/internal/dsl.ts +6926 -0
  82. package/src/sqlite/internal/renderer.ts +47 -0
  83. package/src/sqlite/internal/sql-expression-renderer.ts +1821 -0
  84. package/src/sqlite/json.ts +2 -0
  85. package/src/sqlite/query.ts +196 -0
  86. package/src/sqlite/renderer.ts +24 -0
  87. package/src/sqlite/table.ts +183 -0
  88. package/src/sqlite.ts +22 -0
@@ -0,0 +1,103 @@
1
+ export const sqliteErrorCategories = {
2
+ sqlite: "sqlite"
3
+ } as const
4
+
5
+ export type SqliteErrorCategory = keyof typeof sqliteErrorCategories
6
+
7
+ type SqliteErrorDescriptorShape<Symbol extends string = string> = {
8
+ readonly category: "sqlite"
9
+ readonly number: `${number}`
10
+ readonly symbol: Symbol
11
+ readonly tag: `@sqlite/${Lowercase<Symbol>}`
12
+ readonly messageTemplate: string
13
+ }
14
+
15
+ const descriptor = <Symbol extends string>(
16
+ number: `${number}`,
17
+ symbol: Symbol,
18
+ messageTemplate: string
19
+ ): SqliteErrorDescriptorShape<Symbol> => ({
20
+ category: "sqlite",
21
+ number,
22
+ symbol,
23
+ tag: `@sqlite/${symbol.toLowerCase()}` as `@sqlite/${Lowercase<Symbol>}`,
24
+ messageTemplate
25
+ })
26
+
27
+ export const sqliteErrorCatalogBySymbol = {
28
+ SQLITE_ERROR: descriptor("1", "SQLITE_ERROR", "SQL error or missing database"),
29
+ SQLITE_INTERNAL: descriptor("2", "SQLITE_INTERNAL", "Internal SQLite logic error"),
30
+ SQLITE_PERM: descriptor("3", "SQLITE_PERM", "Access permission denied"),
31
+ SQLITE_ABORT: descriptor("4", "SQLITE_ABORT", "Callback routine requested an abort"),
32
+ SQLITE_BUSY: descriptor("5", "SQLITE_BUSY", "Database file is locked"),
33
+ SQLITE_LOCKED: descriptor("6", "SQLITE_LOCKED", "Database table is locked"),
34
+ SQLITE_NOMEM: descriptor("7", "SQLITE_NOMEM", "Out of memory"),
35
+ SQLITE_READONLY: descriptor("8", "SQLITE_READONLY", "Attempt to write a readonly database"),
36
+ SQLITE_INTERRUPT: descriptor("9", "SQLITE_INTERRUPT", "Operation terminated by sqlite3_interrupt"),
37
+ SQLITE_IOERR: descriptor("10", "SQLITE_IOERR", "Disk I/O error"),
38
+ SQLITE_CORRUPT: descriptor("11", "SQLITE_CORRUPT", "Database disk image is malformed"),
39
+ SQLITE_NOTFOUND: descriptor("12", "SQLITE_NOTFOUND", "Unknown opcode"),
40
+ SQLITE_FULL: descriptor("13", "SQLITE_FULL", "Insertion failed because database is full"),
41
+ SQLITE_CANTOPEN: descriptor("14", "SQLITE_CANTOPEN", "Unable to open the database file"),
42
+ SQLITE_PROTOCOL: descriptor("15", "SQLITE_PROTOCOL", "Database lock protocol error"),
43
+ SQLITE_EMPTY: descriptor("16", "SQLITE_EMPTY", "Database is empty"),
44
+ SQLITE_SCHEMA: descriptor("17", "SQLITE_SCHEMA", "Database schema changed"),
45
+ SQLITE_TOOBIG: descriptor("18", "SQLITE_TOOBIG", "String or blob exceeds size limit"),
46
+ SQLITE_CONSTRAINT: descriptor("19", "SQLITE_CONSTRAINT", "Constraint violation"),
47
+ SQLITE_MISMATCH: descriptor("20", "SQLITE_MISMATCH", "Data type mismatch"),
48
+ SQLITE_MISUSE: descriptor("21", "SQLITE_MISUSE", "Library used incorrectly"),
49
+ SQLITE_NOLFS: descriptor("22", "SQLITE_NOLFS", "Uses OS features not supported on host"),
50
+ SQLITE_AUTH: descriptor("23", "SQLITE_AUTH", "Authorization denied"),
51
+ SQLITE_FORMAT: descriptor("24", "SQLITE_FORMAT", "Auxiliary database format error"),
52
+ SQLITE_RANGE: descriptor("25", "SQLITE_RANGE", "Bind parameter index out of range"),
53
+ SQLITE_NOTADB: descriptor("26", "SQLITE_NOTADB", "File is not a database"),
54
+ SQLITE_NOTICE: descriptor("27", "SQLITE_NOTICE", "Notifications from sqlite3_log"),
55
+ SQLITE_WARNING: descriptor("28", "SQLITE_WARNING", "Warnings from sqlite3_log"),
56
+ SQLITE_ROW: descriptor("100", "SQLITE_ROW", "sqlite3_step has another row ready"),
57
+ SQLITE_DONE: descriptor("101", "SQLITE_DONE", "sqlite3_step has finished executing"),
58
+ SQLITE_BUSY_RECOVERY: descriptor("261", "SQLITE_BUSY_RECOVERY", "Database is busy recovering"),
59
+ SQLITE_BUSY_SNAPSHOT: descriptor("517", "SQLITE_BUSY_SNAPSHOT", "Database snapshot is busy"),
60
+ SQLITE_BUSY_TIMEOUT: descriptor("773", "SQLITE_BUSY_TIMEOUT", "Blocking POSIX advisory lock timed out"),
61
+ SQLITE_CONSTRAINT_CHECK: descriptor("275", "SQLITE_CONSTRAINT_CHECK", "CHECK constraint failed"),
62
+ SQLITE_CONSTRAINT_FOREIGNKEY: descriptor("787", "SQLITE_CONSTRAINT_FOREIGNKEY", "FOREIGN KEY constraint failed"),
63
+ SQLITE_CONSTRAINT_NOTNULL: descriptor("1299", "SQLITE_CONSTRAINT_NOTNULL", "NOT NULL constraint failed"),
64
+ SQLITE_CONSTRAINT_PRIMARYKEY: descriptor("1555", "SQLITE_CONSTRAINT_PRIMARYKEY", "PRIMARY KEY constraint failed"),
65
+ SQLITE_CONSTRAINT_UNIQUE: descriptor("2067", "SQLITE_CONSTRAINT_UNIQUE", "UNIQUE constraint failed")
66
+ } as const
67
+
68
+ export type SqliteErrorSymbol = keyof typeof sqliteErrorCatalogBySymbol
69
+
70
+ export type SqliteErrorDescriptor = (typeof sqliteErrorCatalogBySymbol)[SqliteErrorSymbol]
71
+
72
+ export type SqliteErrorNumber = SqliteErrorDescriptor["number"]
73
+
74
+ export type SqliteErrorTag = SqliteErrorDescriptor["tag"]
75
+
76
+ const sqliteErrorDescriptors = Object.values(sqliteErrorCatalogBySymbol)
77
+
78
+ export const sqliteErrorCatalogByNumber = sqliteErrorDescriptors.reduce(
79
+ (acc, entry) => ({
80
+ ...acc,
81
+ [entry.number]: [...(acc[entry.number as SqliteErrorNumber] ?? []), entry]
82
+ }),
83
+ {} as Record<SqliteErrorNumber, readonly SqliteErrorDescriptor[]>
84
+ )
85
+
86
+ export const isSqliteErrorSymbol = (value: string): value is SqliteErrorSymbol =>
87
+ value in sqliteErrorCatalogBySymbol
88
+
89
+ export const isSqliteErrorNumber = (value: string): value is SqliteErrorNumber =>
90
+ value in sqliteErrorCatalogByNumber
91
+
92
+ export const getSqliteErrorDescriptor = (
93
+ symbol: SqliteErrorSymbol
94
+ ): SqliteErrorDescriptor => sqliteErrorCatalogBySymbol[symbol]
95
+
96
+ export const findSqliteErrorDescriptorsByNumber = (
97
+ number: SqliteErrorNumber
98
+ ): readonly SqliteErrorDescriptor[] => sqliteErrorCatalogByNumber[number] ?? []
99
+
100
+ export const findSqliteErrorDescriptorsByNumberLoose = (
101
+ number: string
102
+ ): readonly SqliteErrorDescriptor[] | undefined =>
103
+ isSqliteErrorNumber(number) ? sqliteErrorCatalogByNumber[number] : undefined
@@ -0,0 +1,19 @@
1
+ /** Common SQLite driver error fields surfaced by the normalizer when present. */
2
+ export interface SqliteErrorFields {
3
+ readonly code?: string
4
+ readonly errno?: number
5
+ readonly sqlState?: string
6
+ readonly sqlMessage?: string
7
+ readonly fatal?: boolean
8
+ readonly sql?: string
9
+ readonly syscall?: string
10
+ readonly address?: string
11
+ readonly port?: number
12
+ readonly hostname?: string
13
+ }
14
+
15
+ /** Rendered SQL context attached to normalized SQLite execution failures. */
16
+ export interface SqliteQueryContext {
17
+ readonly sql: string
18
+ readonly params: readonly unknown[]
19
+ }
@@ -0,0 +1,19 @@
1
+ export * from "./catalog.js"
2
+ export * from "./fields.js"
3
+ export * from "./normalize.js"
4
+ export * from "./requirements.js"
5
+ export * from "./types.js"
6
+
7
+ export {
8
+ findSqliteErrorDescriptorsByNumber as findSqliteErrorDescriptorsByNumber,
9
+ getSqliteErrorDescriptor as getSqliteErrorDescriptor,
10
+ isSqliteErrorNumber as isSqliteErrorNumber,
11
+ isSqliteErrorSymbol as isSqliteErrorSymbol
12
+ } from "./catalog.js"
13
+
14
+ export {
15
+ hasNumber as hasErrorNumber,
16
+ hasSymbol as hasErrorSymbol,
17
+ isSqliteErrorLike as isSqliteErrorLike,
18
+ normalizeSqliteDriverError as normalizeSqliteDriverError
19
+ } from "./normalize.js"
@@ -0,0 +1,229 @@
1
+ import type * as Renderer from "../../internal/renderer.js"
2
+ import {
3
+ findSqliteErrorDescriptorsByNumberLoose,
4
+ getSqliteErrorDescriptor,
5
+ isSqliteErrorNumber,
6
+ isSqliteErrorSymbol,
7
+ type SqliteErrorDescriptor,
8
+ type SqliteErrorNumber,
9
+ type SqliteErrorSymbol,
10
+ } from "./catalog.js"
11
+ import type {
12
+ SqliteErrorFields,
13
+ SqliteQueryContext
14
+ } from "./fields.js"
15
+ import type {
16
+ SqliteErrorLike,
17
+ SqliteKnownErrorBase
18
+ } from "./types.js"
19
+
20
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
21
+ typeof value === "object" && value !== null
22
+
23
+ const unwrapSqliteDriverCause = (cause: unknown): unknown => {
24
+ let current = cause
25
+ while (
26
+ isRecord(current) &&
27
+ "_tag" in current &&
28
+ current._tag === "SqlError" &&
29
+ "cause" in current
30
+ ) {
31
+ current = current.cause
32
+ }
33
+ return current
34
+ }
35
+
36
+ const asString = (value: unknown): string | undefined =>
37
+ typeof value === "string" ? value : undefined
38
+
39
+ const asBoolean = (value: unknown): boolean | undefined =>
40
+ typeof value === "boolean" ? value : undefined
41
+
42
+ const asNumber = (value: unknown): number | undefined => {
43
+ if (typeof value === "number" && Number.isFinite(value)) {
44
+ return value
45
+ }
46
+ if (typeof value === "string" && /^[+-]?\d+$/.test(value.trim())) {
47
+ const parsed = Number(value.trim())
48
+ return Number.isFinite(parsed) ? parsed : undefined
49
+ }
50
+ return undefined
51
+ }
52
+
53
+ const normalizeFields = (error: Record<string, unknown>): SqliteErrorFields => ({
54
+ code: asString(error.code),
55
+ errno: asNumber(error.errno),
56
+ sqlState: asString(error.sqlState),
57
+ sqlMessage: asString(error.sqlMessage),
58
+ fatal: asBoolean(error.fatal),
59
+ sql: asString(error.sql),
60
+ syscall: asString(error.syscall),
61
+ address: asString(error.address),
62
+ port: asNumber(error.port),
63
+ hostname: asString(error.hostname)
64
+ })
65
+
66
+ export type { SqliteErrorLike } from "./types.js"
67
+
68
+ /** Structured known SQLite error derived from the SQLite result-code catalog. */
69
+ export type KnownSqliteError<Symbol extends SqliteErrorSymbol = SqliteErrorSymbol> =
70
+ SqliteKnownErrorBase & { readonly symbol: Symbol }
71
+
72
+ /** Extracts the normalized SQLite error variant for a specific symbol. */
73
+ export type KnownSqliteErrorBySymbol<Symbol extends SqliteErrorSymbol> = KnownSqliteError<Symbol>
74
+
75
+ /** SQLite-like error whose symbol or number is not in the current catalog. */
76
+ export type UnknownSqliteCodeError = Readonly<{
77
+ readonly _tag: "@sqlite/unknown/code"
78
+ readonly code?: string
79
+ readonly errno?: string | number
80
+ readonly message: string
81
+ readonly query?: SqliteQueryContext
82
+ readonly raw: SqliteErrorLike
83
+ } & SqliteErrorFields>
84
+
85
+ /** Fallback for non-SQLite driver failures in the SQLite executor path. */
86
+ export type UnknownSqliteDriverError = Readonly<{
87
+ readonly _tag: "@sqlite/unknown/driver"
88
+ readonly message: string
89
+ readonly query?: SqliteQueryContext
90
+ readonly cause: unknown
91
+ }>
92
+
93
+ /** Any SQLite-specific driver failure surfaced by the SQLite executor. */
94
+ export type SqliteDriverError =
95
+ | KnownSqliteError
96
+ | UnknownSqliteCodeError
97
+ | UnknownSqliteDriverError
98
+
99
+ /** Runtime guard for objects that look like SQLite driver errors. */
100
+ export const isSqliteErrorLike = (value: unknown): value is SqliteErrorLike =>
101
+ isRecord(value) &&
102
+ (
103
+ typeof value.code === "string" ||
104
+ typeof value.errno === "string" ||
105
+ typeof value.errno === "number" ||
106
+ typeof value.sqlState === "string" ||
107
+ typeof value.sqlMessage === "string" ||
108
+ typeof value.message === "string" ||
109
+ typeof value.fatal === "boolean"
110
+ )
111
+
112
+ const errorMessageOf = (error: SqliteErrorLike): string =>
113
+ error.sqlMessage ?? error.message ?? "SQLite driver error"
114
+
115
+ const numberOf = (error: SqliteErrorLike): string | undefined => {
116
+ if (typeof error.errno === "number" && Number.isFinite(error.errno)) {
117
+ return String(error.errno)
118
+ }
119
+ if (typeof error.errno === "string" && error.errno.trim() !== "") {
120
+ return error.errno
121
+ }
122
+ return undefined
123
+ }
124
+
125
+ const findDescriptor = (error: SqliteErrorLike): SqliteErrorDescriptor | undefined => {
126
+ if (typeof error.code === "string" && isSqliteErrorSymbol(error.code)) {
127
+ return getSqliteErrorDescriptor(error.code)
128
+ }
129
+ if (typeof error.code === "string" && isSqliteErrorNumber(error.code)) {
130
+ const matches = findSqliteErrorDescriptorsByNumberLoose(error.code)
131
+ if (matches?.length === 1) {
132
+ return matches[0]
133
+ }
134
+ }
135
+ const number = numberOf(error)
136
+ if (number !== undefined) {
137
+ const matches = findSqliteErrorDescriptorsByNumberLoose(number)
138
+ if (!matches || matches.length === 0) {
139
+ return undefined
140
+ }
141
+ if (matches.length === 1) {
142
+ return matches[0]
143
+ }
144
+ if (typeof error.code === "string") {
145
+ return matches.find((descriptor) => descriptor.symbol === error.code)
146
+ }
147
+ }
148
+ return undefined
149
+ }
150
+
151
+ const makeKnownSqliteError = (
152
+ descriptor: SqliteErrorDescriptor,
153
+ raw: SqliteErrorLike,
154
+ query?: SqliteQueryContext
155
+ ): SqliteKnownErrorBase => {
156
+ const fields = normalizeFields(raw as Record<string, unknown>)
157
+ return {
158
+ _tag: descriptor.tag,
159
+ category: descriptor.category,
160
+ number: descriptor.number,
161
+ symbol: descriptor.symbol,
162
+ messageTemplate: descriptor.messageTemplate,
163
+ message: errorMessageOf(raw),
164
+ query,
165
+ raw,
166
+ ...fields
167
+ } as SqliteKnownErrorBase
168
+ }
169
+
170
+ /** Normalizes an unknown failure into a structured SQLite driver error. */
171
+ export const normalizeSqliteDriverError = (
172
+ cause: unknown,
173
+ query?: SqliteQueryContext | Renderer.RenderedQuery<any, "sqlite">
174
+ ): SqliteDriverError => {
175
+ const normalizedCause = unwrapSqliteDriverCause(cause)
176
+ const context = query === undefined
177
+ ? undefined
178
+ : "sql" in query
179
+ ? { sql: query.sql, params: query.params }
180
+ : query
181
+
182
+ if (!isSqliteErrorLike(normalizedCause)) {
183
+ return {
184
+ _tag: "@sqlite/unknown/driver",
185
+ message: normalizedCause instanceof Error ? normalizedCause.message : "Unknown SQLite driver failure",
186
+ query: context,
187
+ cause
188
+ } as UnknownSqliteDriverError
189
+ }
190
+
191
+ const descriptor = findDescriptor(normalizedCause)
192
+ if (descriptor !== undefined) {
193
+ return makeKnownSqliteError(descriptor, normalizedCause, context)
194
+ }
195
+
196
+ if (typeof normalizedCause.code === "string" || numberOf(normalizedCause) !== undefined) {
197
+ return {
198
+ _tag: "@sqlite/unknown/code",
199
+ code: asString(normalizedCause.code),
200
+ errno: normalizedCause.errno,
201
+ message: errorMessageOf(normalizedCause),
202
+ query: context,
203
+ raw: normalizedCause,
204
+ ...normalizeFields(normalizedCause as Record<string, unknown>)
205
+ } as UnknownSqliteCodeError
206
+ }
207
+
208
+ return {
209
+ _tag: "@sqlite/unknown/driver",
210
+ message: errorMessageOf(normalizedCause),
211
+ query: context,
212
+ cause
213
+ } as UnknownSqliteDriverError
214
+ }
215
+
216
+ /** Type guard for a specific SQLite catalog symbol. */
217
+ export const hasSymbol = <Symbol extends SqliteErrorSymbol>(
218
+ error: SqliteDriverError | { readonly symbol?: string },
219
+ symbol: Symbol
220
+ ): error is KnownSqliteErrorBySymbol<Symbol> =>
221
+ "symbol" in error && error.symbol === symbol
222
+
223
+ /** Type guard for a specific documented SQLite error number. */
224
+ export const hasNumber = <Number extends SqliteErrorNumber>(
225
+ error: SqliteDriverError | { readonly number?: string; readonly errno?: string | number },
226
+ number: Number
227
+ ): error is KnownSqliteError & { readonly number: Number } =>
228
+ ("number" in error && error.number === number) ||
229
+ ("errno" in error && String(error.errno) === number)
@@ -0,0 +1,71 @@
1
+ import { read_query_capabilities, type QueryCapability, type QueryRequirement } from "../../internal/query-requirements.js"
2
+ import type { SqliteQueryContext } from "./fields.js"
3
+ import type {
4
+ KnownSqliteError,
5
+ SqliteDriverError,
6
+ UnknownSqliteCodeError,
7
+ UnknownSqliteDriverError
8
+ } from "./normalize.js"
9
+
10
+ export type SqliteQueryRequirement = Extract<QueryRequirement, "write" | "ddl" | "transaction" | "locking">
11
+
12
+ export const sqlite_requirements_by_sqlstate_prefix = {
13
+ "23": ["write"]
14
+ } as const satisfies Partial<Record<string, readonly SqliteQueryRequirement[]>>
15
+
16
+ export type SqliteQueryRequirementsError = Readonly<{
17
+ readonly _tag: "@sqlite/unknown/query-requirements"
18
+ readonly message: string
19
+ readonly query?: SqliteQueryContext
20
+ readonly requiredCapabilities: readonly SqliteQueryRequirement[]
21
+ readonly actualCapabilities: readonly QueryCapability[]
22
+ readonly cause: SqliteDriverError
23
+ }>
24
+
25
+ export type SqliteReadQueryError =
26
+ | KnownSqliteError
27
+ | UnknownSqliteCodeError
28
+ | UnknownSqliteDriverError
29
+ | SqliteQueryRequirementsError
30
+
31
+ const requiresWriteSqliteError = (error: SqliteDriverError): boolean =>
32
+ requirements_of_sqlite_error(error).length > 0
33
+
34
+ const lookup_sqlite_requirements = (
35
+ sqlState: string
36
+ ): readonly SqliteQueryRequirement[] => {
37
+ const prefix = sqlState.slice(0, 2)
38
+ return prefix in sqlite_requirements_by_sqlstate_prefix
39
+ ? sqlite_requirements_by_sqlstate_prefix[prefix as keyof typeof sqlite_requirements_by_sqlstate_prefix]
40
+ : []
41
+ }
42
+
43
+ export const requirements_of_sqlite_error = (
44
+ error: SqliteDriverError
45
+ ): readonly SqliteQueryRequirement[] => {
46
+ if ("sqlState" in error && typeof error.sqlState === "string") {
47
+ return lookup_sqlite_requirements(error.sqlState)
48
+ }
49
+ if ("symbol" in error && (error.symbol === "SQLITE_READONLY" || error.symbol.startsWith("SQLITE_CONSTRAINT"))) {
50
+ return ["write"]
51
+ }
52
+ return []
53
+ }
54
+
55
+ export const narrowSqliteDriverErrorForReadQuery = (
56
+ error: SqliteDriverError
57
+ ): SqliteReadQueryError => {
58
+ const requiredCapabilities = requirements_of_sqlite_error(error)
59
+ if (!requiresWriteSqliteError(error)) {
60
+ return error as SqliteReadQueryError
61
+ }
62
+
63
+ return {
64
+ _tag: "@sqlite/unknown/query-requirements",
65
+ message: "SQLite driver error requires query capabilities not provided by this plan",
66
+ query: error.query,
67
+ requiredCapabilities,
68
+ actualCapabilities: read_query_capabilities,
69
+ cause: error
70
+ } satisfies SqliteQueryRequirementsError
71
+ }
@@ -0,0 +1,29 @@
1
+ import type { SqliteErrorNumber, SqliteErrorSymbol, SqliteErrorTag } from "./catalog.js"
2
+ import type { SqliteErrorFields, SqliteQueryContext } from "./fields.js"
3
+
4
+ /** Raw SQLite-like error shape as commonly exposed by client libraries. */
5
+ export interface SqliteErrorLike {
6
+ readonly code?: string
7
+ readonly errno?: string | number
8
+ readonly sqlState?: string
9
+ readonly sqlMessage?: string
10
+ readonly message?: string
11
+ readonly fatal?: boolean
12
+ readonly sql?: string
13
+ readonly syscall?: string
14
+ readonly address?: string
15
+ readonly port?: string | number
16
+ readonly hostname?: string
17
+ }
18
+
19
+ /** Broad known-SQLite error surface used by the normalizer return type. */
20
+ export interface SqliteKnownErrorBase extends SqliteErrorFields {
21
+ readonly _tag: SqliteErrorTag
22
+ readonly category: "sqlite"
23
+ readonly number: SqliteErrorNumber
24
+ readonly symbol: SqliteErrorSymbol
25
+ readonly messageTemplate: string
26
+ readonly message: string
27
+ readonly query?: SqliteQueryContext
28
+ readonly raw: SqliteErrorLike
29
+ }