@shivaedev/sql 0.36.1 → 0.38.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 (125) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +91 -1
  3. package/dist/Model.d.ts +9 -0
  4. package/dist/Model.d.ts.map +1 -0
  5. package/dist/Model.js +6 -0
  6. package/dist/Model.js.map +1 -0
  7. package/dist/VersionedRepositoryShape.d.ts +6 -0
  8. package/dist/VersionedRepositoryShape.d.ts.map +1 -0
  9. package/dist/VersionedRepositoryShape.js +2 -0
  10. package/dist/VersionedRepositoryShape.js.map +1 -0
  11. package/dist/WorkerRelease.d.ts +1 -8
  12. package/dist/WorkerRelease.d.ts.map +1 -1
  13. package/dist/WorkerRelease.js +4 -29
  14. package/dist/WorkerRelease.js.map +1 -1
  15. package/dist/adapters/groupColumns.d.ts +14 -0
  16. package/dist/adapters/groupColumns.d.ts.map +1 -0
  17. package/dist/adapters/groupColumns.js +54 -0
  18. package/dist/adapters/groupColumns.js.map +1 -0
  19. package/dist/adapters/storageSchema.d.ts +4 -0
  20. package/dist/adapters/storageSchema.d.ts.map +1 -0
  21. package/dist/adapters/storageSchema.js +7 -0
  22. package/dist/adapters/storageSchema.js.map +1 -0
  23. package/dist/backfillPostgres.d.ts +18 -0
  24. package/dist/backfillPostgres.d.ts.map +1 -0
  25. package/dist/backfillPostgres.js +35 -0
  26. package/dist/backfillPostgres.js.map +1 -0
  27. package/dist/batchWrites.d.ts.map +1 -1
  28. package/dist/batchWrites.js +6 -3
  29. package/dist/batchWrites.js.map +1 -1
  30. package/dist/compositeRepository.d.ts.map +1 -1
  31. package/dist/compositeRepository.js +4 -1
  32. package/dist/compositeRepository.js.map +1 -1
  33. package/dist/conditionalUpsert.d.ts.map +1 -1
  34. package/dist/conditionalUpsert.js +5 -2
  35. package/dist/conditionalUpsert.js.map +1 -1
  36. package/dist/conflictWrites.d.ts.map +1 -1
  37. package/dist/conflictWrites.js +6 -3
  38. package/dist/conflictWrites.js.map +1 -1
  39. package/dist/coordinateWorkerRelease.js +1 -1
  40. package/dist/coordinateWorkerRelease.js.map +1 -1
  41. package/dist/initializeWorkerRelease.d.ts +7 -0
  42. package/dist/initializeWorkerRelease.d.ts.map +1 -0
  43. package/dist/initializeWorkerRelease.js +7 -0
  44. package/dist/initializeWorkerRelease.js.map +1 -0
  45. package/dist/insertFromSelect.d.ts.map +1 -1
  46. package/dist/insertFromSelect.js +4 -1
  47. package/dist/insertFromSelect.js.map +1 -1
  48. package/dist/latestPerGroup.d.ts.map +1 -1
  49. package/dist/latestPerGroup.js +7 -3
  50. package/dist/latestPerGroup.js.map +1 -1
  51. package/dist/makeOrderedChildren.d.ts +26 -0
  52. package/dist/makeOrderedChildren.d.ts.map +1 -0
  53. package/dist/makeOrderedChildren.js +38 -0
  54. package/dist/makeOrderedChildren.js.map +1 -0
  55. package/dist/makeWorkerRelease.d.ts +8 -0
  56. package/dist/makeWorkerRelease.d.ts.map +1 -0
  57. package/dist/makeWorkerRelease.js +12 -0
  58. package/dist/makeWorkerRelease.js.map +1 -0
  59. package/dist/orderedChildStatements.d.ts +30 -0
  60. package/dist/orderedChildStatements.d.ts.map +1 -0
  61. package/dist/orderedChildStatements.js +61 -0
  62. package/dist/orderedChildStatements.js.map +1 -0
  63. package/dist/registerWorkerRelease.d.ts +4 -0
  64. package/dist/registerWorkerRelease.d.ts.map +1 -0
  65. package/dist/registerWorkerRelease.js +14 -0
  66. package/dist/registerWorkerRelease.js.map +1 -0
  67. package/dist/repository.d.ts +8 -8
  68. package/dist/repository.d.ts.map +1 -1
  69. package/dist/repository.js.map +1 -1
  70. package/dist/repositoryQueries.d.ts.map +1 -1
  71. package/dist/repositoryQueries.js +6 -2
  72. package/dist/repositoryQueries.js.map +1 -1
  73. package/dist/repositoryUpdate.d.ts +3 -3
  74. package/dist/repositoryUpdate.d.ts.map +1 -1
  75. package/dist/repositoryWrites.d.ts.map +1 -1
  76. package/dist/repositoryWrites.js +21 -6
  77. package/dist/repositoryWrites.js.map +1 -1
  78. package/dist/setWorkerReleaseActive.d.ts +4 -0
  79. package/dist/setWorkerReleaseActive.d.ts.map +1 -0
  80. package/dist/setWorkerReleaseActive.js +13 -0
  81. package/dist/setWorkerReleaseActive.js.map +1 -0
  82. package/dist/sqlNameTransforms.d.ts +5 -0
  83. package/dist/sqlNameTransforms.d.ts.map +1 -0
  84. package/dist/sqlNameTransforms.js +8 -0
  85. package/dist/sqlNameTransforms.js.map +1 -0
  86. package/dist/upsertResult.d.ts +2 -1
  87. package/dist/upsertResult.d.ts.map +1 -1
  88. package/dist/upsertResult.js +3 -2
  89. package/dist/upsertResult.js.map +1 -1
  90. package/dist/versionedRepository.d.ts +15 -6
  91. package/dist/versionedRepository.d.ts.map +1 -1
  92. package/dist/versionedRepository.js +8 -5
  93. package/dist/versionedRepository.js.map +1 -1
  94. package/dist/versionedStatements.d.ts +1 -1
  95. package/dist/versionedStatements.d.ts.map +1 -1
  96. package/dist/versionedStatements.js +10 -4
  97. package/dist/versionedStatements.js.map +1 -1
  98. package/package.json +11 -11
  99. package/src/Model.ts +7 -0
  100. package/src/VersionedRepositoryShape.ts +11 -0
  101. package/src/WorkerRelease.ts +4 -33
  102. package/src/adapters/groupColumns.ts +63 -0
  103. package/src/adapters/storageSchema.ts +10 -0
  104. package/src/backfillPostgres.ts +47 -0
  105. package/src/batchWrites.ts +6 -3
  106. package/src/compositeRepository.ts +4 -1
  107. package/src/conditionalUpsert.ts +5 -2
  108. package/src/conflictWrites.ts +6 -3
  109. package/src/coordinateWorkerRelease.ts +1 -1
  110. package/src/initializeWorkerRelease.ts +7 -0
  111. package/src/insertFromSelect.ts +4 -1
  112. package/src/latestPerGroup.ts +7 -3
  113. package/src/makeOrderedChildren.ts +78 -0
  114. package/src/makeWorkerRelease.ts +12 -0
  115. package/src/orderedChildStatements.ts +76 -0
  116. package/src/registerWorkerRelease.ts +15 -0
  117. package/src/repository.ts +12 -13
  118. package/src/repositoryQueries.ts +6 -2
  119. package/src/repositoryUpdate.ts +3 -3
  120. package/src/repositoryWrites.ts +21 -6
  121. package/src/setWorkerReleaseActive.ts +17 -0
  122. package/src/sqlNameTransforms.ts +9 -0
  123. package/src/upsertResult.ts +4 -2
  124. package/src/versionedRepository.ts +31 -11
  125. package/src/versionedStatements.ts +14 -4
@@ -3,12 +3,15 @@ import type { Model } from "effect/schema";
3
3
  import { SqlClient } from "effect/sql";
4
4
  import type { SqlError } from "effect/sql/SqlError";
5
5
  import type { Fragment } from "effect/sql/Statement";
6
+ import { groupColumns } from "#adapters/groupColumns.ts";
7
+ import { storageSchema } from "#adapters/storageSchema.ts";
6
8
  import type { Aggregate } from "#aggregateQuery.ts";
7
9
  import { latestPerGroup as makeLatestPerGroup } from "#latestPerGroup.ts";
8
10
  import { makeAggregate } from "#makeAggregate.ts";
9
11
  import { makeQueryClauses } from "#makeQueryClauses.ts";
10
12
  import type { FindMany, LatestPerGroup } from "#repository.ts";
11
13
  import { makeFilter } from "#repositoryFilter.ts";
14
+ import { sqlNameTransforms } from "#sqlNameTransforms.ts";
12
15
 
13
16
  type Key<TModel extends Model.Any> = Extract<keyof TModel["fields"] & keyof Row<TModel>, string>;
14
17
  type Row<TModel extends Model.Any> = Schema.Struct.Type<TModel["fields"]>;
@@ -53,6 +56,7 @@ export function makeQueries<TModel extends Model.Any>(
53
56
  ): Effect.Effect<Queries<TModel>, never, SqlClient.SqlClient> {
54
57
  return Effect.fnUntraced(function* () {
55
58
  const sql = yield* SqlClient.SqlClient;
59
+ const nameTransforms = yield* sqlNameTransforms;
56
60
  const { fieldAt, where } = yield* makeFilter(model);
57
61
  const readLatest = yield* makeLatestPerGroup(model, options.tableName);
58
62
  const { decodeLimit, orderClause, limitClause } = yield* makeQueryClauses(fieldAt);
@@ -77,7 +81,7 @@ export function makeQueries<TModel extends Model.Any>(
77
81
  const predicate = yield* where(query.where, true, true);
78
82
  const order = yield* orderClause(query.orderBy);
79
83
  const limit = yield* limitClause(query.limit);
80
- return sql`select ${sql.csv(keys.map((key) => sql`${sql(key)}`))} from ${sql(options.tableName)} where ${predicate} ${order} ${limit}`;
84
+ return sql`select ${sql.csv(groupColumns(model.fields, keys).map(({ column }) => sql`${sql(column)}`))} from ${sql(options.tableName)} where ${predicate} ${order} ${limit}`;
81
85
  });
82
86
  return { fields, statement };
83
87
  }
@@ -93,7 +97,7 @@ export function makeQueries<TModel extends Model.Any>(
93
97
  return Effect.gen(function* () {
94
98
  const selected = select(query);
95
99
  const rows = yield* sql`${yield* selected.statement}`;
96
- return yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct(selected.fields)))(rows);
100
+ return yield* Schema.decodeUnknownEffect(Schema.Array(storageSchema(Schema.Struct(selected.fields), selected.fields, nameTransforms)))(rows);
97
101
  }).pipe(Effect.withSpan(`${options.spanPrefix}.findMany`));
98
102
  }
99
103
  function latestPerGroup(query: LatestPerGroup<TModel>): ReturnType<Queries<TModel>["latestPerGroup"]>;
@@ -8,11 +8,11 @@ export type UpdateInput<TModel extends Model.Any> = {
8
8
  readonly [TKey in keyof TModel["update"]["Type"]]: WithoutOverride<TModel["update"]["Type"][TKey]>;
9
9
  };
10
10
 
11
- type Column<TModel extends Model.Any, TId extends PropertyKey> = Exclude<Extract<keyof UpdateInput<TModel>, string>, TId>;
12
- type Selected<TModel extends Model.Any, TId extends PropertyKey, TColumn extends PropertyKey> = Required<
11
+ export type Column<TModel extends Model.Any, TId extends PropertyKey> = Exclude<Extract<keyof UpdateInput<TModel>, string>, TId>;
12
+ export type Selected<TModel extends Model.Any, TId extends PropertyKey, TColumn extends PropertyKey> = Required<
13
13
  Pick<UpdateInput<TModel>, Extract<TId | TColumn, keyof UpdateInput<TModel>>>
14
14
  >;
15
- interface Columns<TColumn> {
15
+ export interface Columns<TColumn> {
16
16
  readonly columns: readonly [TColumn, ...TColumn[]];
17
17
  }
18
18
  type WriteResult<TModel extends Model.Any, A, E = never> = Effect.Effect<
@@ -1,9 +1,11 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import type { Model } from "effect/schema";
3
3
  import { SqlClient, SqlSchema } from "effect/sql";
4
+ import { storageSchema } from "#adapters/storageSchema.ts";
4
5
  import type { RepositoryChanges } from "#repositoryChanges.ts";
5
6
  import { makeFilter } from "#repositoryFilter.ts";
6
7
  import { selectedUpdate } from "#repositoryUpdate.ts";
8
+ import { sqlNameTransforms } from "#sqlNameTransforms.ts";
7
9
 
8
10
  export const repositoryWrites = Effect.fnUntraced(function* (
9
11
  model: Model.Any,
@@ -11,6 +13,7 @@ export const repositoryWrites = Effect.fnUntraced(function* (
11
13
  changes: RepositoryChanges,
12
14
  ) {
13
15
  const sql = yield* SqlClient.SqlClient;
16
+ const nameTransforms = yield* sqlNameTransforms;
14
17
  const { idColumns, tableName, spanPrefix } = options;
15
18
  const { where } = yield* makeFilter(model);
16
19
  const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
@@ -33,17 +36,29 @@ export const repositoryWrites = Effect.fnUntraced(function* (
33
36
  ),
34
37
  );
35
38
  }
36
- const insertRow = SqlSchema.findOne({ execute: insert, Request: model.insert, Result: model });
37
- const insertVoid = SqlSchema.void({ execute: insert, Request: model.insert });
38
- const updateRow = SqlSchema.findOne({ execute: update, Request: model.update, Result: model });
39
- const updateVoid = SqlSchema.void({ execute: update, Request: model.update });
39
+ const insertRow = SqlSchema.findOne({
40
+ execute: insert,
41
+ Request: storageSchema(model.insert, model.fields, nameTransforms),
42
+ Result: storageSchema(model, model.fields, nameTransforms),
43
+ });
44
+ const insertVoid = SqlSchema.void({ execute: insert, Request: storageSchema(model.insert, model.fields, nameTransforms) });
45
+ const updateRow = SqlSchema.findOne({
46
+ execute: update,
47
+ Request: storageSchema(model.update, model.fields, nameTransforms),
48
+ Result: storageSchema(model, model.fields, nameTransforms),
49
+ });
50
+ const updateVoid = SqlSchema.void({ execute: update, Request: storageSchema(model.update, model.fields, nameTransforms) });
40
51
  function selected(input: unknown, columns: readonly string[], returning: boolean) {
41
52
  return Effect.gen(function* () {
42
53
  const row = yield* record(input);
43
54
  const Request = yield* selectedUpdate(model, columns, idColumns, row);
44
55
  return yield* returning
45
- ? SqlSchema.findOne({ execute: update, Request, Result: model })(row)
46
- : SqlSchema.void({ execute: update, Request })(row);
56
+ ? SqlSchema.findOne({
57
+ execute: update,
58
+ Request: storageSchema(Request, model.fields, nameTransforms),
59
+ Result: storageSchema(model, model.fields, nameTransforms),
60
+ })(row)
61
+ : SqlSchema.void({ execute: update, Request: storageSchema(Request, model.fields, nameTransforms) })(row);
47
62
  });
48
63
  }
49
64
  const idColumn = idColumns[0] ?? "";
@@ -0,0 +1,17 @@
1
+ import { Effect } from "effect";
2
+ import type { initializeWorkerRelease } from "./initializeWorkerRelease.ts";
3
+
4
+ export function setWorkerReleaseActive(state: Effect.Success<typeof initializeWorkerRelease>) {
5
+ return (next: boolean) =>
6
+ state
7
+ .locked(
8
+ Effect.gen(function* () {
9
+ if (next === state.active) {
10
+ return;
11
+ }
12
+ state.active = next;
13
+ yield* Effect.forEach(state.controls, (control) => control(state.active), { concurrency: "unbounded", discard: true });
14
+ }),
15
+ )
16
+ .pipe(Effect.uninterruptible);
17
+ }
@@ -0,0 +1,9 @@
1
+ import { Effect } from "effect";
2
+ import { SqlClient } from "effect/sql";
3
+ import type { SqlNameTransforms } from "#clientOptions.ts";
4
+
5
+ export const sqlNameTransforms = Effect.gen(function* () {
6
+ const client = yield* SqlClient.SqlClient;
7
+ const namedClient: SqlClient.SqlClient & { readonly config?: SqlNameTransforms } = client;
8
+ return namedClient.config;
9
+ });
@@ -1,6 +1,8 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import type { Model } from "effect/schema";
3
3
  import { SqlClient } from "effect/sql";
4
+ import { storageSchema } from "#adapters/storageSchema.ts";
5
+ import type { SqlNameTransforms } from "#clientOptions.ts";
4
6
 
5
7
  export interface UpsertRow<TRow> {
6
8
  readonly inserted: boolean;
@@ -12,8 +14,8 @@ export const upsertReturning = Effect.fnUntraced(function* () {
12
14
  return sql`*, (xmax = 0) as ${sql("effectSqlInserted")}`;
13
15
  });
14
16
 
15
- export function makeUpsertDecoder(model: Model.Any) {
16
- const decode = Schema.decodeUnknownEffect(model);
17
+ export function makeUpsertDecoder(model: Model.Any, names?: SqlNameTransforms) {
18
+ const decode = Schema.decodeUnknownEffect(storageSchema(model, model.fields, names));
17
19
  return (row: Readonly<Record<string, unknown>> | undefined) =>
18
20
  Effect.map(decode(row), (decoded) => ({ inserted: row?.effectSqlInserted === true, row: decoded }));
19
21
  }
@@ -2,10 +2,12 @@ import { Effect, Option, Schema } from "effect";
2
2
  import type { Model } from "effect/schema";
3
3
  import { SqlClient, type SqlModel } from "effect/sql";
4
4
  import type { SqlError } from "effect/sql/SqlError";
5
+ import { storageSchema } from "#adapters/storageSchema.ts";
5
6
  import type { PrimaryKey, Repository } from "#compositeRepository.ts";
6
7
  import type { OptimisticConcurrencyError } from "#repository.ts";
7
8
  import type { RepositoryChanges } from "#repositoryChanges.ts";
8
- import type { UpdateInput } from "#repositoryUpdate.ts";
9
+ import type { Column, Columns, Selected, UpdateInput } from "#repositoryUpdate.ts";
10
+ import { sqlNameTransforms } from "#sqlNameTransforms.ts";
9
11
  import { type VersionedOptions, versionedStatements } from "#versionedStatements.ts";
10
12
 
11
13
  export type IdColumn<TModel extends Model.Any> = keyof TModel["Type"] & keyof TModel["update"]["Type"] & keyof TModel["fields"];
@@ -30,17 +32,34 @@ type WriteError = SqlError | Schema.SchemaError | OptimisticConcurrencyError;
30
32
  type KeyColumn<TModel extends Model.Any, TId extends PrimaryKey<TModel>> = TId extends readonly IdColumn<TModel>[]
31
33
  ? TId[number]
32
34
  : Extract<TId, IdColumn<TModel>>;
33
- export interface VersionedWrites<TModel extends Model.Any, TId extends PrimaryKey<TModel>> {
35
+ type RequiredColumn<TModel extends Model.Any, TId extends PrimaryKey<TModel>, TVersion extends PropertyKey> = KeyColumn<TModel, TId> | TVersion;
36
+ interface VersionedUpdate<
37
+ TModel extends Model.Any,
38
+ TId extends PrimaryKey<TModel>,
39
+ TVersion extends VersionColumn<TModel, TId>,
40
+ A,
41
+ R = TModel["update"]["EncodingServices"] | TModel["DecodingServices"],
42
+ > {
43
+ <TColumn extends Column<TModel, RequiredColumn<TModel, TId, TVersion>>>(
44
+ input: Selected<TModel, RequiredColumn<TModel, TId, TVersion>, TColumn>,
45
+ options: Columns<TColumn>,
46
+ ): Effect.Effect<A, WriteError, R>;
47
+ (input: UpdateInput<TModel>): Effect.Effect<A, WriteError, R>;
48
+ }
49
+
50
+ export interface VersionedWrites<
51
+ TModel extends Model.Any,
52
+ TId extends PrimaryKey<TModel>,
53
+ TVersion extends VersionColumn<TModel, TId> = VersionColumn<TModel, TId>,
54
+ > {
34
55
  readonly delete: (
35
56
  id: TId extends readonly IdColumn<TModel>[] ? Pick<TModel["Type"], TId[number]> : TModel["fields"][Extract<TId, IdColumn<TModel>>]["Type"],
36
57
  expectedVersion: number,
37
58
  ) => Effect.Effect<void, WriteError, TModel["fields"][KeyColumn<TModel, TId>]["EncodingServices"]>;
38
59
  readonly insert: Repository<TModel, TId>["insert"];
39
60
  readonly insertVoid: Repository<TModel, TId>["insertVoid"];
40
- readonly update: (
41
- input: UpdateInput<TModel>,
42
- ) => Effect.Effect<TModel["Type"], WriteError, TModel["update"]["EncodingServices"] | TModel["DecodingServices"]>;
43
- readonly updateVoid: (input: UpdateInput<TModel>) => Effect.Effect<void, WriteError, TModel["update"]["EncodingServices"]>;
61
+ readonly update: VersionedUpdate<TModel, TId, TVersion, TModel["Type"]>;
62
+ readonly updateVoid: VersionedUpdate<TModel, TId, TVersion, void, TModel["update"]["EncodingServices"]>;
44
63
  }
45
64
 
46
65
  export const makeVersionedWrites = Effect.fnUntraced(function* (
@@ -48,9 +67,10 @@ export const makeVersionedWrites = Effect.fnUntraced(function* (
48
67
  options: VersionedOptions & { readonly changes: RepositoryChanges; readonly spanPrefix: string },
49
68
  ) {
50
69
  const sql = yield* SqlClient.SqlClient;
70
+ const nameTransforms = yield* sqlNameTransforms;
51
71
  const { spanPrefix, changes } = options;
52
72
  const statements = yield* versionedStatements(model, options);
53
- const decodeRow = Schema.decodeUnknownEffect(model);
73
+ const decodeRow = Schema.decodeUnknownEffect(storageSchema(model, model.fields, nameTransforms));
54
74
  function insert(input: unknown) {
55
75
  return Effect.gen(function* () {
56
76
  const statement = yield* statements.insert(input);
@@ -74,9 +94,9 @@ export const makeVersionedWrites = Effect.fnUntraced(function* (
74
94
  return rows.length === 0 ? Option.none() : Option.some(yield* decodeRow(rows[0]));
75
95
  }).pipe(changes.write, Effect.withSpan(`${spanPrefix}.insertOrIgnore`));
76
96
  }
77
- function update(input: Readonly<Record<string, unknown>>) {
97
+ function update(input: Readonly<Record<string, unknown>>, settings?: { readonly columns: readonly string[] }) {
78
98
  return Effect.gen(function* () {
79
- const { statement, classify } = yield* statements.update(input);
99
+ const { statement, classify } = yield* statements.update(input, settings?.columns);
80
100
  const rows = yield* sql`${statement} returning ${changes.returning()}`;
81
101
  if (rows.length === 0) {
82
102
  return yield* classify();
@@ -85,9 +105,9 @@ export const makeVersionedWrites = Effect.fnUntraced(function* (
85
105
  return yield* decodeRow(rows[0]);
86
106
  }).pipe(changes.write, Effect.withSpan(`${spanPrefix}.update`));
87
107
  }
88
- function updateVoid(input: Readonly<Record<string, unknown>>) {
108
+ function updateVoid(input: Readonly<Record<string, unknown>>, settings?: { readonly columns: readonly string[] }) {
89
109
  return Effect.gen(function* () {
90
- const { statement, classify } = yield* statements.update(input);
110
+ const { statement, classify } = yield* statements.update(input, settings?.columns);
91
111
  const rows = yield* sql`${statement} returning ${changes.returning()}`;
92
112
  if (rows.length === 0) {
93
113
  return yield* classify();
@@ -1,8 +1,11 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import type { Model } from "effect/schema";
3
3
  import { SqlClient } from "effect/sql";
4
+ import { storageSchema } from "#adapters/storageSchema.ts";
4
5
  import { makeConflictClause } from "#makeConflictClause.ts";
5
6
  import type { OptimisticConcurrencyError } from "#repository.ts";
7
+ import { selectedUpdate } from "#repositoryUpdate.ts";
8
+ import { sqlNameTransforms } from "#sqlNameTransforms.ts";
6
9
 
7
10
  const Version = Schema.Number.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0));
8
11
  export interface VersionedOptions {
@@ -16,6 +19,7 @@ export interface VersionedOptions {
16
19
 
17
20
  export const versionedStatements = Effect.fnUntraced(function* (model: Model.Any, options: VersionedOptions) {
18
21
  const sql = yield* SqlClient.SqlClient;
22
+ const nameTransforms = yield* sqlNameTransforms;
19
23
  const { tableName, idColumns, versionColumn, conflict } = options;
20
24
  const entries: [string, Schema.Constraint][] = [];
21
25
  for (const column of idColumns) {
@@ -31,8 +35,8 @@ export const versionedStatements = Effect.fnUntraced(function* (model: Model.Any
31
35
  return yield* Effect.die(new Error(`Unknown version column: ${versionColumn}`));
32
36
  }
33
37
  const encodeVersion = Schema.encodeEffect(versionSchema);
34
- const encodeInsert = Schema.encodeEffect(model.insert);
35
- const encodeUpdate = Schema.encodeEffect(model.update);
38
+ const encodeInsert = Schema.encodeEffect(storageSchema(model.insert, model.fields, nameTransforms));
39
+ const encodeUpdate = Schema.encodeEffect(storageSchema(model.update, model.fields, nameTransforms));
36
40
  const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
37
41
  const validateVersion = Schema.decodeUnknownEffect(Version);
38
42
  const clause =
@@ -59,9 +63,15 @@ export const versionedStatements = Effect.fnUntraced(function* (model: Model.Any
59
63
  return sql`insert into ${sql(tableName)} ${sql.insert(encoded)} ${suffix}`;
60
64
  });
61
65
  }
62
- function update(input: Readonly<Record<string, unknown>>) {
66
+ function update(input: Readonly<Record<string, unknown>>, columns?: readonly string[]) {
63
67
  return Effect.gen(function* () {
64
- const encoded = yield* record(yield* encodeUpdate(input));
68
+ const encoded = yield* record(
69
+ yield* columns === undefined
70
+ ? encodeUpdate(input)
71
+ : Effect.flatMap(selectedUpdate(model, columns, [...idColumns, versionColumn], input), (schema) =>
72
+ Schema.encodeEffect(storageSchema(schema, model.fields, nameTransforms))(input),
73
+ ),
74
+ );
65
75
  const expectedVersion = yield* validateVersion(encoded[versionColumn]);
66
76
  const assignments = Object.entries(encoded).filter(([column]) => !idColumns.includes(column) && column !== versionColumn);
67
77
  const set = sql.csv([