@shivaedev/sql 0.34.0 → 0.35.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/CHANGELOG.md +8 -0
- package/README.md +2 -2
- package/dist/aggregateColumn.d.ts +3 -3
- package/dist/aggregateColumn.d.ts.map +1 -1
- package/dist/aggregateColumn.js +4 -2
- package/dist/aggregateColumn.js.map +1 -1
- package/dist/batchWrites.d.ts +4 -4
- package/dist/batchWrites.d.ts.map +1 -1
- package/dist/batchWrites.js +8 -5
- package/dist/batchWrites.js.map +1 -1
- package/dist/batchedDelete.js +1 -1
- package/dist/batchedDelete.js.map +1 -1
- package/dist/compositeRepository.d.ts +9 -5
- package/dist/compositeRepository.d.ts.map +1 -1
- package/dist/compositeRepository.js +12 -9
- package/dist/compositeRepository.js.map +1 -1
- package/dist/conditionalUpsert.d.ts +4 -4
- package/dist/conditionalUpsert.d.ts.map +1 -1
- package/dist/conditionalUpsert.js +7 -4
- package/dist/conditionalUpsert.js.map +1 -1
- package/dist/conflictWrites.d.ts +4 -4
- package/dist/conflictWrites.d.ts.map +1 -1
- package/dist/conflictWrites.js +11 -8
- package/dist/conflictWrites.js.map +1 -1
- package/dist/insertFromSelect.d.ts +3 -3
- package/dist/insertFromSelect.d.ts.map +1 -1
- package/dist/insertFromSelect.js +5 -3
- package/dist/insertFromSelect.js.map +1 -1
- package/dist/latestPerGroup.d.ts +2 -2
- package/dist/latestPerGroup.d.ts.map +1 -1
- package/dist/latestPerGroup.js +5 -4
- package/dist/latestPerGroup.js.map +1 -1
- package/dist/makeAggregate.d.ts +4 -3
- package/dist/makeAggregate.d.ts.map +1 -1
- package/dist/makeAggregate.js +35 -31
- package/dist/makeAggregate.js.map +1 -1
- package/dist/makeConflictClause.d.ts +3 -3
- package/dist/makeConflictClause.d.ts.map +1 -1
- package/dist/makeConflictClause.js +6 -4
- package/dist/makeConflictClause.js.map +1 -1
- package/dist/makeQueryClauses.d.ts +3 -3
- package/dist/makeQueryClauses.d.ts.map +1 -1
- package/dist/makeQueryClauses.js +5 -3
- package/dist/makeQueryClauses.js.map +1 -1
- package/dist/pinnedGroups.d.ts +3 -4
- package/dist/pinnedGroups.d.ts.map +1 -1
- package/dist/pinnedGroups.js +8 -10
- package/dist/pinnedGroups.js.map +1 -1
- package/dist/repository.d.ts +1 -1
- package/dist/repository.d.ts.map +1 -1
- package/dist/repository.js +7 -9
- package/dist/repository.js.map +1 -1
- package/dist/repositoryChanges.d.ts +2 -2
- package/dist/repositoryChanges.d.ts.map +1 -1
- package/dist/repositoryChanges.js +2 -1
- package/dist/repositoryChanges.js.map +1 -1
- package/dist/repositoryFilter.d.ts +4 -4
- package/dist/repositoryFilter.d.ts.map +1 -1
- package/dist/repositoryFilter.js +5 -3
- package/dist/repositoryFilter.js.map +1 -1
- package/dist/repositoryQueries.d.ts +3 -3
- package/dist/repositoryQueries.d.ts.map +1 -1
- package/dist/repositoryQueries.js +45 -41
- package/dist/repositoryQueries.js.map +1 -1
- package/dist/repositoryWrites.d.ts +9 -5
- package/dist/repositoryWrites.d.ts.map +1 -1
- package/dist/repositoryWrites.js +8 -7
- package/dist/repositoryWrites.js.map +1 -1
- package/dist/tupleIn.d.ts +1 -2
- package/dist/tupleIn.d.ts.map +1 -1
- package/dist/tupleIn.js +16 -17
- package/dist/tupleIn.js.map +1 -1
- package/dist/upsertResult.d.ts +2 -2
- package/dist/upsertResult.d.ts.map +1 -1
- package/dist/upsertResult.js +4 -2
- package/dist/upsertResult.js.map +1 -1
- package/dist/versionedRepository.d.ts +5 -5
- package/dist/versionedRepository.d.ts.map +1 -1
- package/dist/versionedRepository.js +5 -3
- package/dist/versionedRepository.js.map +1 -1
- package/dist/versionedStatements.d.ts +3 -3
- package/dist/versionedStatements.d.ts.map +1 -1
- package/dist/versionedStatements.js +12 -9
- package/dist/versionedStatements.js.map +1 -1
- package/package.json +7 -6
- package/src/aggregateColumn.ts +4 -3
- package/src/batchWrites.ts +10 -10
- package/src/batchedDelete.ts +1 -1
- package/src/compositeRepository.ts +15 -15
- package/src/conditionalUpsert.ts +7 -6
- package/src/conflictWrites.ts +14 -10
- package/src/insertFromSelect.ts +6 -6
- package/src/latestPerGroup.ts +5 -4
- package/src/makeAggregate.ts +48 -47
- package/src/makeConflictClause.ts +6 -6
- package/src/makeQueryClauses.ts +6 -6
- package/src/pinnedGroups.ts +10 -14
- package/src/repository.ts +8 -9
- package/src/repositoryChanges.ts +2 -6
- package/src/repositoryFilter.ts +5 -4
- package/src/repositoryQueries.ts +57 -55
- package/src/repositoryWrites.ts +9 -9
- package/src/tupleIn.ts +16 -18
- package/src/upsertResult.ts +4 -3
- package/src/versionedRepository.ts +6 -6
- package/src/versionedStatements.ts +15 -15
- package/dist/testing.d.ts +0 -27
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -75
- package/dist/testing.js.map +0 -1
- package/src/testing.ts +0 -103
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versionedRepository.js","sourceRoot":"","sources":["../src/versionedRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"versionedRepository.js","sourceRoot":"","sources":["../src/versionedRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAiB,MAAM,YAAY,CAAC;AAMtD,OAAO,EAAyB,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAqCrF,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAC7D,KAAgB,EAChB,OAAgG;IAEhG,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACxC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACpD,SAAS,MAAM,CAAC,KAAc;QAC7B,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA,GAAG,SAAS,cAAc,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;YACvE,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC5B,OAAO,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,UAAU,SAAS,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,SAAS,UAAU,CAAC,KAAc;QACjC,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA,GAAG,SAAS,cAAc,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;YACvE,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,UAAU,aAAa,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,SAAS,cAAc,CAAC,KAAc;QACrC,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACxD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA,GAAG,SAAS,cAAc,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;YACvE,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC5B,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnF,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,UAAU,iBAAiB,CAAC,CAAC,CAAC;IACzE,CAAC;IACD,SAAS,MAAM,CAAC,KAAwC;QACvD,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1B,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA,GAAG,SAAS,cAAc,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;YACvE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC1B,CAAC;YACD,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC5B,OAAO,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,UAAU,SAAS,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,SAAS,UAAU,CAAC,KAAwC;QAC3D,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1B,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA,GAAG,SAAS,cAAc,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;YACvE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC1B,CAAC;YACD,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,UAAU,aAAa,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,SAAS,SAAS,CAAC,EAAW,EAAE,QAAgB;QAC/C,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1B,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YACvE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA,GAAG,SAAS,cAAc,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;YACvE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC1B,CAAC;YACD,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,UAAU,SAAS,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,CAAC;AAC5I,CAAC,CAAC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import type { Model } from "effect/schema";
|
|
3
|
-
import
|
|
3
|
+
import { SqlClient } from "effect/sql";
|
|
4
4
|
import type { OptimisticConcurrencyError } from "#repository.ts";
|
|
5
5
|
export interface VersionedOptions {
|
|
6
6
|
readonly conflict: (id: unknown, expectedVersion: number, reason: "missing" | "stale") => OptimisticConcurrencyError;
|
|
@@ -10,7 +10,7 @@ export interface VersionedOptions {
|
|
|
10
10
|
readonly tableName: string;
|
|
11
11
|
readonly versionColumn: string;
|
|
12
12
|
}
|
|
13
|
-
export declare
|
|
13
|
+
export declare const versionedStatements: (model: Model.Any, options: VersionedOptions) => Effect.Effect<{
|
|
14
14
|
insert: (input: unknown, ignore?: boolean) => Effect.Effect<import("effect/sql/Statement").Statement<import("effect/sql/SqlConnection").Row>, Schema.SchemaError | import("effect/sql/SqlError").SqlError, unknown>;
|
|
15
15
|
remove: (id: unknown, expected: number) => Effect.Effect<{
|
|
16
16
|
classify: () => Effect.Effect<never, OptimisticConcurrencyError | import("effect/sql/SqlError").SqlError, never>;
|
|
@@ -20,5 +20,5 @@ export declare function versionedStatements(model: Model.Any, sql: SqlClient.Sql
|
|
|
20
20
|
classify: () => Effect.Effect<never, OptimisticConcurrencyError | import("effect/sql/SqlError").SqlError, never>;
|
|
21
21
|
statement: import("effect/sql/Statement").Statement<import("effect/sql/SqlConnection").Row>;
|
|
22
22
|
}, Schema.SchemaError, unknown>;
|
|
23
|
-
}
|
|
23
|
+
}, never, SqlClient.SqlClient>;
|
|
24
24
|
//# sourceMappingURL=versionedStatements.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versionedStatements.d.ts","sourceRoot":"","sources":["../src/versionedStatements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,
|
|
1
|
+
{"version":3,"file":"versionedStatements.d.ts","sourceRoot":"","sources":["../src/versionedStatements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAGjE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,KAAK,0BAA0B,CAAC;IACrH,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED,eAAO,MAAM,mBAAmB;oBAsCR,OAAO;iBAsBV,OAAO,YAAY,MAAM;;;;oBAftB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;;;;8BA2BvD,CAAC"}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
2
3
|
import { makeConflictClause } from "#makeConflictClause.ts";
|
|
3
4
|
const Version = Schema.Number.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0));
|
|
4
|
-
export
|
|
5
|
+
export const versionedStatements = Effect.fnUntraced(function* (model, options) {
|
|
6
|
+
const sql = yield* SqlClient.SqlClient;
|
|
5
7
|
const { tableName, idColumns, versionColumn, conflict } = options;
|
|
6
|
-
const
|
|
8
|
+
const entries = [];
|
|
9
|
+
for (const column of idColumns) {
|
|
7
10
|
const field = model.fields[column];
|
|
8
11
|
if (field === undefined) {
|
|
9
|
-
|
|
12
|
+
return yield* Effect.die(new Error(`Unknown ID column: ${column}`));
|
|
10
13
|
}
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
const encodeKey = Schema.encodeEffect(Schema.Struct(
|
|
14
|
+
entries.push([column, field]);
|
|
15
|
+
}
|
|
16
|
+
const encodeKey = Schema.encodeEffect(Schema.Struct(Object.fromEntries(entries)));
|
|
14
17
|
const versionSchema = model.fields[versionColumn];
|
|
15
18
|
if (versionSchema === undefined) {
|
|
16
|
-
|
|
19
|
+
return yield* Effect.die(new Error(`Unknown version column: ${versionColumn}`));
|
|
17
20
|
}
|
|
18
21
|
const encodeVersion = Schema.encodeEffect(versionSchema);
|
|
19
22
|
const encodeInsert = Schema.encodeEffect(model.insert);
|
|
20
23
|
const encodeUpdate = Schema.encodeEffect(model.update);
|
|
21
24
|
const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
|
|
22
25
|
const validateVersion = Schema.decodeUnknownEffect(Version);
|
|
23
|
-
const clause = options.conflictColumns === undefined ? undefined : makeConflictClause(model,
|
|
26
|
+
const clause = options.conflictColumns === undefined ? undefined : yield* makeConflictClause(model, { ...options, conflictColumns: options.conflictColumns });
|
|
24
27
|
function key(input) {
|
|
25
28
|
return idColumns.length === 1 ? { [idColumns[0] ?? ""]: input } : input;
|
|
26
29
|
}
|
|
@@ -70,5 +73,5 @@ export function versionedStatements(model, sql, options) {
|
|
|
70
73
|
});
|
|
71
74
|
}
|
|
72
75
|
return { insert, remove, update };
|
|
73
|
-
}
|
|
76
|
+
});
|
|
74
77
|
//# sourceMappingURL=versionedStatements.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versionedStatements.js","sourceRoot":"","sources":["../src/versionedStatements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"versionedStatements.js","sourceRoot":"","sources":["../src/versionedStatements.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Schema-derived repositories over native Effect SQL",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect"
|
|
@@ -50,11 +50,12 @@
|
|
|
50
50
|
"@effect/platform-node": "4.0.6",
|
|
51
51
|
"@effect/sql-pg": "4.0.6",
|
|
52
52
|
"@effect/sql-sqlite-node": "4.0.6",
|
|
53
|
-
"@shivaedev/contract": "0.
|
|
54
|
-
"@shivaedev/jobs": "0.
|
|
55
|
-
"@shivaedev/quality": "0.57.
|
|
56
|
-
"@shivaedev/test-
|
|
57
|
-
"@shivaedev/test-
|
|
53
|
+
"@shivaedev/contract": "0.33.0",
|
|
54
|
+
"@shivaedev/jobs": "0.32.0",
|
|
55
|
+
"@shivaedev/quality": "0.57.1",
|
|
56
|
+
"@shivaedev/test-kits": "0.1.0",
|
|
57
|
+
"@shivaedev/test-sql": "0.18.0",
|
|
58
|
+
"@shivaedev/test-story": "0.36.0",
|
|
58
59
|
"@types/node": "24.19.2",
|
|
59
60
|
"effect": "4.0.6",
|
|
60
61
|
"typescript": "7.0.2",
|
package/src/aggregateColumn.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect, Schema, SchemaAST } from "effect";
|
|
2
|
-
import
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
3
|
|
|
4
4
|
const Expression = Schema.Tuple([Schema.Tuple([Schema.Literals(["count", "sum", "avg", "min", "max", "boolOr", "boolAnd"]), Schema.String])]);
|
|
5
5
|
const functions = { avg: "avg", boolAnd: "bool_and", boolOr: "bool_or", count: "count", max: "max", min: "min", sum: "sum" };
|
|
@@ -31,7 +31,8 @@ function resultSchema(operation: Operation, field: Schema.Constraint): Schema.Co
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export
|
|
34
|
+
export const aggregateColumn = Effect.fnUntraced(function* (fieldAt: (key: string) => Effect.Effect<Schema.Constraint, Schema.SchemaError>) {
|
|
35
|
+
const sql = yield* SqlClient.SqlClient;
|
|
35
36
|
return Effect.fnUntraced(function* (name: string, expression: object) {
|
|
36
37
|
const [[operation, key]] = yield* Schema.decodeUnknownEffect(Expression)(Object.entries(expression));
|
|
37
38
|
const countRows = operation === "count" && key === "*";
|
|
@@ -46,4 +47,4 @@ export function aggregateColumn(sql: SqlClient.SqlClient, fieldAt: (key: string)
|
|
|
46
47
|
schema: operation === "count" ? Result : Schema.NullOr(Result),
|
|
47
48
|
};
|
|
48
49
|
});
|
|
49
|
-
}
|
|
50
|
+
});
|
package/src/batchWrites.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import type { Model } from "effect/schema";
|
|
3
|
-
import
|
|
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 type { RepositoryChanges } from "
|
|
7
|
-
import { makeUpsertDecoder, upsertReturning } from "
|
|
6
|
+
import type { RepositoryChanges } from "#repositoryChanges.ts";
|
|
7
|
+
import { makeUpsertDecoder, upsertReturning } from "#upsertResult.ts";
|
|
8
8
|
|
|
9
9
|
export type InsertMany<TModel extends Model.Any, TServices = never> = (
|
|
10
10
|
inputs: readonly TModel["insert"]["Type"][],
|
|
@@ -14,20 +14,20 @@ export interface BatchWrites<TModel extends Model.Any> {
|
|
|
14
14
|
readonly insertMany: InsertMany<TModel>;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function
|
|
17
|
+
export const makeInsertMany = Effect.fnUntraced(function* <TServices = never>(
|
|
18
18
|
model: Model.Any,
|
|
19
|
-
sql: SqlClient.SqlClient,
|
|
20
19
|
options: { readonly tableName: string; readonly spanPrefix: string; readonly changes: RepositoryChanges },
|
|
21
20
|
operation: string,
|
|
22
|
-
prepare
|
|
23
|
-
Effect.succeed(sql``),
|
|
21
|
+
prepare?: (row: Readonly<Record<string, unknown>>) => Effect.Effect<Fragment, SqlError | Schema.SchemaError, TServices>,
|
|
24
22
|
upsert = false,
|
|
25
23
|
) {
|
|
24
|
+
const sql = yield* SqlClient.SqlClient;
|
|
25
|
+
const prepareRow = prepare ?? (() => Effect.succeed(sql``));
|
|
26
26
|
const encode = Schema.encodeEffect(model.insert);
|
|
27
27
|
const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
|
|
28
28
|
const decode = Schema.decodeUnknownEffect(Schema.Array(model));
|
|
29
29
|
const decodeUpsert = makeUpsertDecoder(model);
|
|
30
|
-
const returning = options.changes.returning(upsert ? upsertReturning(
|
|
30
|
+
const returning = options.changes.returning(upsert ? yield* upsertReturning() : sql`*`);
|
|
31
31
|
return (inputs: readonly unknown[]) =>
|
|
32
32
|
Effect.gen(function* () {
|
|
33
33
|
if (inputs.length === 0) {
|
|
@@ -57,7 +57,7 @@ export function makeInsertMany<TServices = never>(
|
|
|
57
57
|
for (const row of rows) {
|
|
58
58
|
const value = sql`(${sql.csv(columns.map((column) => (Object.hasOwn(row, column) ? sql`${row[column]}` : sql`default`)))})`;
|
|
59
59
|
const size = value.compile()[1].length;
|
|
60
|
-
const prepared = yield*
|
|
60
|
+
const prepared = yield* prepareRow(row);
|
|
61
61
|
const suffixSize = sql`${prepared}`.compile()[1].length;
|
|
62
62
|
// Bound native fragment size as well as PostgreSQL's 65,535 bind parameters.
|
|
63
63
|
if (chunk.length > 0 && (chunk.length === chunkSize || parameters + size + suffixSize > 10_000)) {
|
|
@@ -71,4 +71,4 @@ export function makeInsertMany<TServices = never>(
|
|
|
71
71
|
yield* options.changes.record(changedRows);
|
|
72
72
|
return results;
|
|
73
73
|
}).pipe(options.changes.write, Effect.withSpan(`${options.spanPrefix}.${operation}`));
|
|
74
|
-
}
|
|
74
|
+
});
|
package/src/batchedDelete.ts
CHANGED
|
@@ -69,7 +69,7 @@ export const makeBatchedDelete = Effect.fnUntraced(function* (
|
|
|
69
69
|
changes: RepositoryChanges,
|
|
70
70
|
) {
|
|
71
71
|
const sql = yield* SqlClient.SqlClient;
|
|
72
|
-
const { where } = makeFilter(model
|
|
72
|
+
const { where } = yield* makeFilter(model);
|
|
73
73
|
function deleteBatch(predicate: Fragment, limit: number) {
|
|
74
74
|
const count = SqlSchema.findOne({
|
|
75
75
|
execute: (batchLimit) => sql`with removed as (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Cause } from "effect";
|
|
2
2
|
import { Effect, Schema } from "effect";
|
|
3
3
|
import type { Model } from "effect/schema";
|
|
4
|
-
import {
|
|
4
|
+
import { SqlClient, SqlSchema } from "effect/sql";
|
|
5
5
|
import type { SqlError } from "effect/sql/SqlError";
|
|
6
6
|
import type { BatchedDelete } from "#batchedDelete.ts";
|
|
7
7
|
import type { RepositoryChanges } from "#repositoryChanges.ts";
|
|
@@ -41,9 +41,8 @@ export type Repository<TModel extends Model.Any, TId extends PrimaryKey<TModel>>
|
|
|
41
41
|
}
|
|
42
42
|
: never);
|
|
43
43
|
|
|
44
|
-
export
|
|
44
|
+
export const makeCompositeCrud = Effect.fnUntraced(function* (
|
|
45
45
|
model: Model.Any,
|
|
46
|
-
sql: SqlClient.SqlClient,
|
|
47
46
|
options: {
|
|
48
47
|
readonly columns: readonly string[];
|
|
49
48
|
readonly tableName: string;
|
|
@@ -51,19 +50,20 @@ export function makeCompositeCrud(
|
|
|
51
50
|
},
|
|
52
51
|
changes: RepositoryChanges,
|
|
53
52
|
) {
|
|
53
|
+
const sql = yield* SqlClient.SqlClient;
|
|
54
54
|
const { columns, tableName, spanPrefix } = options;
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
);
|
|
55
|
+
const entries: [string, Schema.Constraint][] = [];
|
|
56
|
+
for (const column of columns) {
|
|
57
|
+
const field = model.fields[column];
|
|
58
|
+
if (field === undefined) {
|
|
59
|
+
return yield* Effect.die(new Error(`Unknown ID column: ${column}`));
|
|
60
|
+
}
|
|
61
|
+
entries.push([column, field]);
|
|
62
|
+
}
|
|
63
|
+
const fields = Object.fromEntries(entries);
|
|
64
64
|
const Key = columns.length === 1 ? model.fields[columns[0] ?? ""] : Schema.Struct(fields);
|
|
65
65
|
if (Key === undefined) {
|
|
66
|
-
|
|
66
|
+
return yield* Effect.die(new Error("Unknown repository ID column"));
|
|
67
67
|
}
|
|
68
68
|
const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
|
|
69
69
|
function predicate(input: Readonly<Record<string, unknown>>) {
|
|
@@ -80,7 +80,7 @@ export function makeCompositeCrud(
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
return {
|
|
83
|
-
...repositoryWrites(model,
|
|
83
|
+
...(yield* repositoryWrites(model, { ...options, idColumns: columns }, changes)),
|
|
84
84
|
findById: (id: unknown) => find(id).pipe(Effect.withSpan(`${spanPrefix}.findById`)),
|
|
85
85
|
};
|
|
86
|
-
}
|
|
86
|
+
});
|
package/src/conditionalUpsert.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import type { Model } from "effect/schema";
|
|
3
|
-
import
|
|
3
|
+
import { SqlClient } from "effect/sql";
|
|
4
4
|
import type { SqlError } from "effect/sql/SqlError";
|
|
5
5
|
import { makeConflictClause } from "#makeConflictClause.ts";
|
|
6
6
|
import type { RepositoryChanges } from "#repositoryChanges.ts";
|
|
@@ -53,9 +53,8 @@ export interface ConditionalUpsertMany<TModel extends Model.Any, TOptions, TServ
|
|
|
53
53
|
): Effect.Effect<UpsertRow<TModel["Type"]>[], SqlError | Schema.SchemaError, TServices>;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
export
|
|
56
|
+
export const makeConditionalUpsert = Effect.fnUntraced(function* (
|
|
57
57
|
model: Model.Any,
|
|
58
|
-
sql: SqlClient.SqlClient,
|
|
59
58
|
options: {
|
|
60
59
|
readonly tableName: string;
|
|
61
60
|
readonly spanPrefix: string;
|
|
@@ -65,10 +64,12 @@ export function makeConditionalUpsert(
|
|
|
65
64
|
readonly idColumns: readonly string[];
|
|
66
65
|
},
|
|
67
66
|
) {
|
|
67
|
+
const sql = yield* SqlClient.SqlClient;
|
|
68
|
+
const returning = yield* upsertReturning();
|
|
68
69
|
const encode = Schema.encodeEffect(model.insert);
|
|
69
70
|
const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
|
|
70
71
|
const decode = makeUpsertDecoder(model);
|
|
71
|
-
const clause = makeConflictClause(model,
|
|
72
|
+
const clause = yield* makeConflictClause(model, options);
|
|
72
73
|
return (
|
|
73
74
|
inputs: readonly unknown[],
|
|
74
75
|
settings: { readonly updateColumns: readonly UpsertAssignment[]; readonly updateWhere: Readonly<Record<string, unknown>> },
|
|
@@ -78,7 +79,7 @@ export function makeConditionalUpsert(
|
|
|
78
79
|
Effect.gen(function* () {
|
|
79
80
|
const row = yield* record(yield* encode(input));
|
|
80
81
|
const suffix = yield* clause(row, settings.updateColumns, settings.updateWhere);
|
|
81
|
-
return sql`insert into ${sql(options.tableName)} ${sql.insert(row)} ${suffix} returning ${options.changes.returning(
|
|
82
|
+
return sql`insert into ${sql(options.tableName)} ${sql.insert(row)} ${suffix} returning ${options.changes.returning(returning)}`;
|
|
82
83
|
}),
|
|
83
84
|
);
|
|
84
85
|
const changedRows: Readonly<Record<string, unknown>>[] = [];
|
|
@@ -99,4 +100,4 @@ export function makeConditionalUpsert(
|
|
|
99
100
|
yield* options.changes.record(changedRows);
|
|
100
101
|
return results;
|
|
101
102
|
}).pipe(options.changes.write, Effect.withSpan(`${options.spanPrefix}.conditionalUpsert`));
|
|
102
|
-
}
|
|
103
|
+
});
|
package/src/conflictWrites.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Option, Schema } from "effect";
|
|
2
2
|
import type { Model } from "effect/schema";
|
|
3
|
-
import
|
|
3
|
+
import { SqlClient } from "effect/sql";
|
|
4
4
|
import type { SqlError } from "effect/sql/SqlError";
|
|
5
5
|
import { type InsertMany, makeInsertMany } from "#batchWrites.ts";
|
|
6
6
|
import { type ConditionalUpsert, type ConditionalUpsertMany, makeConditionalUpsert } from "#conditionalUpsert.ts";
|
|
@@ -40,9 +40,8 @@ export interface ConflictWrites<TModel extends Model.Any, TExcluded extends Prop
|
|
|
40
40
|
>;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export
|
|
43
|
+
export const makeConflictWrites = Effect.fnUntraced(function* (
|
|
44
44
|
model: Model.Any,
|
|
45
|
-
sql: SqlClient.SqlClient,
|
|
46
45
|
options: {
|
|
47
46
|
readonly changes: RepositoryChanges;
|
|
48
47
|
readonly tableName: string;
|
|
@@ -52,25 +51,27 @@ export function makeConflictWrites(
|
|
|
52
51
|
readonly idColumns: readonly string[];
|
|
53
52
|
},
|
|
54
53
|
) {
|
|
54
|
+
const sql = yield* SqlClient.SqlClient;
|
|
55
55
|
const { tableName, spanPrefix } = options;
|
|
56
|
+
const returning = yield* upsertReturning();
|
|
56
57
|
const encode = Schema.encodeEffect(model.insert);
|
|
57
58
|
const decode = Schema.decodeUnknownEffect(model);
|
|
58
59
|
const decodeUpsert = makeUpsertDecoder(model);
|
|
59
60
|
const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
|
|
60
|
-
const conditional = makeConditionalUpsert(model,
|
|
61
|
-
const clause = makeConflictClause(model,
|
|
61
|
+
const conditional = yield* makeConditionalUpsert(model, options);
|
|
62
|
+
const clause = yield* makeConflictClause(model, options);
|
|
62
63
|
function statement(input: unknown, updateColumns?: readonly UpsertAssignment[]) {
|
|
63
64
|
return Effect.gen(function* () {
|
|
64
65
|
const encoded = yield* record(yield* encode(input));
|
|
65
66
|
const suffix = yield* clause(encoded, updateColumns);
|
|
66
|
-
return sql`insert into ${sql(tableName)} ${sql.insert(encoded)} ${suffix} returning ${options.changes.returning(updateColumns === undefined ? sql`*` :
|
|
67
|
+
return sql`insert into ${sql(tableName)} ${sql.insert(encoded)} ${suffix} returning ${options.changes.returning(updateColumns === undefined ? sql`*` : returning)}`;
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
70
|
return {
|
|
70
|
-
insertFromSelectOrIgnore: makeInsertFromSelect(model,
|
|
71
|
+
insertFromSelectOrIgnore: yield* makeInsertFromSelect(model, options, "insertFromSelectOrIgnore", (columns) =>
|
|
71
72
|
clause(Object.fromEntries(columns.map((key) => [key, undefined]))),
|
|
72
73
|
),
|
|
73
|
-
insertManyOrIgnore: makeInsertMany(model,
|
|
74
|
+
insertManyOrIgnore: yield* makeInsertMany(model, options, "insertManyOrIgnore", (row) => clause(row)),
|
|
74
75
|
insertOrIgnore: (input: unknown) =>
|
|
75
76
|
Effect.gen(function* () {
|
|
76
77
|
const rows = yield* yield* statement(input);
|
|
@@ -93,7 +94,10 @@ export function makeConflictWrites(
|
|
|
93
94
|
settings: { readonly updateColumns: readonly UpsertAssignment[]; readonly updateWhere?: Readonly<Record<string, unknown>> },
|
|
94
95
|
) =>
|
|
95
96
|
settings.updateWhere === undefined
|
|
96
|
-
?
|
|
97
|
+
? Effect.flatMap(
|
|
98
|
+
makeInsertMany(model, options, "upsertMany", (row) => clause(row, settings.updateColumns), true),
|
|
99
|
+
(insertMany) => insertMany(inputs),
|
|
100
|
+
).pipe(Effect.provideService(SqlClient.SqlClient, sql))
|
|
97
101
|
: conditional(inputs, { ...settings, updateWhere: settings.updateWhere }),
|
|
98
102
|
};
|
|
99
|
-
}
|
|
103
|
+
});
|
package/src/insertFromSelect.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import type { Model } from "effect/schema";
|
|
3
|
-
import
|
|
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
6
|
import type { RepositoryChanges } from "#repositoryChanges.ts";
|
|
@@ -24,24 +24,24 @@ export interface SelectWrites<TModel extends Model.Any> {
|
|
|
24
24
|
readonly insertFromSelect: InsertFromSelect<TModel>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function
|
|
27
|
+
export const makeInsertFromSelect = Effect.fnUntraced(function* <TServices = never>(
|
|
28
28
|
model: Model.Any,
|
|
29
|
-
sql: SqlClient.SqlClient,
|
|
30
29
|
options: { readonly tableName: string; readonly spanPrefix: string; readonly changes: RepositoryChanges },
|
|
31
30
|
operation: string,
|
|
32
|
-
prepare
|
|
31
|
+
prepare?: (columns: readonly string[]) => Effect.Effect<Fragment, SqlError | Schema.SchemaError, TServices>,
|
|
33
32
|
) {
|
|
33
|
+
const sql = yield* SqlClient.SqlClient;
|
|
34
34
|
const decode = Schema.decodeUnknownEffect(Schema.Array(model));
|
|
35
35
|
return (source: RepositorySelect<Schema.Struct.Fields, unknown>, columns: Readonly<Record<string, string>>) =>
|
|
36
36
|
Effect.gen(function* () {
|
|
37
37
|
const names = Object.keys(columns);
|
|
38
38
|
const selected = sql.csv(Object.values(columns).map((column) => sql`${sql(column)}`));
|
|
39
39
|
const target = sql.csv(names.map((column) => sql`${sql(column)}`));
|
|
40
|
-
const suffix = yield* prepare(names);
|
|
40
|
+
const suffix = yield* prepare?.(names) ?? Effect.succeed(sql``);
|
|
41
41
|
const statement = yield* source.statement;
|
|
42
42
|
const rows =
|
|
43
43
|
yield* sql`insert into ${sql(options.tableName)} (${target}) select ${selected} from (${statement}) as ${sql("source")} where true ${suffix} returning ${options.changes.returning()}`;
|
|
44
44
|
yield* options.changes.record(rows);
|
|
45
45
|
return yield* decode(rows);
|
|
46
46
|
}).pipe(options.changes.write, Effect.withSpan(`${options.spanPrefix}.${operation}`));
|
|
47
|
-
}
|
|
47
|
+
});
|
package/src/latestPerGroup.ts
CHANGED
|
@@ -18,8 +18,9 @@ function groupMasks(input: readonly (readonly unknown[])[]) {
|
|
|
18
18
|
return [...masks.values()];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export
|
|
22
|
-
const
|
|
21
|
+
export const latestPerGroup = Effect.fnUntraced(function* (model: Model.Any, tableName: string) {
|
|
22
|
+
const sql = yield* SqlClient.SqlClient;
|
|
23
|
+
const { fieldAt, where } = yield* makeFilter(model);
|
|
23
24
|
function groupSource(groups: readonly (readonly unknown[])[], partitionBy: readonly string[], fromTuple: boolean) {
|
|
24
25
|
const partitions = sql.csv(partitionBy.map((key) => sql`${sql(key)}`));
|
|
25
26
|
if (fromTuple) {
|
|
@@ -37,7 +38,7 @@ export function latestPerGroup(model: Model.Any, sql: SqlClient.SqlClient, table
|
|
|
37
38
|
const partitions = sql.csv(query.partitionBy.map((key) => sql`${sql(key)}`));
|
|
38
39
|
const keys = Object.keys(model.fields);
|
|
39
40
|
const columns = sql.csv(keys.map((key) => sql`${sql(key)}`));
|
|
40
|
-
const pinned = yield* pinnedGroups(model,
|
|
41
|
+
const pinned = yield* pinnedGroups(model, query.partitionBy, query.where).pipe(Effect.provideService(SqlClient.SqlClient, sql));
|
|
41
42
|
if (pinned !== undefined && pinned.groups.length === 0) {
|
|
42
43
|
return [];
|
|
43
44
|
}
|
|
@@ -82,4 +83,4 @@ export function latestPerGroup(model: Model.Any, sql: SqlClient.SqlClient, table
|
|
|
82
83
|
order by ${partitions}, ${sql(rankColumn)}`;
|
|
83
84
|
return yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct(model.fields)))(rows);
|
|
84
85
|
});
|
|
85
|
-
}
|
|
86
|
+
});
|
package/src/makeAggregate.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema, SchemaIssue } from "effect";
|
|
2
2
|
import type { Model } from "effect/schema";
|
|
3
|
-
import
|
|
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
6
|
import { aggregateColumn } from "#aggregateColumn.ts";
|
|
@@ -10,55 +10,56 @@ import { type FindManyFilter, makeFilter } from "#repositoryFilter.ts";
|
|
|
10
10
|
|
|
11
11
|
export function makeAggregate<TModel extends Model.Any>(
|
|
12
12
|
model: TModel,
|
|
13
|
-
sql: SqlClient.SqlClient,
|
|
14
13
|
options: { readonly tableName: string; readonly spanPrefix: string },
|
|
15
|
-
): Aggregate<TModel>;
|
|
14
|
+
): Effect.Effect<Aggregate<TModel>, never, SqlClient.SqlClient>;
|
|
16
15
|
export function makeAggregate(
|
|
17
16
|
model: Model.Any,
|
|
18
|
-
sql: SqlClient.SqlClient,
|
|
19
17
|
options: { readonly tableName: string; readonly spanPrefix: string },
|
|
20
|
-
): unknown {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (groups.includes(name)) {
|
|
41
|
-
return yield* Effect.fail(new Schema.SchemaError(new SchemaIssue.Pointer([name], new SchemaIssue.UnexpectedKey(model.ast, name))));
|
|
18
|
+
): Effect.Effect<unknown, never, SqlClient.SqlClient> {
|
|
19
|
+
return Effect.fnUntraced(function* () {
|
|
20
|
+
const sql = yield* SqlClient.SqlClient;
|
|
21
|
+
const { fieldAt, where } = yield* makeFilter(model);
|
|
22
|
+
const selectAggregate = yield* aggregateColumn(fieldAt);
|
|
23
|
+
return (query: {
|
|
24
|
+
readonly groupBy: readonly string[];
|
|
25
|
+
readonly aggregates: Readonly<Record<string, object>>;
|
|
26
|
+
readonly where?: FindManyFilter<Model.Any>;
|
|
27
|
+
readonly orderBy?: readonly { readonly field: string; readonly direction: "asc" | "desc" }[];
|
|
28
|
+
readonly limit?: number;
|
|
29
|
+
}): Effect.Effect<unknown, SqlError | Schema.SchemaError, unknown> =>
|
|
30
|
+
Effect.gen(function* () {
|
|
31
|
+
const groups = yield* Schema.decodeUnknownEffect(Schema.NonEmptyArray(Schema.String))(query.groupBy);
|
|
32
|
+
const fields = new Map<string, Schema.Constraint>();
|
|
33
|
+
const columns: Fragment[] = [];
|
|
34
|
+
const ordering = new Map<string, Fragment>();
|
|
35
|
+
for (const key of groups) {
|
|
36
|
+
fields.set(key, yield* fieldAt(key));
|
|
37
|
+
columns.push(sql`${sql(key)}`);
|
|
42
38
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
39
|
+
for (const [name, expression] of Object.entries(query.aggregates)) {
|
|
40
|
+
if (groups.includes(name)) {
|
|
41
|
+
return yield* Effect.fail(new Schema.SchemaError(new SchemaIssue.Pointer([name], new SchemaIssue.UnexpectedKey(model.ast, name))));
|
|
42
|
+
}
|
|
43
|
+
const selected = yield* selectAggregate(name, expression);
|
|
44
|
+
ordering.set(name, selected.order);
|
|
45
|
+
columns.push(selected.column);
|
|
46
|
+
fields.set(name, selected.schema);
|
|
47
|
+
}
|
|
48
|
+
const Result = Schema.Struct(Object.fromEntries(fields));
|
|
49
|
+
const clauses = yield* makeQueryClauses(
|
|
50
|
+
(key) =>
|
|
51
|
+
fields.has(key)
|
|
52
|
+
? Effect.succeed(fields.get(key))
|
|
53
|
+
: Effect.fail(new Schema.SchemaError(new SchemaIssue.Pointer([key], new SchemaIssue.UnexpectedKey(Result.ast, key)))),
|
|
54
|
+
(key) => ordering.get(key) ?? sql`${sql(key)}`,
|
|
55
|
+
).pipe(Effect.provideService(SqlClient.SqlClient, sql));
|
|
56
|
+
const predicate = yield* where(query.where, true, true);
|
|
57
|
+
const order = yield* clauses.orderClause(query.orderBy);
|
|
58
|
+
const limit = yield* clauses.limitClause(query.limit);
|
|
59
|
+
const partitions = sql.csv(groups.map((key) => sql`${sql(key)}`));
|
|
60
|
+
const rows =
|
|
61
|
+
yield* sql`select ${sql.csv(columns)} from ${sql(options.tableName)} where ${predicate} group by ${partitions} ${order} ${limit}`;
|
|
62
|
+
return yield* Schema.decodeUnknownEffect(Schema.Array(Result))(rows);
|
|
63
|
+
}).pipe(Effect.withSpan(`${options.spanPrefix}.aggregate`));
|
|
64
|
+
})();
|
|
64
65
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Array as Arr, Effect, Schema, SchemaAST, SchemaIssue } from "effect";
|
|
2
2
|
import type { Model } from "effect/schema";
|
|
3
|
-
import
|
|
3
|
+
import { SqlClient } from "effect/sql";
|
|
4
4
|
import { makeFilter } from "#repositoryFilter.ts";
|
|
5
5
|
import type { UpsertAssignment } from "#upsertColumns.ts";
|
|
6
6
|
|
|
7
|
-
export
|
|
7
|
+
export const makeConflictClause = Effect.fnUntraced(function* (
|
|
8
8
|
model: Model.Any,
|
|
9
|
-
sql: SqlClient.SqlClient,
|
|
10
9
|
options: {
|
|
11
10
|
readonly tableName: string;
|
|
12
11
|
readonly conflictColumns: readonly string[];
|
|
@@ -14,9 +13,10 @@ export function makeConflictClause(
|
|
|
14
13
|
readonly idColumns: readonly string[];
|
|
15
14
|
},
|
|
16
15
|
) {
|
|
16
|
+
const sql = yield* SqlClient.SqlClient;
|
|
17
17
|
const { conflictColumns, idColumns } = options;
|
|
18
|
-
const updateFilter = makeFilter(model,
|
|
19
|
-
const filter = makeFilter(model
|
|
18
|
+
const updateFilter = yield* makeFilter(model, options);
|
|
19
|
+
const filter = yield* makeFilter(model);
|
|
20
20
|
const updateFields = SchemaAST.isObjects(model.update.ast) ? model.update.ast.propertySignatures.map((field) => field.name) : [];
|
|
21
21
|
const immutable = Object.keys(model.fields).filter((column) => !updateFields.includes(column));
|
|
22
22
|
function invalid(column: string) {
|
|
@@ -95,4 +95,4 @@ export function makeConflictClause(
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
return clause;
|
|
98
|
-
}
|
|
98
|
+
});
|