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.
- package/dist/mysql.js +1957 -595
- package/dist/postgres/metadata.js +2507 -182
- package/dist/postgres.js +9587 -8201
- package/dist/sqlite.js +8360 -0
- package/package.json +7 -2
- package/src/internal/column-state.ts +7 -0
- package/src/internal/column.ts +22 -0
- package/src/internal/derived-table.ts +29 -3
- package/src/internal/dialect.ts +14 -1
- package/src/internal/dsl-mutation-runtime.ts +173 -4
- package/src/internal/dsl-plan-runtime.ts +165 -20
- package/src/internal/dsl-query-runtime.ts +60 -6
- package/src/internal/dsl-transaction-ddl-runtime.ts +72 -2
- package/src/internal/executor.ts +62 -13
- package/src/internal/expression-ast.ts +3 -2
- package/src/internal/grouping-key.ts +141 -1
- package/src/internal/implication-runtime.ts +2 -1
- package/src/internal/json/types.ts +155 -40
- package/src/internal/predicate/analysis.ts +103 -1
- package/src/internal/predicate/atom.ts +7 -0
- package/src/internal/predicate/context.ts +170 -17
- package/src/internal/predicate/key.ts +64 -2
- package/src/internal/predicate/normalize.ts +115 -34
- package/src/internal/predicate/runtime.ts +144 -13
- package/src/internal/query.ts +563 -103
- package/src/internal/renderer.ts +39 -2
- package/src/internal/runtime/driver-value-mapping.ts +244 -0
- package/src/internal/runtime/normalize.ts +62 -38
- package/src/internal/runtime/schema.ts +5 -3
- package/src/internal/runtime/value.ts +153 -30
- package/src/internal/scalar.ts +11 -0
- package/src/internal/table-options.ts +108 -1
- package/src/internal/table.ts +87 -29
- package/src/mysql/column.ts +19 -2
- package/src/mysql/datatypes/index.ts +21 -0
- package/src/mysql/errors/catalog.ts +5 -5
- package/src/mysql/errors/normalize.ts +2 -2
- package/src/mysql/executor.ts +20 -5
- package/src/mysql/internal/dialect.ts +12 -6
- package/src/mysql/internal/dsl.ts +995 -263
- package/src/mysql/internal/renderer.ts +13 -3
- package/src/mysql/internal/sql-expression-renderer.ts +530 -128
- package/src/mysql/query.ts +9 -2
- package/src/mysql/renderer.ts +7 -2
- package/src/mysql/table.ts +38 -12
- package/src/postgres/cast.ts +22 -7
- package/src/postgres/column.ts +5 -2
- package/src/postgres/errors/normalize.ts +2 -2
- package/src/postgres/executor.ts +68 -10
- package/src/postgres/function/core.ts +19 -1
- package/src/postgres/internal/dialect.ts +12 -6
- package/src/postgres/internal/dsl.ts +958 -288
- package/src/postgres/internal/renderer.ts +13 -3
- package/src/postgres/internal/schema-ddl.ts +2 -1
- package/src/postgres/internal/schema-model.ts +6 -3
- package/src/postgres/internal/sql-expression-renderer.ts +477 -96
- package/src/postgres/json.ts +57 -17
- package/src/postgres/query.ts +9 -2
- package/src/postgres/renderer.ts +7 -2
- package/src/postgres/schema-management.ts +91 -4
- package/src/postgres/schema.ts +1 -1
- package/src/postgres/table.ts +189 -53
- package/src/postgres/type.ts +4 -0
- package/src/sqlite/column.ts +128 -0
- package/src/sqlite/datatypes/index.ts +79 -0
- package/src/sqlite/datatypes/spec.ts +98 -0
- package/src/sqlite/errors/catalog.ts +103 -0
- package/src/sqlite/errors/fields.ts +19 -0
- package/src/sqlite/errors/index.ts +19 -0
- package/src/sqlite/errors/normalize.ts +229 -0
- package/src/sqlite/errors/requirements.ts +71 -0
- package/src/sqlite/errors/types.ts +29 -0
- package/src/sqlite/executor.ts +227 -0
- package/src/sqlite/function/aggregate.ts +2 -0
- package/src/sqlite/function/core.ts +2 -0
- package/src/sqlite/function/index.ts +19 -0
- package/src/sqlite/function/string.ts +2 -0
- package/src/sqlite/function/temporal.ts +100 -0
- package/src/sqlite/function/window.ts +2 -0
- package/src/sqlite/internal/dialect.ts +37 -0
- package/src/sqlite/internal/dsl.ts +6926 -0
- package/src/sqlite/internal/renderer.ts +47 -0
- package/src/sqlite/internal/sql-expression-renderer.ts +1821 -0
- package/src/sqlite/json.ts +2 -0
- package/src/sqlite/query.ts +196 -0
- package/src/sqlite/renderer.ts +24 -0
- package/src/sqlite/table.ts +183 -0
- 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
|
+
}
|