effect-qb 0.12.3
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/README.md +1294 -0
- package/dist/mysql.js +57575 -0
- package/dist/postgres.js +6303 -0
- package/package.json +42 -0
- package/src/internal/aggregation-validation.ts +57 -0
- package/src/internal/case-analysis.ts +50 -0
- package/src/internal/coercion-analysis.ts +30 -0
- package/src/internal/coercion-errors.ts +29 -0
- package/src/internal/coercion-kind.ts +32 -0
- package/src/internal/coercion-normalize.ts +7 -0
- package/src/internal/coercion-rules.ts +25 -0
- package/src/internal/column-state.ts +453 -0
- package/src/internal/column.ts +417 -0
- package/src/internal/datatypes/define.ts +44 -0
- package/src/internal/datatypes/lookup.ts +280 -0
- package/src/internal/datatypes/shape.ts +72 -0
- package/src/internal/derived-table.ts +149 -0
- package/src/internal/dialect.ts +30 -0
- package/src/internal/executor.ts +390 -0
- package/src/internal/expression-ast.ts +349 -0
- package/src/internal/expression.ts +325 -0
- package/src/internal/grouping-key.ts +82 -0
- package/src/internal/json/ast.ts +63 -0
- package/src/internal/json/errors.ts +13 -0
- package/src/internal/json/path.ts +227 -0
- package/src/internal/json/shape.ts +1 -0
- package/src/internal/json/types.ts +386 -0
- package/src/internal/mysql-dialect.ts +39 -0
- package/src/internal/mysql-renderer.ts +37 -0
- package/src/internal/plan.ts +64 -0
- package/src/internal/postgres-dialect.ts +34 -0
- package/src/internal/postgres-renderer.ts +40 -0
- package/src/internal/predicate-analysis.ts +71 -0
- package/src/internal/predicate-atom.ts +43 -0
- package/src/internal/predicate-branches.ts +40 -0
- package/src/internal/predicate-context.ts +279 -0
- package/src/internal/predicate-formula.ts +100 -0
- package/src/internal/predicate-key.ts +28 -0
- package/src/internal/predicate-nnf.ts +12 -0
- package/src/internal/predicate-normalize.ts +202 -0
- package/src/internal/projection-alias.ts +15 -0
- package/src/internal/projections.ts +101 -0
- package/src/internal/query-ast.ts +297 -0
- package/src/internal/query-factory.ts +6757 -0
- package/src/internal/query-requirements.ts +40 -0
- package/src/internal/query.ts +1590 -0
- package/src/internal/renderer.ts +102 -0
- package/src/internal/runtime-normalize.ts +344 -0
- package/src/internal/runtime-schema.ts +428 -0
- package/src/internal/runtime-value.ts +85 -0
- package/src/internal/schema-derivation.ts +131 -0
- package/src/internal/sql-expression-renderer.ts +1353 -0
- package/src/internal/table-options.ts +225 -0
- package/src/internal/table.ts +674 -0
- package/src/mysql/column.ts +30 -0
- package/src/mysql/datatypes/index.ts +6 -0
- package/src/mysql/datatypes/spec.ts +180 -0
- package/src/mysql/errors/catalog.ts +51662 -0
- package/src/mysql/errors/fields.ts +21 -0
- package/src/mysql/errors/index.ts +18 -0
- package/src/mysql/errors/normalize.ts +232 -0
- package/src/mysql/errors/requirements.ts +73 -0
- package/src/mysql/executor.ts +134 -0
- package/src/mysql/query.ts +189 -0
- package/src/mysql/renderer.ts +19 -0
- package/src/mysql/table.ts +157 -0
- package/src/mysql.ts +18 -0
- package/src/postgres/column.ts +20 -0
- package/src/postgres/datatypes/index.ts +8 -0
- package/src/postgres/datatypes/spec.ts +264 -0
- package/src/postgres/errors/catalog.ts +452 -0
- package/src/postgres/errors/fields.ts +48 -0
- package/src/postgres/errors/index.ts +4 -0
- package/src/postgres/errors/normalize.ts +209 -0
- package/src/postgres/errors/requirements.ts +65 -0
- package/src/postgres/errors/types.ts +38 -0
- package/src/postgres/executor.ts +131 -0
- package/src/postgres/query.ts +189 -0
- package/src/postgres/renderer.ts +29 -0
- package/src/postgres/table.ts +157 -0
- package/src/postgres.ts +18 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Common MySQL driver error fields surfaced by the normalizer when present. */
|
|
2
|
+
export interface MysqlErrorFields {
|
|
3
|
+
readonly errno?: number
|
|
4
|
+
readonly sqlState?: string
|
|
5
|
+
readonly sqlMessage?: string
|
|
6
|
+
readonly fatal?: boolean
|
|
7
|
+
readonly sql?: string
|
|
8
|
+
readonly syscall?: string
|
|
9
|
+
readonly address?: string
|
|
10
|
+
readonly port?: number
|
|
11
|
+
readonly hostname?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Rendered SQL context attached to normalized MySQL execution failures. */
|
|
15
|
+
export interface MysqlQueryContext {
|
|
16
|
+
readonly sql: string
|
|
17
|
+
readonly params: readonly unknown[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type MySqlErrorFields = MysqlErrorFields
|
|
21
|
+
export type MySqlQueryContext = MysqlQueryContext
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from "./catalog.js"
|
|
2
|
+
export * from "./fields.js"
|
|
3
|
+
export * from "./normalize.js"
|
|
4
|
+
export * from "./requirements.js"
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
findMysqlErrorDescriptorsByNumber as findMySqlErrorDescriptorsByNumber,
|
|
8
|
+
getMysqlErrorDescriptor as getMySqlErrorDescriptor,
|
|
9
|
+
isMysqlErrorNumber as isMySqlErrorNumber,
|
|
10
|
+
isMysqlErrorSymbol as isMySqlErrorSymbol
|
|
11
|
+
} from "./catalog.js"
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
hasNumber as hasErrorNumber,
|
|
15
|
+
hasSymbol as hasErrorSymbol,
|
|
16
|
+
isMysqlErrorLike as isMySqlErrorLike,
|
|
17
|
+
normalizeMysqlDriverError as normalizeMySqlDriverError
|
|
18
|
+
} from "./normalize.js"
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import type * as Renderer from "../../internal/renderer.js"
|
|
2
|
+
import {
|
|
3
|
+
findMysqlErrorDescriptorsByNumberLoose,
|
|
4
|
+
getMysqlErrorDescriptor,
|
|
5
|
+
isMysqlErrorNumber,
|
|
6
|
+
isMysqlErrorSymbol,
|
|
7
|
+
type MysqlErrorDescriptor,
|
|
8
|
+
type MysqlErrorNumber,
|
|
9
|
+
type MysqlErrorSymbol,
|
|
10
|
+
type MysqlErrorTag
|
|
11
|
+
} from "./catalog.js"
|
|
12
|
+
import type {
|
|
13
|
+
MysqlErrorFields,
|
|
14
|
+
MysqlQueryContext
|
|
15
|
+
} from "./fields.js"
|
|
16
|
+
|
|
17
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
18
|
+
typeof value === "object" && value !== null
|
|
19
|
+
|
|
20
|
+
const asString = (value: unknown): string | undefined =>
|
|
21
|
+
typeof value === "string" ? value : undefined
|
|
22
|
+
|
|
23
|
+
const asBoolean = (value: unknown): boolean | undefined =>
|
|
24
|
+
typeof value === "boolean" ? value : undefined
|
|
25
|
+
|
|
26
|
+
const asNumber = (value: unknown): number | undefined => {
|
|
27
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
28
|
+
return value
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
31
|
+
const parsed = Number(value)
|
|
32
|
+
return Number.isFinite(parsed) ? parsed : undefined
|
|
33
|
+
}
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const normalizeFields = (error: Record<string, unknown>): MysqlErrorFields => ({
|
|
38
|
+
errno: asNumber(error.errno),
|
|
39
|
+
sqlState: asString(error.sqlState),
|
|
40
|
+
sqlMessage: asString(error.sqlMessage),
|
|
41
|
+
fatal: asBoolean(error.fatal),
|
|
42
|
+
sql: asString(error.sql),
|
|
43
|
+
syscall: asString(error.syscall),
|
|
44
|
+
address: asString(error.address),
|
|
45
|
+
port: asNumber(error.port),
|
|
46
|
+
hostname: asString(error.hostname)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
/** Raw MySQL-like error shape as commonly exposed by client libraries. */
|
|
50
|
+
export interface MysqlErrorLike {
|
|
51
|
+
readonly code?: string
|
|
52
|
+
readonly errno?: string | number
|
|
53
|
+
readonly sqlState?: string
|
|
54
|
+
readonly sqlMessage?: string
|
|
55
|
+
readonly message?: string
|
|
56
|
+
readonly fatal?: boolean
|
|
57
|
+
readonly sql?: string
|
|
58
|
+
readonly syscall?: string
|
|
59
|
+
readonly address?: string
|
|
60
|
+
readonly port?: string | number
|
|
61
|
+
readonly hostname?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Structured known MySQL error derived from the generated official catalog. */
|
|
65
|
+
export type KnownMysqlError<Symbol extends MysqlErrorSymbol = MysqlErrorSymbol> = Readonly<{
|
|
66
|
+
readonly _tag: MysqlErrorTag<Symbol>
|
|
67
|
+
readonly category: MysqlErrorDescriptor<Symbol>["category"]
|
|
68
|
+
readonly number: MysqlErrorDescriptor<Symbol>["number"]
|
|
69
|
+
readonly symbol: Symbol
|
|
70
|
+
readonly documentedSqlState: MysqlErrorDescriptor<Symbol>["sqlState"]
|
|
71
|
+
readonly messageTemplate: MysqlErrorDescriptor<Symbol>["messageTemplate"]
|
|
72
|
+
readonly message: string
|
|
73
|
+
readonly query?: MysqlQueryContext
|
|
74
|
+
readonly raw: MysqlErrorLike
|
|
75
|
+
} & MysqlErrorFields>
|
|
76
|
+
|
|
77
|
+
/** Extracts the normalized MySQL error variant for a specific symbol. */
|
|
78
|
+
export type KnownMysqlErrorBySymbol<Symbol extends MysqlErrorSymbol> = KnownMysqlError<Symbol>
|
|
79
|
+
|
|
80
|
+
/** MySQL-like error whose symbol or number is not in the current catalog. */
|
|
81
|
+
export type UnknownMysqlCodeError = Readonly<{
|
|
82
|
+
readonly _tag: "@mysql/unknown/code"
|
|
83
|
+
readonly code?: string
|
|
84
|
+
readonly errno?: string | number
|
|
85
|
+
readonly message: string
|
|
86
|
+
readonly query?: MysqlQueryContext
|
|
87
|
+
readonly raw: MysqlErrorLike
|
|
88
|
+
} & MysqlErrorFields>
|
|
89
|
+
|
|
90
|
+
/** Fallback for non-MySQL driver failures in the MySQL executor path. */
|
|
91
|
+
export type UnknownMysqlDriverError = Readonly<{
|
|
92
|
+
readonly _tag: "@mysql/unknown/driver"
|
|
93
|
+
readonly message: string
|
|
94
|
+
readonly query?: MysqlQueryContext
|
|
95
|
+
readonly cause: unknown
|
|
96
|
+
}>
|
|
97
|
+
|
|
98
|
+
/** Any MySQL-specific driver failure surfaced by the MySQL executor. */
|
|
99
|
+
export type MysqlDriverError =
|
|
100
|
+
| KnownMysqlError
|
|
101
|
+
| UnknownMysqlCodeError
|
|
102
|
+
| UnknownMysqlDriverError
|
|
103
|
+
|
|
104
|
+
/** Runtime guard for objects that look like MySQL driver errors. */
|
|
105
|
+
export const isMysqlErrorLike = (value: unknown): value is MysqlErrorLike =>
|
|
106
|
+
isRecord(value) &&
|
|
107
|
+
(
|
|
108
|
+
typeof value.code === "string" ||
|
|
109
|
+
typeof value.errno === "string" ||
|
|
110
|
+
typeof value.errno === "number" ||
|
|
111
|
+
typeof value.sqlState === "string" ||
|
|
112
|
+
typeof value.sqlMessage === "string" ||
|
|
113
|
+
typeof value.message === "string" ||
|
|
114
|
+
typeof value.fatal === "boolean"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
const errorMessageOf = (error: MysqlErrorLike): string =>
|
|
118
|
+
error.sqlMessage ?? error.message ?? "MySQL driver error"
|
|
119
|
+
|
|
120
|
+
const numberOf = (error: MysqlErrorLike): string | undefined => {
|
|
121
|
+
if (typeof error.errno === "number" && Number.isFinite(error.errno)) {
|
|
122
|
+
return String(error.errno)
|
|
123
|
+
}
|
|
124
|
+
if (typeof error.errno === "string" && error.errno.trim() !== "") {
|
|
125
|
+
return error.errno
|
|
126
|
+
}
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const findDescriptor = (error: MysqlErrorLike): MysqlErrorDescriptor | undefined => {
|
|
131
|
+
if (typeof error.code === "string" && isMysqlErrorSymbol(error.code)) {
|
|
132
|
+
return getMysqlErrorDescriptor(error.code)
|
|
133
|
+
}
|
|
134
|
+
if (typeof error.code === "string" && isMysqlErrorNumber(error.code)) {
|
|
135
|
+
const matches = findMysqlErrorDescriptorsByNumberLoose(error.code)
|
|
136
|
+
if (matches?.length === 1) {
|
|
137
|
+
return matches[0]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const number = numberOf(error)
|
|
141
|
+
if (number !== undefined) {
|
|
142
|
+
const matches = findMysqlErrorDescriptorsByNumberLoose(number)
|
|
143
|
+
if (!matches || matches.length === 0) {
|
|
144
|
+
return undefined
|
|
145
|
+
}
|
|
146
|
+
if (matches.length === 1) {
|
|
147
|
+
return matches[0]
|
|
148
|
+
}
|
|
149
|
+
if (typeof error.code === "string") {
|
|
150
|
+
return matches.find((descriptor) => descriptor.symbol === error.code)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return undefined
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const makeKnownMysqlError = <Symbol extends MysqlErrorSymbol>(
|
|
157
|
+
descriptor: MysqlErrorDescriptor<Symbol>,
|
|
158
|
+
raw: MysqlErrorLike,
|
|
159
|
+
query?: MysqlQueryContext
|
|
160
|
+
): KnownMysqlError<Symbol> => ({
|
|
161
|
+
_tag: descriptor.tag,
|
|
162
|
+
category: descriptor.category,
|
|
163
|
+
number: descriptor.number,
|
|
164
|
+
symbol: descriptor.symbol,
|
|
165
|
+
documentedSqlState: descriptor.sqlState,
|
|
166
|
+
messageTemplate: descriptor.messageTemplate,
|
|
167
|
+
message: errorMessageOf(raw),
|
|
168
|
+
query,
|
|
169
|
+
raw,
|
|
170
|
+
...normalizeFields(raw as Record<string, unknown>),
|
|
171
|
+
sqlState: asString(raw.sqlState) ?? descriptor.sqlState
|
|
172
|
+
}) as KnownMysqlError<Symbol>
|
|
173
|
+
|
|
174
|
+
/** Normalizes an unknown failure into a structured MySQL driver error. */
|
|
175
|
+
export const normalizeMysqlDriverError = (
|
|
176
|
+
cause: unknown,
|
|
177
|
+
query?: MysqlQueryContext | Renderer.RenderedQuery<any, "mysql">
|
|
178
|
+
): MysqlDriverError => {
|
|
179
|
+
const context = query === undefined
|
|
180
|
+
? undefined
|
|
181
|
+
: "sql" in query
|
|
182
|
+
? { sql: query.sql, params: query.params }
|
|
183
|
+
: query
|
|
184
|
+
|
|
185
|
+
if (!isMysqlErrorLike(cause)) {
|
|
186
|
+
return {
|
|
187
|
+
_tag: "@mysql/unknown/driver",
|
|
188
|
+
message: cause instanceof Error ? cause.message : "Unknown MySQL driver failure",
|
|
189
|
+
query: context,
|
|
190
|
+
cause
|
|
191
|
+
} as UnknownMysqlDriverError
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const descriptor = findDescriptor(cause)
|
|
195
|
+
if (descriptor !== undefined) {
|
|
196
|
+
return makeKnownMysqlError(descriptor, cause, context)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (typeof cause.code === "string" || numberOf(cause) !== undefined) {
|
|
200
|
+
return {
|
|
201
|
+
_tag: "@mysql/unknown/code",
|
|
202
|
+
code: asString(cause.code),
|
|
203
|
+
errno: cause.errno,
|
|
204
|
+
message: errorMessageOf(cause),
|
|
205
|
+
query: context,
|
|
206
|
+
raw: cause,
|
|
207
|
+
...normalizeFields(cause as Record<string, unknown>)
|
|
208
|
+
} as UnknownMysqlCodeError
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
_tag: "@mysql/unknown/driver",
|
|
213
|
+
message: errorMessageOf(cause),
|
|
214
|
+
query: context,
|
|
215
|
+
cause
|
|
216
|
+
} as UnknownMysqlDriverError
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Type guard for a specific MySQL catalog symbol. */
|
|
220
|
+
export const hasSymbol = <Symbol extends MysqlErrorSymbol>(
|
|
221
|
+
error: MysqlDriverError | { readonly symbol?: string },
|
|
222
|
+
symbol: Symbol
|
|
223
|
+
): error is KnownMysqlErrorBySymbol<Symbol> =>
|
|
224
|
+
"symbol" in error && error.symbol === symbol
|
|
225
|
+
|
|
226
|
+
/** Type guard for a specific documented MySQL error number. */
|
|
227
|
+
export const hasNumber = <Number extends MysqlErrorNumber>(
|
|
228
|
+
error: MysqlDriverError | { readonly number?: string; readonly errno?: string | number },
|
|
229
|
+
number: Number
|
|
230
|
+
): error is KnownMysqlError & { readonly number: Number } =>
|
|
231
|
+
("number" in error && error.number === number) ||
|
|
232
|
+
("errno" in error && String(error.errno) === number)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { read_query_capabilities, type QueryCapability, type QueryRequirement } from "../../internal/query-requirements.js"
|
|
2
|
+
import type { MysqlQueryContext } from "./fields.js"
|
|
3
|
+
import type {
|
|
4
|
+
KnownMysqlError,
|
|
5
|
+
MysqlDriverError,
|
|
6
|
+
UnknownMysqlCodeError,
|
|
7
|
+
UnknownMysqlDriverError
|
|
8
|
+
} from "./normalize.js"
|
|
9
|
+
|
|
10
|
+
export type MysqlQueryRequirement = Extract<QueryRequirement, "write" | "ddl" | "transaction" | "locking">
|
|
11
|
+
|
|
12
|
+
export const mysql_requirements_by_sqlstate_prefix = {
|
|
13
|
+
"23": ["write"]
|
|
14
|
+
} as const satisfies Partial<Record<string, readonly MysqlQueryRequirement[]>>
|
|
15
|
+
|
|
16
|
+
export type MysqlQueryRequirementsError = Readonly<{
|
|
17
|
+
readonly _tag: "@mysql/unknown/query-requirements"
|
|
18
|
+
readonly message: string
|
|
19
|
+
readonly query?: MysqlQueryContext
|
|
20
|
+
readonly requiredCapabilities: readonly MysqlQueryRequirement[]
|
|
21
|
+
readonly actualCapabilities: readonly QueryCapability[]
|
|
22
|
+
readonly cause: MysqlDriverError
|
|
23
|
+
}>
|
|
24
|
+
|
|
25
|
+
type WriteRequiredMysqlSqlState = `23${string}`
|
|
26
|
+
|
|
27
|
+
export type MysqlReadQueryError =
|
|
28
|
+
| Exclude<KnownMysqlError, { readonly documentedSqlState: WriteRequiredMysqlSqlState }>
|
|
29
|
+
| UnknownMysqlCodeError
|
|
30
|
+
| UnknownMysqlDriverError
|
|
31
|
+
| MysqlQueryRequirementsError
|
|
32
|
+
|
|
33
|
+
const requiresWriteMysqlError = (error: MysqlDriverError): boolean =>
|
|
34
|
+
requirements_of_mysql_error(error).length > 0
|
|
35
|
+
|
|
36
|
+
const lookup_mysql_requirements = (
|
|
37
|
+
sqlState: string
|
|
38
|
+
): readonly MysqlQueryRequirement[] => {
|
|
39
|
+
const prefix = sqlState.slice(0, 2)
|
|
40
|
+
return prefix in mysql_requirements_by_sqlstate_prefix
|
|
41
|
+
? mysql_requirements_by_sqlstate_prefix[prefix as keyof typeof mysql_requirements_by_sqlstate_prefix]
|
|
42
|
+
: []
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const requirements_of_mysql_error = (
|
|
46
|
+
error: MysqlDriverError
|
|
47
|
+
): readonly MysqlQueryRequirement[] => {
|
|
48
|
+
if ("documentedSqlState" in error && typeof error.documentedSqlState === "string") {
|
|
49
|
+
return lookup_mysql_requirements(error.documentedSqlState)
|
|
50
|
+
}
|
|
51
|
+
if ("sqlState" in error && typeof error.sqlState === "string") {
|
|
52
|
+
return lookup_mysql_requirements(error.sqlState)
|
|
53
|
+
}
|
|
54
|
+
return []
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const narrowMysqlDriverErrorForReadQuery = (
|
|
58
|
+
error: MysqlDriverError
|
|
59
|
+
): MysqlReadQueryError => {
|
|
60
|
+
const requiredCapabilities = requirements_of_mysql_error(error)
|
|
61
|
+
if (!requiresWriteMysqlError(error)) {
|
|
62
|
+
return error as MysqlReadQueryError
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
_tag: "@mysql/unknown/query-requirements",
|
|
67
|
+
message: "MySQL driver error requires query capabilities not provided by this plan",
|
|
68
|
+
query: error.query,
|
|
69
|
+
requiredCapabilities,
|
|
70
|
+
actualCapabilities: read_query_capabilities,
|
|
71
|
+
cause: error
|
|
72
|
+
} satisfies MysqlQueryRequirementsError
|
|
73
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import * as SqlClient from "@effect/sql/SqlClient"
|
|
3
|
+
|
|
4
|
+
import * as CoreExecutor from "../internal/executor.js"
|
|
5
|
+
import * as Query from "./query.js"
|
|
6
|
+
import * as Renderer from "./renderer.js"
|
|
7
|
+
import {
|
|
8
|
+
narrowMysqlDriverErrorForReadQuery,
|
|
9
|
+
normalizeMysqlDriverError,
|
|
10
|
+
type MysqlDriverError,
|
|
11
|
+
type MysqlReadQueryError
|
|
12
|
+
} from "./errors/index.js"
|
|
13
|
+
|
|
14
|
+
/** MySQL-specialized flat row returned by SQL drivers. */
|
|
15
|
+
export type FlatRow = CoreExecutor.FlatRow
|
|
16
|
+
/** Runtime decode failure raised after SQL execution but before row remapping. */
|
|
17
|
+
export type RowDecodeError = CoreExecutor.RowDecodeError
|
|
18
|
+
/** MySQL-specialized rendered-query driver. */
|
|
19
|
+
export type Driver<Error = never, Context = never> = CoreExecutor.Driver<"mysql", Error, Context>
|
|
20
|
+
/** MySQL-specialized executor contract. */
|
|
21
|
+
export type Executor<Error = never, Context = never> = CoreExecutor.Executor<"mysql", Error, Context>
|
|
22
|
+
/** Optional renderer / driver overrides for the standard MySQL executor pipeline. */
|
|
23
|
+
export interface MakeOptions<Error = never, Context = never> {
|
|
24
|
+
readonly renderer?: Renderer.Renderer
|
|
25
|
+
readonly driver?: Driver<Error, Context>
|
|
26
|
+
readonly driverMode?: CoreExecutor.DriverMode
|
|
27
|
+
}
|
|
28
|
+
/** Standard composed error shape for MySQL executors. */
|
|
29
|
+
export type MysqlExecutorError = MysqlDriverError | RowDecodeError
|
|
30
|
+
/** Read-query error surface emitted by built-in MySQL executors. */
|
|
31
|
+
export type MysqlQueryError<PlanValue extends Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
32
|
+
Exclude<Query.CapabilitiesOfPlan<PlanValue>, "read"> extends never ? MysqlReadQueryError : MysqlExecutorError
|
|
33
|
+
|
|
34
|
+
/** Runs an effect within the ambient MySQL SQL transaction service. */
|
|
35
|
+
export const withTransaction = CoreExecutor.withTransaction
|
|
36
|
+
/** Runs an effect in a nested MySQL SQL transaction scope. */
|
|
37
|
+
export const withSavepoint = CoreExecutor.withSavepoint
|
|
38
|
+
|
|
39
|
+
/** MySQL executor whose error channel narrows based on the query plan. */
|
|
40
|
+
export interface QueryExecutor<Context = never> {
|
|
41
|
+
readonly dialect: "mysql"
|
|
42
|
+
execute<PlanValue extends Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
43
|
+
plan: Query.DialectCompatiblePlan<PlanValue, "mysql">
|
|
44
|
+
): Effect.Effect<Query.ResultRows<PlanValue>, MysqlQueryError<PlanValue>, Context>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Constructs a MySQL-specialized SQL driver. */
|
|
48
|
+
export const driver = <
|
|
49
|
+
Error = never,
|
|
50
|
+
Context = never
|
|
51
|
+
>(
|
|
52
|
+
execute: <Row>(
|
|
53
|
+
query: Renderer.RenderedQuery<Row>
|
|
54
|
+
) => Effect.Effect<ReadonlyArray<FlatRow>, Error, Context>
|
|
55
|
+
): Driver<Error, Context> =>
|
|
56
|
+
CoreExecutor.driver("mysql", execute)
|
|
57
|
+
|
|
58
|
+
const fromDriver = <
|
|
59
|
+
Error = never,
|
|
60
|
+
Context = never
|
|
61
|
+
>(
|
|
62
|
+
renderer: Renderer.Renderer,
|
|
63
|
+
sqlDriver: Driver<Error, Context>,
|
|
64
|
+
driverMode: CoreExecutor.DriverMode = "raw"
|
|
65
|
+
): QueryExecutor<Context> => ({
|
|
66
|
+
dialect: "mysql",
|
|
67
|
+
execute(plan) {
|
|
68
|
+
const rendered = renderer.render(plan)
|
|
69
|
+
return Effect.mapError(
|
|
70
|
+
Effect.flatMap(
|
|
71
|
+
sqlDriver.execute(rendered),
|
|
72
|
+
(rows) => Effect.try({
|
|
73
|
+
try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode }),
|
|
74
|
+
catch: (error) => error as RowDecodeError
|
|
75
|
+
})
|
|
76
|
+
),
|
|
77
|
+
(error) => {
|
|
78
|
+
if (typeof error === "object" && error !== null && "_tag" in error && error._tag === "RowDecodeError") {
|
|
79
|
+
return error as RowDecodeError
|
|
80
|
+
}
|
|
81
|
+
const normalized = normalizeMysqlDriverError(error, rendered)
|
|
82
|
+
return CoreExecutor.hasWriteCapability(plan)
|
|
83
|
+
? normalized
|
|
84
|
+
: narrowMysqlDriverErrorForReadQuery(normalized)
|
|
85
|
+
}
|
|
86
|
+
) as Effect.Effect<any, any, Context>
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const sqlClientDriver = (): Driver<any, SqlClient.SqlClient> =>
|
|
91
|
+
driver((query) =>
|
|
92
|
+
Effect.flatMap(SqlClient.SqlClient, (sql) =>
|
|
93
|
+
sql.unsafe<FlatRow>(query.sql, [...query.params])))
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates the standard MySQL executor pipeline.
|
|
97
|
+
*
|
|
98
|
+
* By default this uses the built-in MySQL renderer plus the ambient
|
|
99
|
+
* `@effect/sql` `SqlClient`. Advanced callers can override the renderer,
|
|
100
|
+
* driver, or both.
|
|
101
|
+
*/
|
|
102
|
+
export function make(): QueryExecutor<SqlClient.SqlClient>
|
|
103
|
+
export function make(
|
|
104
|
+
options: {
|
|
105
|
+
readonly renderer?: Renderer.Renderer
|
|
106
|
+
readonly driverMode?: CoreExecutor.DriverMode
|
|
107
|
+
}
|
|
108
|
+
): QueryExecutor<SqlClient.SqlClient>
|
|
109
|
+
export function make<Error = never, Context = never>(
|
|
110
|
+
options: {
|
|
111
|
+
readonly renderer?: Renderer.Renderer
|
|
112
|
+
readonly driver: Driver<Error, Context>
|
|
113
|
+
readonly driverMode?: CoreExecutor.DriverMode
|
|
114
|
+
}
|
|
115
|
+
): QueryExecutor<Context>
|
|
116
|
+
export function make<Error = never, Context = never>(
|
|
117
|
+
options: MakeOptions<Error, Context> = {}
|
|
118
|
+
): QueryExecutor<any> {
|
|
119
|
+
if (options.driver) {
|
|
120
|
+
return fromDriver(options.renderer ?? Renderer.make(), options.driver, options.driverMode)
|
|
121
|
+
}
|
|
122
|
+
return fromDriver(options.renderer ?? Renderer.make(), sqlClientDriver(), options.driverMode)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Creates a MySQL-specialized executor from a typed implementation callback. */
|
|
126
|
+
export const custom = <
|
|
127
|
+
Error = never,
|
|
128
|
+
Context = never
|
|
129
|
+
>(
|
|
130
|
+
execute: <PlanValue extends Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
131
|
+
plan: Query.DialectCompatiblePlan<PlanValue, "mysql">
|
|
132
|
+
) => Effect.Effect<Query.ResultRows<PlanValue>, Error, Context>
|
|
133
|
+
): Executor<Error, Context> =>
|
|
134
|
+
CoreExecutor.make("mysql", execute as any) as Executor<Error, Context>
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import * as Expression from "../internal/expression.js"
|
|
2
|
+
import { mysqlDatatypes } from "./datatypes/index.js"
|
|
3
|
+
import {
|
|
4
|
+
type CapabilitiesOfPlan,
|
|
5
|
+
type CompletePlan,
|
|
6
|
+
type DialectCompatiblePlan,
|
|
7
|
+
type DerivedSourceRequiredError,
|
|
8
|
+
type CteSource,
|
|
9
|
+
type EffectiveNullability,
|
|
10
|
+
type ExpressionInput,
|
|
11
|
+
type ExpressionOutput,
|
|
12
|
+
type GroupByInput,
|
|
13
|
+
type MergeCapabilities,
|
|
14
|
+
type MergeCapabilityTuple,
|
|
15
|
+
type HavingPredicateInput,
|
|
16
|
+
type OrderDirection,
|
|
17
|
+
type OutputOfSelection,
|
|
18
|
+
type MutationInputOf,
|
|
19
|
+
type MutationTargetLike,
|
|
20
|
+
type NumericExpressionInput,
|
|
21
|
+
type PredicateInput,
|
|
22
|
+
type QueryCapability,
|
|
23
|
+
type QueryPlan,
|
|
24
|
+
type QueryRequirement,
|
|
25
|
+
type SetCompatiblePlan,
|
|
26
|
+
type SetCompatibleRightPlan,
|
|
27
|
+
type SetOperator,
|
|
28
|
+
type QueryStatement,
|
|
29
|
+
type ResultRow,
|
|
30
|
+
type ResultRows,
|
|
31
|
+
type RuntimeResultRow,
|
|
32
|
+
type RuntimeResultRows,
|
|
33
|
+
type SchemaTableLike,
|
|
34
|
+
type SourceCapabilitiesOf,
|
|
35
|
+
type SourceRequiredOf,
|
|
36
|
+
type SourceRequirementError,
|
|
37
|
+
type StatementOfPlan,
|
|
38
|
+
type StringExpressionInput
|
|
39
|
+
} from "../internal/query.js"
|
|
40
|
+
import { makeDialectQuery } from "../internal/query-factory.js"
|
|
41
|
+
|
|
42
|
+
const mysqlQuery = makeDialectQuery({
|
|
43
|
+
dialect: "mysql",
|
|
44
|
+
textDb: { dialect: "mysql", kind: "text" } as Expression.DbType.MySqlText,
|
|
45
|
+
numericDb: { dialect: "mysql", kind: "double" } as Expression.DbType.MySqlDouble,
|
|
46
|
+
boolDb: { dialect: "mysql", kind: "boolean" } as Expression.DbType.MySqlBool,
|
|
47
|
+
timestampDb: { dialect: "mysql", kind: "timestamp" } as Expression.DbType.MySqlTimestamp,
|
|
48
|
+
nullDb: { dialect: "mysql", kind: "null" } as Expression.DbType.Base<"mysql", "null">,
|
|
49
|
+
type: mysqlDatatypes
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
export const literal = mysqlQuery.literal
|
|
53
|
+
export const cast = mysqlQuery.cast
|
|
54
|
+
export const type = mysqlQuery.type
|
|
55
|
+
export const json = mysqlQuery.json
|
|
56
|
+
export const eq = mysqlQuery.eq
|
|
57
|
+
export const neq = mysqlQuery.neq
|
|
58
|
+
export const lt = mysqlQuery.lt
|
|
59
|
+
export const lte = mysqlQuery.lte
|
|
60
|
+
export const gt = mysqlQuery.gt
|
|
61
|
+
export const gte = mysqlQuery.gte
|
|
62
|
+
export const isNull = mysqlQuery.isNull
|
|
63
|
+
export const isNotNull = mysqlQuery.isNotNull
|
|
64
|
+
export const upper = mysqlQuery.upper
|
|
65
|
+
export const lower = mysqlQuery.lower
|
|
66
|
+
export const like = mysqlQuery.like
|
|
67
|
+
export const ilike = mysqlQuery.ilike
|
|
68
|
+
export const and = mysqlQuery.and
|
|
69
|
+
export const or = mysqlQuery.or
|
|
70
|
+
export const not = mysqlQuery.not
|
|
71
|
+
export const all = mysqlQuery.all
|
|
72
|
+
export const any = mysqlQuery.any
|
|
73
|
+
const case_ = mysqlQuery.case
|
|
74
|
+
export const match = mysqlQuery.match
|
|
75
|
+
export const coalesce = mysqlQuery.coalesce
|
|
76
|
+
export const in_ = mysqlQuery.in
|
|
77
|
+
export const notIn = mysqlQuery.notIn
|
|
78
|
+
export const between = mysqlQuery.between
|
|
79
|
+
export const contains = mysqlQuery.contains
|
|
80
|
+
export const containedBy = mysqlQuery.containedBy
|
|
81
|
+
export const overlaps = mysqlQuery.overlaps
|
|
82
|
+
export const concat = mysqlQuery.concat
|
|
83
|
+
export const exists = mysqlQuery.exists
|
|
84
|
+
export const over = mysqlQuery.over
|
|
85
|
+
export const rowNumber = mysqlQuery.rowNumber
|
|
86
|
+
export const rank = mysqlQuery.rank
|
|
87
|
+
export const denseRank = mysqlQuery.denseRank
|
|
88
|
+
export const count = mysqlQuery.count
|
|
89
|
+
export const max = mysqlQuery.max
|
|
90
|
+
export const min = mysqlQuery.min
|
|
91
|
+
export const isDistinctFrom = mysqlQuery.isDistinctFrom
|
|
92
|
+
export const isNotDistinctFrom = mysqlQuery.isNotDistinctFrom
|
|
93
|
+
export const excluded = mysqlQuery.excluded
|
|
94
|
+
export const as = mysqlQuery.as
|
|
95
|
+
export const with_ = mysqlQuery.with
|
|
96
|
+
export const withRecursive = mysqlQuery.withRecursive
|
|
97
|
+
export const lateral = mysqlQuery.lateral
|
|
98
|
+
export const scalar = mysqlQuery.scalar
|
|
99
|
+
export const inSubquery = mysqlQuery.inSubquery
|
|
100
|
+
export const compareAny = mysqlQuery.compareAny
|
|
101
|
+
export const compareAll = mysqlQuery.compareAll
|
|
102
|
+
export const values = mysqlQuery.values
|
|
103
|
+
export const unnest = mysqlQuery.unnest
|
|
104
|
+
export const generateSeries = mysqlQuery.generateSeries
|
|
105
|
+
export const returning = mysqlQuery.returning
|
|
106
|
+
export const defaultValues = mysqlQuery.defaultValues
|
|
107
|
+
export const onConflict = mysqlQuery.onConflict
|
|
108
|
+
export const insert = mysqlQuery.insert
|
|
109
|
+
export const update = mysqlQuery.update
|
|
110
|
+
export const upsert = mysqlQuery.upsert
|
|
111
|
+
export const delete_ = mysqlQuery.delete
|
|
112
|
+
export const truncate = mysqlQuery.truncate
|
|
113
|
+
export const merge = mysqlQuery.merge
|
|
114
|
+
export const transaction = mysqlQuery.transaction
|
|
115
|
+
export const commit = mysqlQuery.commit
|
|
116
|
+
export const rollback = mysqlQuery.rollback
|
|
117
|
+
export const savepoint = mysqlQuery.savepoint
|
|
118
|
+
export const rollbackTo = mysqlQuery.rollbackTo
|
|
119
|
+
export const releaseSavepoint = mysqlQuery.releaseSavepoint
|
|
120
|
+
export const createTable = mysqlQuery.createTable
|
|
121
|
+
export const dropTable = mysqlQuery.dropTable
|
|
122
|
+
export const createIndex = mysqlQuery.createIndex
|
|
123
|
+
export const dropIndex = mysqlQuery.dropIndex
|
|
124
|
+
export const union = mysqlQuery.union
|
|
125
|
+
export const unionAll = mysqlQuery.unionAll
|
|
126
|
+
export const intersect = mysqlQuery.intersect
|
|
127
|
+
export const intersectAll = mysqlQuery.intersectAll
|
|
128
|
+
export const except = mysqlQuery.except
|
|
129
|
+
export const exceptAll = mysqlQuery.exceptAll
|
|
130
|
+
export const select = mysqlQuery.select
|
|
131
|
+
export const where = mysqlQuery.where
|
|
132
|
+
export const having = mysqlQuery.having
|
|
133
|
+
export const from = mysqlQuery.from
|
|
134
|
+
export const innerJoin = mysqlQuery.innerJoin
|
|
135
|
+
export const leftJoin = mysqlQuery.leftJoin
|
|
136
|
+
export const rightJoin = mysqlQuery.rightJoin
|
|
137
|
+
export const fullJoin = mysqlQuery.fullJoin
|
|
138
|
+
export const crossJoin = mysqlQuery.crossJoin
|
|
139
|
+
export const distinct = mysqlQuery.distinct
|
|
140
|
+
export const distinctOn = mysqlQuery.distinctOn
|
|
141
|
+
export const limit = mysqlQuery.limit
|
|
142
|
+
export const offset = mysqlQuery.offset
|
|
143
|
+
export const lock = mysqlQuery.lock
|
|
144
|
+
export const orderBy = mysqlQuery.orderBy
|
|
145
|
+
export const groupBy = mysqlQuery.groupBy
|
|
146
|
+
export { case_ as case }
|
|
147
|
+
export { in_ as in }
|
|
148
|
+
export { with_ as with }
|
|
149
|
+
export { delete_ as delete }
|
|
150
|
+
|
|
151
|
+
export type {
|
|
152
|
+
CapabilitiesOfPlan,
|
|
153
|
+
CompletePlan,
|
|
154
|
+
DialectCompatiblePlan,
|
|
155
|
+
DerivedSourceRequiredError,
|
|
156
|
+
CteSource,
|
|
157
|
+
EffectiveNullability,
|
|
158
|
+
ExpressionInput,
|
|
159
|
+
ExpressionOutput,
|
|
160
|
+
GroupByInput,
|
|
161
|
+
MergeCapabilities,
|
|
162
|
+
MergeCapabilityTuple,
|
|
163
|
+
HavingPredicateInput,
|
|
164
|
+
OrderDirection,
|
|
165
|
+
OutputOfSelection,
|
|
166
|
+
MutationInputOf,
|
|
167
|
+
MutationTargetLike,
|
|
168
|
+
NumericExpressionInput,
|
|
169
|
+
PredicateInput,
|
|
170
|
+
QueryCapability,
|
|
171
|
+
QueryPlan,
|
|
172
|
+
QueryStatement,
|
|
173
|
+
QueryRequirement,
|
|
174
|
+
SetCompatiblePlan,
|
|
175
|
+
SetCompatibleRightPlan,
|
|
176
|
+
SetOperator,
|
|
177
|
+
ResultRow,
|
|
178
|
+
ResultRows,
|
|
179
|
+
RuntimeResultRow,
|
|
180
|
+
RuntimeResultRows,
|
|
181
|
+
SchemaTableLike,
|
|
182
|
+
SourceCapabilitiesOf,
|
|
183
|
+
SourceRequiredOf,
|
|
184
|
+
SourceRequirementError,
|
|
185
|
+
StatementOfPlan,
|
|
186
|
+
StringExpressionInput
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export { union_query_capabilities } from "../internal/query.js"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as CoreRenderer from "../internal/renderer.js"
|
|
2
|
+
import { renderMysqlPlan } from "../internal/mysql-renderer.js"
|
|
3
|
+
|
|
4
|
+
/** MySQL-specialized rendered query shape. */
|
|
5
|
+
export type RenderedQuery<Row> = CoreRenderer.RenderedQuery<Row, "mysql">
|
|
6
|
+
/** Extracts the row type carried by a MySQL rendered query. */
|
|
7
|
+
export type RowOf<Value extends RenderedQuery<any>> = CoreRenderer.RowOf<Value>
|
|
8
|
+
/** MySQL-specialized renderer contract. */
|
|
9
|
+
export type Renderer = CoreRenderer.Renderer<"mysql">
|
|
10
|
+
|
|
11
|
+
export { TypeId } from "../internal/renderer.js"
|
|
12
|
+
export type { Projection } from "../internal/renderer.js"
|
|
13
|
+
|
|
14
|
+
/** Creates the built-in MySQL renderer. */
|
|
15
|
+
export const make = (): Renderer =>
|
|
16
|
+
CoreRenderer.make("mysql", renderMysqlPlan)
|
|
17
|
+
|
|
18
|
+
/** Shared built-in MySQL renderer instance. */
|
|
19
|
+
export const mysql = make()
|