@shivaedev/sql 0.29.0 → 0.31.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 (50) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +101 -1
  3. package/dist/applyLowMemoryPostgres.d.ts +7 -0
  4. package/dist/applyLowMemoryPostgres.d.ts.map +1 -0
  5. package/dist/applyLowMemoryPostgres.js +24 -0
  6. package/dist/applyLowMemoryPostgres.js.map +1 -0
  7. package/dist/batchedDelete.d.ts +33 -0
  8. package/dist/batchedDelete.d.ts.map +1 -0
  9. package/dist/batchedDelete.js +64 -0
  10. package/dist/batchedDelete.js.map +1 -0
  11. package/dist/compositeRepository.d.ts +14 -13
  12. package/dist/compositeRepository.d.ts.map +1 -1
  13. package/dist/compositeRepository.js.map +1 -1
  14. package/dist/jsonb.d.ts +3 -0
  15. package/dist/jsonb.d.ts.map +1 -0
  16. package/dist/jsonb.js +9 -0
  17. package/dist/jsonb.js.map +1 -0
  18. package/dist/repository.d.ts.map +1 -1
  19. package/dist/repository.js +19 -17
  20. package/dist/repository.js.map +1 -1
  21. package/dist/repositoryChanges.d.ts +1 -0
  22. package/dist/repositoryChanges.d.ts.map +1 -1
  23. package/dist/repositoryChanges.js +9 -1
  24. package/dist/repositoryChanges.js.map +1 -1
  25. package/dist/repositoryUpdate.d.ts +31 -0
  26. package/dist/repositoryUpdate.d.ts.map +1 -0
  27. package/dist/repositoryUpdate.js +16 -0
  28. package/dist/repositoryUpdate.js.map +1 -0
  29. package/dist/repositoryWrites.d.ts +5 -2
  30. package/dist/repositoryWrites.d.ts.map +1 -1
  31. package/dist/repositoryWrites.js +19 -4
  32. package/dist/repositoryWrites.js.map +1 -1
  33. package/dist/tryPurgeLock.d.ts +4 -0
  34. package/dist/tryPurgeLock.d.ts.map +1 -0
  35. package/dist/tryPurgeLock.js +15 -0
  36. package/dist/tryPurgeLock.js.map +1 -0
  37. package/dist/versionedRepository.d.ts +3 -2
  38. package/dist/versionedRepository.d.ts.map +1 -1
  39. package/dist/versionedRepository.js.map +1 -1
  40. package/package.json +4 -4
  41. package/src/applyLowMemoryPostgres.ts +28 -0
  42. package/src/batchedDelete.ts +93 -0
  43. package/src/compositeRepository.ts +25 -34
  44. package/src/jsonb.ts +12 -0
  45. package/src/repository.ts +19 -17
  46. package/src/repositoryChanges.ts +9 -1
  47. package/src/repositoryUpdate.ts +65 -0
  48. package/src/repositoryWrites.ts +19 -4
  49. package/src/tryPurgeLock.ts +18 -0
  50. package/src/versionedRepository.ts +3 -2
@@ -3,6 +3,7 @@ import type { Model } from "effect/schema";
3
3
  import { type SqlClient, SqlSchema } from "effect/sql";
4
4
  import type { RepositoryChanges } from "./repositoryChanges.ts";
5
5
  import { makeFilter } from "./repositoryFilter.ts";
6
+ import { selectedUpdate } from "./repositoryUpdate.ts";
6
7
 
7
8
  export function repositoryWrites(
8
9
  model: Model.Any,
@@ -36,6 +37,15 @@ export function repositoryWrites(
36
37
  const insertVoid = SqlSchema.void({ execute: insert, Request: model.insert });
37
38
  const updateRow = SqlSchema.findOne({ execute: update, Request: model.update, Result: model });
38
39
  const updateVoid = SqlSchema.void({ execute: update, Request: model.update });
40
+ function selected(input: unknown, columns: readonly string[], returning: boolean) {
41
+ return Effect.gen(function* () {
42
+ const row = yield* record(input);
43
+ const Request = yield* selectedUpdate(model, columns, idColumns, row);
44
+ return yield* returning
45
+ ? SqlSchema.findOne({ execute: update, Request, Result: model })(row)
46
+ : SqlSchema.void({ execute: update, Request })(row);
47
+ });
48
+ }
39
49
  const idColumn = idColumns[0] ?? "";
40
50
  const key = model.fields[idColumn];
41
51
  if (key === undefined) {
@@ -63,10 +73,15 @@ export function repositoryWrites(
63
73
  insert: (input: unknown) =>
64
74
  changes.write(insertRow(input).pipe(Effect.catchTag("NoSuchElementError", Effect.die))).pipe(Effect.withSpan(`${spanPrefix}.insert`)),
65
75
  insertVoid: (input: unknown) => changes.write(insertVoid(input)).pipe(Effect.withSpan(`${spanPrefix}.insertVoid`)),
66
- update: (input: unknown, settings?: { readonly onMissing: "fail" }) =>
76
+ update: (input: unknown, settings?: { readonly onMissing?: "fail"; readonly columns?: readonly string[] }) => {
77
+ const result = settings?.columns === undefined ? updateRow(input) : selected(input, settings.columns, true);
78
+ return changes
79
+ .write(settings?.onMissing === "fail" ? result : result.pipe(Effect.catchTag("NoSuchElementError", Effect.die)))
80
+ .pipe(Effect.withSpan(`${spanPrefix}.update`));
81
+ },
82
+ updateVoid: (input: unknown, settings?: { readonly columns: readonly string[] }) =>
67
83
  changes
68
- .write(settings?.onMissing === "fail" ? updateRow(input) : updateRow(input).pipe(Effect.catchTag("NoSuchElementError", Effect.die)))
69
- .pipe(Effect.withSpan(`${spanPrefix}.update`)),
70
- updateVoid: (input: unknown) => changes.write(updateVoid(input)).pipe(Effect.withSpan(`${spanPrefix}.updateVoid`)),
84
+ .write(settings === undefined ? updateVoid(input) : selected(input, settings.columns, false))
85
+ .pipe(Effect.withSpan(`${spanPrefix}.updateVoid`)),
71
86
  };
72
87
  }
@@ -0,0 +1,18 @@
1
+ import { Effect } from "effect";
2
+ import { SqlClient } from "effect/sql";
3
+ import type { Statement } from "effect/sql/Statement";
4
+
5
+ export const tryPurgeLock = Effect.fnUntraced(function* (tableName: string) {
6
+ const sql = yield* SqlClient.SqlClient;
7
+ const [identifier] = sql`${sql(tableName)}`.compile();
8
+ const key = sql`hashtextextended('@shivaedev/sql/deleteBatched:' || ${identifier}::regclass::oid::text, 0)`;
9
+ const connection = yield* sql.reserve;
10
+ function execute(statement: Statement<Readonly<Record<string, unknown>>>) {
11
+ const [text, parameters] = statement.compile();
12
+ return connection.execute(text, parameters, undefined);
13
+ }
14
+ const [lock] = yield* Effect.acquireRelease(execute(sql`select pg_try_advisory_lock(${key}) as acquired`), (rows) =>
15
+ rows[0]?.acquired === true ? Effect.orDie(execute(sql`select pg_advisory_unlock(${key})`)) : Effect.void,
16
+ );
17
+ return lock?.acquired === true;
18
+ });
@@ -4,6 +4,7 @@ import type { SqlClient, SqlModel } from "effect/sql";
4
4
  import type { SqlError } from "effect/sql/SqlError";
5
5
  import type { PrimaryKey, Repository } from "#compositeRepository.ts";
6
6
  import type { OptimisticConcurrencyError } from "#repository.ts";
7
+ import type { UpdateInput } from "#repositoryUpdate.ts";
7
8
  import { type VersionedOptions, versionedStatements } from "#versionedStatements.ts";
8
9
  import type { RepositoryChanges } from "./repositoryChanges.ts";
9
10
 
@@ -37,9 +38,9 @@ export interface VersionedWrites<TModel extends Model.Any, TId extends PrimaryKe
37
38
  readonly insert: Repository<TModel, TId>["insert"];
38
39
  readonly insertVoid: Repository<TModel, TId>["insertVoid"];
39
40
  readonly update: (
40
- input: TModel["update"]["Type"],
41
+ input: UpdateInput<TModel>,
41
42
  ) => Effect.Effect<TModel["Type"], WriteError, TModel["update"]["EncodingServices"] | TModel["DecodingServices"]>;
42
- readonly updateVoid: (input: TModel["update"]["Type"]) => Effect.Effect<void, WriteError, TModel["update"]["EncodingServices"]>;
43
+ readonly updateVoid: (input: UpdateInput<TModel>) => Effect.Effect<void, WriteError, TModel["update"]["EncodingServices"]>;
43
44
  }
44
45
 
45
46
  export function makeVersionedWrites(