@effect/sql-mssql 4.0.0-beta.7 → 4.0.0-beta.71
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/MssqlClient.d.ts +71 -15
- package/dist/MssqlClient.d.ts.map +1 -1
- package/dist/MssqlClient.js +164 -37
- package/dist/MssqlClient.js.map +1 -1
- package/dist/MssqlMigrator.d.ts +30 -5
- package/dist/MssqlMigrator.d.ts.map +1 -1
- package/dist/MssqlMigrator.js +8 -4
- package/dist/MssqlMigrator.js.map +1 -1
- package/dist/Parameter.d.ts +14 -6
- package/dist/Parameter.d.ts.map +1 -1
- package/dist/Parameter.js +43 -4
- package/dist/Parameter.js.map +1 -1
- package/dist/Procedure.d.ts +41 -16
- package/dist/Procedure.d.ts.map +1 -1
- package/dist/Procedure.js +51 -8
- package/dist/Procedure.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +6 -6
- package/package.json +4 -4
- package/src/MssqlClient.ts +231 -36
- package/src/MssqlMigrator.ts +30 -5
- package/src/Parameter.ts +50 -7
- package/src/Procedure.ts +74 -18
- package/src/index.ts +6 -6
package/src/MssqlClient.ts
CHANGED
|
@@ -1,7 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Microsoft SQL Server client implementation for Effect SQL, backed by the
|
|
3
|
+
* `tedious` driver.
|
|
4
|
+
*
|
|
5
|
+
* This module provides the `MssqlClient` service, constructors, layers, and SQL
|
|
6
|
+
* Server statement compiler. The layers provide both `MssqlClient` and the
|
|
7
|
+
* generic `SqlClient` service, so code written against Effect SQL can run
|
|
8
|
+
* regular queries while SQL Server-specific code can add typed Tedious
|
|
9
|
+
* parameters with `param` and execute stored procedures with `call`.
|
|
10
|
+
*
|
|
11
|
+
* **Mental model**
|
|
12
|
+
*
|
|
13
|
+
* A client owns a scoped pool of Tedious connections and validates startup with
|
|
14
|
+
* `SELECT 1`. Ordinary queries borrow a pooled connection for one operation.
|
|
15
|
+
* Transactions keep one connection for their lifetime, and nested transactions
|
|
16
|
+
* are represented with SQL Server savepoints. Long-running transactions
|
|
17
|
+
* therefore reduce available pool capacity.
|
|
18
|
+
*
|
|
19
|
+
* **Common tasks**
|
|
20
|
+
*
|
|
21
|
+
* Use {@link layer} for a concrete `MssqlClientConfig`, {@link layerConfig} when
|
|
22
|
+
* configuration should come from Effect `Config`, and {@link make} when a scoped
|
|
23
|
+
* client value is needed directly. Use `param` to override the default mapping
|
|
24
|
+
* from Effect SQL primitive values to Tedious `DataType`s, and use the
|
|
25
|
+
* `Procedure` and `Parameter` helpers when a stored procedure needs typed input
|
|
26
|
+
* parameters, output parameters, or result rows.
|
|
27
|
+
*
|
|
28
|
+
* **Gotchas**
|
|
29
|
+
*
|
|
30
|
+
* Tedious permits one active request per connection, and this client does not
|
|
31
|
+
* implement streaming queries. Statements compile to SQL Server-style `@1`
|
|
32
|
+
* parameters and bracket-escaped identifiers. Be explicit about connection pool
|
|
33
|
+
* and timeout settings for workloads with transactions. Review TLS settings:
|
|
34
|
+
* `encrypt` defaults to `false`, and `trustServer` defaults to `true` for the
|
|
35
|
+
* Tedious `trustServerCertificate` option unless overridden.
|
|
36
|
+
*
|
|
37
|
+
* @since 4.0.0
|
|
3
38
|
*/
|
|
4
39
|
import * as Config from "effect/Config"
|
|
40
|
+
import * as Context from "effect/Context"
|
|
5
41
|
import * as Duration from "effect/Duration"
|
|
6
42
|
import * as Effect from "effect/Effect"
|
|
7
43
|
import { identity } from "effect/Function"
|
|
@@ -9,12 +45,23 @@ import * as Layer from "effect/Layer"
|
|
|
9
45
|
import * as Pool from "effect/Pool"
|
|
10
46
|
import * as Redacted from "effect/Redacted"
|
|
11
47
|
import * as Scope from "effect/Scope"
|
|
12
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
13
48
|
import * as Stream from "effect/Stream"
|
|
14
49
|
import * as Reactivity from "effect/unstable/reactivity/Reactivity"
|
|
15
50
|
import * as Client from "effect/unstable/sql/SqlClient"
|
|
16
51
|
import type { Connection } from "effect/unstable/sql/SqlConnection"
|
|
17
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
AuthenticationError,
|
|
54
|
+
AuthorizationError,
|
|
55
|
+
ConnectionError,
|
|
56
|
+
ConstraintError,
|
|
57
|
+
DeadlockError,
|
|
58
|
+
LockTimeoutError,
|
|
59
|
+
SerializationError,
|
|
60
|
+
SqlError,
|
|
61
|
+
SqlSyntaxError,
|
|
62
|
+
UniqueViolation,
|
|
63
|
+
UnknownError
|
|
64
|
+
} from "effect/unstable/sql/SqlError"
|
|
18
65
|
import * as Statement from "effect/unstable/sql/Statement"
|
|
19
66
|
import * as Tedious from "tedious"
|
|
20
67
|
import type { ConnectionOptions } from "tedious/lib/connection.ts"
|
|
@@ -28,21 +75,116 @@ const ATTR_DB_NAMESPACE = "db.namespace"
|
|
|
28
75
|
const ATTR_SERVER_ADDRESS = "server.address"
|
|
29
76
|
const ATTR_SERVER_PORT = "server.port"
|
|
30
77
|
|
|
78
|
+
const mssqlNumberFromCause = (cause: unknown): number | undefined => {
|
|
79
|
+
if (typeof cause !== "object" || cause === null || !("number" in cause)) {
|
|
80
|
+
return undefined
|
|
81
|
+
}
|
|
82
|
+
const number = cause.number
|
|
83
|
+
return typeof number === "number" ? number : undefined
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const mssqlConnectionErrorCodes = new Set([233, 10054])
|
|
87
|
+
const mssqlAuthenticationErrorCodes = new Set([4060, 18452, 18456])
|
|
88
|
+
const mssqlAuthorizationErrorCodes = new Set([229, 230, 262, 297, 300])
|
|
89
|
+
const mssqlSyntaxErrorCodes = new Set([102, 207, 208, 2714])
|
|
90
|
+
const mssqlConstraintErrorCodes = new Set([515, 547])
|
|
91
|
+
|
|
92
|
+
const UNKNOWN_CONSTRAINT = "unknown"
|
|
93
|
+
|
|
94
|
+
const normalizeConstraintIdentifier = (identifier: unknown): string => {
|
|
95
|
+
if (typeof identifier !== "string") {
|
|
96
|
+
return UNKNOWN_CONSTRAINT
|
|
97
|
+
}
|
|
98
|
+
const trimmed = identifier.trim()
|
|
99
|
+
return trimmed.length === 0 ? UNKNOWN_CONSTRAINT : trimmed
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const mssqlCauseProperty = (cause: unknown, property: "constraint" | "message"): unknown => {
|
|
103
|
+
if (typeof cause !== "object" || cause === null || !(property in cause)) {
|
|
104
|
+
return undefined
|
|
105
|
+
}
|
|
106
|
+
return (cause as Record<string, unknown>)[property]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const mssqlUniqueViolationConstraintFromMessage = (number: 2601 | 2627, message: unknown): string => {
|
|
110
|
+
if (typeof message !== "string") {
|
|
111
|
+
return UNKNOWN_CONSTRAINT
|
|
112
|
+
}
|
|
113
|
+
const match = number === 2627 ?
|
|
114
|
+
/\bconstraint\s+'([^']*)'/i.exec(message) :
|
|
115
|
+
/\bunique index\s+'([^']*)'/i.exec(message)
|
|
116
|
+
return match === null ? UNKNOWN_CONSTRAINT : normalizeConstraintIdentifier(match[1])
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const mssqlUniqueViolationConstraintFromCause = (number: 2601 | 2627, cause: unknown): string => {
|
|
120
|
+
const constraint = normalizeConstraintIdentifier(mssqlCauseProperty(cause, "constraint"))
|
|
121
|
+
if (constraint !== UNKNOWN_CONSTRAINT) {
|
|
122
|
+
return constraint
|
|
123
|
+
}
|
|
124
|
+
return mssqlUniqueViolationConstraintFromMessage(number, mssqlCauseProperty(cause, "message"))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const classifyError = (
|
|
128
|
+
cause: unknown,
|
|
129
|
+
message: string,
|
|
130
|
+
operation: string,
|
|
131
|
+
fallback: "connection" | "unknown" = "unknown"
|
|
132
|
+
) => {
|
|
133
|
+
const props = { cause, message, operation }
|
|
134
|
+
const number = mssqlNumberFromCause(cause)
|
|
135
|
+
if (number !== undefined) {
|
|
136
|
+
if (mssqlConnectionErrorCodes.has(number)) {
|
|
137
|
+
return new ConnectionError(props)
|
|
138
|
+
}
|
|
139
|
+
if (mssqlAuthenticationErrorCodes.has(number)) {
|
|
140
|
+
return new AuthenticationError(props)
|
|
141
|
+
}
|
|
142
|
+
if (mssqlAuthorizationErrorCodes.has(number)) {
|
|
143
|
+
return new AuthorizationError(props)
|
|
144
|
+
}
|
|
145
|
+
if (mssqlSyntaxErrorCodes.has(number)) {
|
|
146
|
+
return new SqlSyntaxError(props)
|
|
147
|
+
}
|
|
148
|
+
if (number === 2601 || number === 2627) {
|
|
149
|
+
return new UniqueViolation({ ...props, constraint: mssqlUniqueViolationConstraintFromCause(number, cause) })
|
|
150
|
+
}
|
|
151
|
+
if (mssqlConstraintErrorCodes.has(number)) {
|
|
152
|
+
return new ConstraintError(props)
|
|
153
|
+
}
|
|
154
|
+
if (number === 1205) {
|
|
155
|
+
return new DeadlockError(props)
|
|
156
|
+
}
|
|
157
|
+
if (number === 3960) {
|
|
158
|
+
return new SerializationError(props)
|
|
159
|
+
}
|
|
160
|
+
if (number === 1222) {
|
|
161
|
+
return new LockTimeoutError(props)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return fallback === "connection" ? new ConnectionError(props) : new UnknownError(props)
|
|
165
|
+
}
|
|
166
|
+
|
|
31
167
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
168
|
+
* Runtime type identifier used to mark `MssqlClient` values.
|
|
169
|
+
*
|
|
170
|
+
* @category type IDs
|
|
171
|
+
* @since 4.0.0
|
|
34
172
|
*/
|
|
35
173
|
export const TypeId: unique symbol = Symbol.for("@effect/sql-mssql/MssqlClient")
|
|
36
174
|
|
|
37
175
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
176
|
+
* Type-level identifier used to mark `MssqlClient` values.
|
|
177
|
+
*
|
|
178
|
+
* @category type IDs
|
|
179
|
+
* @since 4.0.0
|
|
40
180
|
*/
|
|
41
181
|
export type TypeId = typeof TypeId
|
|
42
182
|
|
|
43
183
|
/**
|
|
184
|
+
* Microsoft SQL Server client service, extending `SqlClient` with typed parameter fragments and stored procedure calls.
|
|
185
|
+
*
|
|
44
186
|
* @category models
|
|
45
|
-
* @since
|
|
187
|
+
* @since 4.0.0
|
|
46
188
|
*/
|
|
47
189
|
export interface MssqlClient extends Client.SqlClient {
|
|
48
190
|
readonly [TypeId]: TypeId
|
|
@@ -65,14 +207,18 @@ export interface MssqlClient extends Client.SqlClient {
|
|
|
65
207
|
}
|
|
66
208
|
|
|
67
209
|
/**
|
|
210
|
+
* Context tag used to access the `MssqlClient` service.
|
|
211
|
+
*
|
|
68
212
|
* @category tags
|
|
69
|
-
* @since
|
|
213
|
+
* @since 4.0.0
|
|
70
214
|
*/
|
|
71
|
-
export const MssqlClient =
|
|
215
|
+
export const MssqlClient = Context.Service<MssqlClient>("@effect/sql-mssql/MssqlClient")
|
|
72
216
|
|
|
73
217
|
/**
|
|
218
|
+
* Configuration for a Microsoft SQL Server client, including connection, authentication, pool, parameter type, span attribute, and query/result name transform options.
|
|
219
|
+
*
|
|
74
220
|
* @category models
|
|
75
|
-
* @since
|
|
221
|
+
* @since 4.0.0
|
|
76
222
|
*/
|
|
77
223
|
export interface MssqlClientConfig {
|
|
78
224
|
readonly domain?: string | undefined
|
|
@@ -111,14 +257,18 @@ interface MssqlConnection extends Connection {
|
|
|
111
257
|
readonly rollback: (name?: string) => Effect.Effect<void, SqlError>
|
|
112
258
|
}
|
|
113
259
|
|
|
114
|
-
const TransactionConnection = Client.TransactionConnection as unknown as
|
|
260
|
+
const TransactionConnection = Client.TransactionConnection as unknown as (clientId: number) => Context.Service<
|
|
115
261
|
readonly [conn: MssqlConnection, counter: number],
|
|
116
262
|
readonly [conn: MssqlConnection, counter: number]
|
|
117
263
|
>
|
|
118
264
|
|
|
265
|
+
let clientIdCounter = 0
|
|
266
|
+
|
|
119
267
|
/**
|
|
268
|
+
* Creates a scoped Microsoft SQL Server client backed by a connection pool, with transaction and stored procedure support. Streaming queries are not implemented.
|
|
269
|
+
*
|
|
120
270
|
* @category constructors
|
|
121
|
-
* @since
|
|
271
|
+
* @since 4.0.0
|
|
122
272
|
*/
|
|
123
273
|
export const make = (
|
|
124
274
|
options: MssqlClientConfig
|
|
@@ -174,7 +324,9 @@ export const make = (
|
|
|
174
324
|
yield* Effect.callback<void, SqlError>((resume) => {
|
|
175
325
|
conn.connect((cause) => {
|
|
176
326
|
if (cause) {
|
|
177
|
-
resume(
|
|
327
|
+
resume(
|
|
328
|
+
Effect.fail(new SqlError({ reason: classifyError(cause, "Failed to connect", "connect", "connection") }))
|
|
329
|
+
)
|
|
178
330
|
} else {
|
|
179
331
|
resume(Effect.void)
|
|
180
332
|
}
|
|
@@ -189,7 +341,9 @@ export const make = (
|
|
|
189
341
|
Effect.callback<any, SqlError>((resume) => {
|
|
190
342
|
const req = new Tedious.Request(sql, (cause, _rowCount, result) => {
|
|
191
343
|
if (cause) {
|
|
192
|
-
resume(
|
|
344
|
+
resume(
|
|
345
|
+
Effect.fail(new SqlError({ reason: classifyError(cause, "Failed to execute statement", "execute") }))
|
|
346
|
+
)
|
|
193
347
|
return
|
|
194
348
|
}
|
|
195
349
|
|
|
@@ -232,7 +386,9 @@ export const make = (
|
|
|
232
386
|
escape(procedure.name),
|
|
233
387
|
(cause, _, rows) => {
|
|
234
388
|
if (cause) {
|
|
235
|
-
resume(
|
|
389
|
+
resume(
|
|
390
|
+
Effect.fail(new SqlError({ reason: classifyError(cause, "Failed to execute statement", "execute") }))
|
|
391
|
+
)
|
|
236
392
|
} else {
|
|
237
393
|
rows = rowsToObjects(rows)
|
|
238
394
|
if (transformRows) {
|
|
@@ -291,7 +447,13 @@ export const make = (
|
|
|
291
447
|
begin: Effect.callback<void, SqlError>((resume) => {
|
|
292
448
|
conn.beginTransaction((cause) => {
|
|
293
449
|
if (cause) {
|
|
294
|
-
resume(
|
|
450
|
+
resume(
|
|
451
|
+
Effect.fail(
|
|
452
|
+
new SqlError({
|
|
453
|
+
reason: classifyError(cause, "Failed to begin transaction", "beginTransaction")
|
|
454
|
+
})
|
|
455
|
+
)
|
|
456
|
+
)
|
|
295
457
|
} else {
|
|
296
458
|
resume(Effect.void)
|
|
297
459
|
}
|
|
@@ -300,7 +462,13 @@ export const make = (
|
|
|
300
462
|
commit: Effect.callback<void, SqlError>((resume) => {
|
|
301
463
|
conn.commitTransaction((cause) => {
|
|
302
464
|
if (cause) {
|
|
303
|
-
resume(
|
|
465
|
+
resume(
|
|
466
|
+
Effect.fail(
|
|
467
|
+
new SqlError({
|
|
468
|
+
reason: classifyError(cause, "Failed to commit transaction", "commitTransaction")
|
|
469
|
+
})
|
|
470
|
+
)
|
|
471
|
+
)
|
|
304
472
|
} else {
|
|
305
473
|
resume(Effect.void)
|
|
306
474
|
}
|
|
@@ -310,7 +478,11 @@ export const make = (
|
|
|
310
478
|
Effect.callback<void, SqlError>((resume) => {
|
|
311
479
|
conn.saveTransaction((cause) => {
|
|
312
480
|
if (cause) {
|
|
313
|
-
resume(
|
|
481
|
+
resume(
|
|
482
|
+
Effect.fail(
|
|
483
|
+
new SqlError({ reason: classifyError(cause, "Failed to create savepoint", "createSavepoint") })
|
|
484
|
+
)
|
|
485
|
+
)
|
|
314
486
|
} else {
|
|
315
487
|
resume(Effect.void)
|
|
316
488
|
}
|
|
@@ -320,7 +492,13 @@ export const make = (
|
|
|
320
492
|
Effect.callback<void, SqlError>((resume) => {
|
|
321
493
|
conn.rollbackTransaction((cause) => {
|
|
322
494
|
if (cause) {
|
|
323
|
-
resume(
|
|
495
|
+
resume(
|
|
496
|
+
Effect.fail(
|
|
497
|
+
new SqlError({
|
|
498
|
+
reason: classifyError(cause, "Failed to rollback transaction", "rollbackTransaction")
|
|
499
|
+
})
|
|
500
|
+
)
|
|
501
|
+
)
|
|
324
502
|
} else {
|
|
325
503
|
resume(Effect.void)
|
|
326
504
|
}
|
|
@@ -349,22 +527,29 @@ export const make = (
|
|
|
349
527
|
|
|
350
528
|
yield* Pool.get(pool).pipe(
|
|
351
529
|
Effect.tap((connection) => connection.executeUnprepared("SELECT 1", [], undefined)),
|
|
352
|
-
Effect.mapError((
|
|
530
|
+
Effect.mapError((cause) =>
|
|
531
|
+
new SqlError({ reason: classifyError(cause, "MssqlClient: Failed to connect", "connect", "connection") })
|
|
532
|
+
),
|
|
353
533
|
Effect.scoped,
|
|
354
534
|
Effect.timeoutOrElse({
|
|
355
535
|
duration: options.connectTimeout ?? Duration.seconds(5),
|
|
356
|
-
|
|
536
|
+
orElse: () =>
|
|
357
537
|
Effect.fail(
|
|
358
538
|
new SqlError({
|
|
359
|
-
|
|
360
|
-
|
|
539
|
+
reason: new ConnectionError({
|
|
540
|
+
message: "MssqlClient: Connection timeout",
|
|
541
|
+
cause: new Error("connection timeout"),
|
|
542
|
+
operation: "connect"
|
|
543
|
+
})
|
|
361
544
|
})
|
|
362
545
|
)
|
|
363
546
|
})
|
|
364
547
|
)
|
|
365
548
|
|
|
549
|
+
const transactionService = TransactionConnection(clientIdCounter++)
|
|
550
|
+
|
|
366
551
|
const withTransaction = Client.makeWithTransaction({
|
|
367
|
-
transactionService
|
|
552
|
+
transactionService,
|
|
368
553
|
spanAttributes,
|
|
369
554
|
acquireConnection: Effect.gen(function*() {
|
|
370
555
|
const scope = Scope.makeUnsafe()
|
|
@@ -382,6 +567,7 @@ export const make = (
|
|
|
382
567
|
yield* Client.make({
|
|
383
568
|
acquirer: Pool.get(pool),
|
|
384
569
|
compiler,
|
|
570
|
+
transactionService: transactionService as any,
|
|
385
571
|
spanAttributes,
|
|
386
572
|
transformRows
|
|
387
573
|
}),
|
|
@@ -426,42 +612,48 @@ export const make = (
|
|
|
426
612
|
})
|
|
427
613
|
|
|
428
614
|
/**
|
|
615
|
+
* Creates a layer from a `Config`-wrapped SQL Server client configuration, providing both `MssqlClient` and `SqlClient`.
|
|
616
|
+
*
|
|
429
617
|
* @category layers
|
|
430
|
-
* @since
|
|
618
|
+
* @since 4.0.0
|
|
431
619
|
*/
|
|
432
620
|
export const layerConfig: (
|
|
433
621
|
config: Config.Wrap<MssqlClientConfig>
|
|
434
622
|
) => Layer.Layer<Client.SqlClient | MssqlClient, Config.ConfigError | SqlError> = (
|
|
435
623
|
config: Config.Wrap<MssqlClientConfig>
|
|
436
624
|
): Layer.Layer<Client.SqlClient | MssqlClient, Config.ConfigError | SqlError> =>
|
|
437
|
-
Layer.
|
|
438
|
-
Config.unwrap(config).
|
|
625
|
+
Layer.effectContext(
|
|
626
|
+
Config.unwrap(config).pipe(
|
|
439
627
|
Effect.flatMap(make),
|
|
440
628
|
Effect.map((client) =>
|
|
441
|
-
|
|
442
|
-
|
|
629
|
+
Context.make(MssqlClient, client).pipe(
|
|
630
|
+
Context.add(Client.SqlClient, client)
|
|
443
631
|
)
|
|
444
632
|
)
|
|
445
633
|
)
|
|
446
634
|
).pipe(Layer.provide(Reactivity.layer))
|
|
447
635
|
|
|
448
636
|
/**
|
|
637
|
+
* Creates a layer from a concrete SQL Server client configuration, providing both `MssqlClient` and `SqlClient`.
|
|
638
|
+
*
|
|
449
639
|
* @category layers
|
|
450
|
-
* @since
|
|
640
|
+
* @since 4.0.0
|
|
451
641
|
*/
|
|
452
642
|
export const layer = (
|
|
453
643
|
config: MssqlClientConfig
|
|
454
644
|
): Layer.Layer<Client.SqlClient | MssqlClient, never | SqlError> =>
|
|
455
|
-
Layer.
|
|
645
|
+
Layer.effectContext(
|
|
456
646
|
Effect.map(make(config), (client) =>
|
|
457
|
-
|
|
458
|
-
|
|
647
|
+
Context.make(MssqlClient, client).pipe(
|
|
648
|
+
Context.add(Client.SqlClient, client)
|
|
459
649
|
))
|
|
460
650
|
).pipe(Layer.provide(Reactivity.layer))
|
|
461
651
|
|
|
462
652
|
/**
|
|
653
|
+
* Creates the SQL Server statement compiler, using `@1`-style placeholders, bracket-escaped identifiers, and SQL Server `OUTPUT INSERTED` returning clauses.
|
|
654
|
+
*
|
|
463
655
|
* @category compiler
|
|
464
|
-
* @since
|
|
656
|
+
* @since 4.0.0
|
|
465
657
|
*/
|
|
466
658
|
export const makeCompiler = (transform?: (_: string) => string) =>
|
|
467
659
|
Statement.makeCompiler<MssqlCustom>({
|
|
@@ -510,7 +702,10 @@ function numberToParamName(n: number) {
|
|
|
510
702
|
}
|
|
511
703
|
|
|
512
704
|
/**
|
|
513
|
-
*
|
|
705
|
+
* Default mapping from Effect SQL primitive value kinds to Tedious SQL Server parameter data types.
|
|
706
|
+
*
|
|
707
|
+
* @category configuration
|
|
708
|
+
* @since 4.0.0
|
|
514
709
|
*/
|
|
515
710
|
export const defaultParameterTypes: Record<Statement.PrimitiveKind, DataType> = {
|
|
516
711
|
string: Tedious.TYPES.VarChar,
|
package/src/MssqlMigrator.ts
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Utilities for applying Effect SQL migrations to Microsoft SQL Server.
|
|
3
|
+
*
|
|
4
|
+
* This module re-exports the shared `Migrator` loaders and error types, then
|
|
5
|
+
* provides `run` and `layer` helpers for applying ordered migrations through
|
|
6
|
+
* the current SQL Server `SqlClient`. It is typically used at application
|
|
7
|
+
* startup, during deployment, in integration tests that provision temporary SQL
|
|
8
|
+
* Server databases, or in layer graphs that need the database schema to be
|
|
9
|
+
* current before dependent services are acquired.
|
|
10
|
+
*
|
|
11
|
+
* Applied migrations are stored in `effect_sql_migrations` by default and use
|
|
12
|
+
* the shared `<id>_<name>` loader convention. Only migrations with ids greater
|
|
13
|
+
* than the latest recorded id are run, so avoid inserting older migration ids
|
|
14
|
+
* after a later migration has reached production. SQL Server migrations run
|
|
15
|
+
* inside the shared migrator transaction and this adapter does not add a
|
|
16
|
+
* dialect-specific table lock, so coordinate concurrent startup runners and
|
|
17
|
+
* write T-SQL that is valid inside an explicit transaction. Remember that `GO`
|
|
18
|
+
* is a client-side batch separator rather than a T-SQL statement, and split
|
|
19
|
+
* migrations when SQL Server requires an object definition such as `CREATE
|
|
20
|
+
* VIEW`, `CREATE PROCEDURE`, or `CREATE TRIGGER` to start its own batch. This
|
|
21
|
+
* adapter also does not emit SQL Server schema dumps for `schemaDirectory`.
|
|
22
|
+
*
|
|
23
|
+
* @since 4.0.0
|
|
3
24
|
*/
|
|
4
25
|
import type * as Effect from "effect/Effect"
|
|
5
26
|
import * as Layer from "effect/Layer"
|
|
@@ -8,13 +29,15 @@ import type * as Client from "effect/unstable/sql/SqlClient"
|
|
|
8
29
|
import type { SqlError } from "effect/unstable/sql/SqlError"
|
|
9
30
|
|
|
10
31
|
/**
|
|
11
|
-
* @since
|
|
32
|
+
* @since 4.0.0
|
|
12
33
|
*/
|
|
13
34
|
export * from "effect/unstable/sql/Migrator"
|
|
14
35
|
|
|
15
36
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
37
|
+
* Runs SQL migrations using the configured `SqlClient`, returning the migrations that were applied.
|
|
38
|
+
*
|
|
39
|
+
* @category constructors
|
|
40
|
+
* @since 4.0.0
|
|
18
41
|
*/
|
|
19
42
|
export const run: <R>(
|
|
20
43
|
options: Migrator.MigratorOptions<R>
|
|
@@ -25,8 +48,10 @@ export const run: <R>(
|
|
|
25
48
|
> = Migrator.make({})
|
|
26
49
|
|
|
27
50
|
/**
|
|
51
|
+
* Creates a layer that runs the configured SQL migrations during layer construction.
|
|
52
|
+
*
|
|
28
53
|
* @category layers
|
|
29
|
-
* @since
|
|
54
|
+
* @since 4.0.0
|
|
30
55
|
*/
|
|
31
56
|
export const layer = <R>(
|
|
32
57
|
options: Migrator.MigratorOptions<R>
|
package/src/Parameter.ts
CHANGED
|
@@ -1,25 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Typed SQL Server stored procedure parameter metadata.
|
|
3
|
+
*
|
|
4
|
+
* This module builds {@link Parameter} values that pair a stored procedure
|
|
5
|
+
* parameter name with a Tedious `DataType`, Tedious `ParameterOptions`, and a
|
|
6
|
+
* phantom TypeScript value type. `Procedure.param` and
|
|
7
|
+
* `Procedure.outputParam` use this metadata, and `MssqlClient.call` forwards it
|
|
8
|
+
* to Tedious when registering input and output parameters.
|
|
9
|
+
*
|
|
10
|
+
* **Mental model**
|
|
11
|
+
*
|
|
12
|
+
* A `Parameter<A>` describes how Tedious should bind a value; it is not the
|
|
13
|
+
* value itself. The `A` type guides the record accepted by
|
|
14
|
+
* `Procedure.compile`, while Tedious validates and encodes the runtime value
|
|
15
|
+
* when the request is executed.
|
|
16
|
+
*
|
|
17
|
+
* **Common tasks**
|
|
18
|
+
*
|
|
19
|
+
* - Annotate inputs that need explicit SQL Server data types, sizes, precision,
|
|
20
|
+
* scale, or table-valued parameter options.
|
|
21
|
+
* - Define output parameters so `MssqlClient.call` can collect returned values
|
|
22
|
+
* by name.
|
|
23
|
+
* - Reuse the same metadata shape from direct `make` calls and the `Procedure`
|
|
24
|
+
* builders.
|
|
25
|
+
*
|
|
26
|
+
* **Gotchas**
|
|
27
|
+
*
|
|
28
|
+
* Names should match the stored procedure parameter name expected by Tedious,
|
|
29
|
+
* normally without a leading `@`. Table-valued parameter values must use
|
|
30
|
+
* Tedious' table shape with `name`, optional `schema`, `columns`, and `rows`.
|
|
31
|
+
* Output parameters are registered without an initial value, so input-output
|
|
32
|
+
* parameters need explicit modeling instead of assuming compiled input values
|
|
33
|
+
* are reused.
|
|
34
|
+
*
|
|
35
|
+
* @see {@link make} for constructing parameter metadata directly.
|
|
36
|
+
*
|
|
37
|
+
* @since 4.0.0
|
|
3
38
|
*/
|
|
4
39
|
import { identity } from "effect/Function"
|
|
5
40
|
import type { DataType } from "tedious/lib/data-type.ts"
|
|
6
41
|
import type { ParameterOptions } from "tedious/lib/request.ts"
|
|
7
42
|
|
|
8
43
|
/**
|
|
44
|
+
* Runtime type identifier used to mark SQL Server stored procedure parameter metadata.
|
|
45
|
+
*
|
|
9
46
|
* @category type id
|
|
10
|
-
* @since
|
|
47
|
+
* @since 4.0.0
|
|
11
48
|
*/
|
|
12
49
|
export const TypeId: TypeId = "~@effect/sql-mssql/Parameter"
|
|
13
50
|
|
|
14
51
|
/**
|
|
52
|
+
* Type-level identifier used to mark SQL Server stored procedure parameter metadata.
|
|
53
|
+
*
|
|
15
54
|
* @category type id
|
|
16
|
-
* @since
|
|
55
|
+
* @since 4.0.0
|
|
17
56
|
*/
|
|
18
57
|
export type TypeId = "~@effect/sql-mssql/Parameter"
|
|
19
58
|
|
|
20
59
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
60
|
+
* Metadata for a SQL Server stored procedure parameter, including its name, Tedious data type, options, and phantom value type.
|
|
61
|
+
*
|
|
62
|
+
* @category models
|
|
63
|
+
* @since 4.0.0
|
|
23
64
|
*/
|
|
24
65
|
export interface Parameter<out A> {
|
|
25
66
|
readonly [TypeId]: (_: never) => A
|
|
@@ -30,8 +71,10 @@ export interface Parameter<out A> {
|
|
|
30
71
|
}
|
|
31
72
|
|
|
32
73
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
74
|
+
* Creates typed metadata for a SQL Server stored procedure parameter.
|
|
75
|
+
*
|
|
76
|
+
* @category constructors
|
|
77
|
+
* @since 4.0.0
|
|
35
78
|
*/
|
|
36
79
|
export const make = <A>(
|
|
37
80
|
name: string,
|