@deterministic-code/datasources 0.1.30 → 0.1.36

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 (31) hide show
  1. package/dist/BatchCommandIBulkWriteStrategy.js +4 -4
  2. package/dist/CRUDRepository.d.ts +2 -0
  3. package/dist/CRUDRepository.js +33 -6
  4. package/dist/Datasource.js +2 -2
  5. package/dist/Schema.d.ts +8 -2
  6. package/dist/Schema.js +1 -0
  7. package/dist/SchemaDefault.d.ts +5 -0
  8. package/dist/SchemaDefault.js +11 -0
  9. package/dist/StatementMappers.js +1 -1
  10. package/dist/index.d.ts +3 -9
  11. package/dist/index.js +1 -2
  12. package/dist/statements/CreateNestedReadStoredProcedureStatement.d.ts +1 -1
  13. package/dist/statements/ICreateNestedReadStoredProcedureStatement.d.ts +1 -1
  14. package/dist/statements/NestedSelectBuilder.d.ts +6 -0
  15. package/dist/statements/NestedSelectBuilder.js +63 -0
  16. package/dist/statements/NestedSelectBuilderOptions.d.ts +5 -0
  17. package/dist/statements/NestedSelectBuilderOptions.js +1 -0
  18. package/dist/statements/NestedSelectRelation.d.ts +9 -0
  19. package/dist/statements/NestedSelectRelation.js +1 -0
  20. package/dist/statements/NestedSelectRelationKind.d.ts +1 -0
  21. package/dist/statements/NestedSelectRelationKind.js +1 -0
  22. package/dist/statements/NestedSelectStatement.d.ts +10 -0
  23. package/dist/statements/NestedSelectStatement.js +1 -0
  24. package/dist/statements/NestedSelectType.d.ts +7 -0
  25. package/dist/statements/NestedSelectType.js +1 -0
  26. package/dist/statements/SelectFieldAliases.d.ts +14 -0
  27. package/dist/statements/SelectFieldAliases.js +62 -0
  28. package/dist/statements/SelectStatementTextRenderer.js +1 -1
  29. package/dist/statements/index.d.ts +6 -0
  30. package/dist/statements/index.js +1 -0
  31. package/package.json +1 -1
@@ -89,7 +89,7 @@ export class BatchCommandIBulkWriteStrategy {
89
89
  };
90
90
  mappedUpdate = (item) => {
91
91
  const record = this.reverse(item);
92
- const fields = this.fields(record, (col) => listed(this.options.updateProperties, col, !this.ids.includes(col)));
92
+ const fields = this.fields(record, (col) => listed(this.options.updateProperties, col, !this.ids.includes(col)), false);
93
93
  if (fields.length === 0)
94
94
  throw new DatasourceError("Update requires at least one non-Id field");
95
95
  const statement = this.statements().update(fields, this.idsWhere(record), (name, value) => this.cell(name, value, true));
@@ -102,7 +102,7 @@ export class BatchCommandIBulkWriteStrategy {
102
102
  };
103
103
  updateCommand = (item, byWhere) => {
104
104
  const record = this.reverse(item);
105
- const fields = this.fields(record, (col) => listed(this.options.updateProperties, col, !this.ids.includes(col)));
105
+ const fields = this.fields(record, (col) => listed(this.options.updateProperties, col, !this.ids.includes(col)), false);
106
106
  if (fields.length === 0)
107
107
  throw new DatasourceError("Update requires at least one non-Id field");
108
108
  const statement = this.statements().update(fields, byWhere ?? this.idsWhere(record), (name, value) => this.cell(name, value, byWhere === undefined));
@@ -135,8 +135,8 @@ export class BatchCommandIBulkWriteStrategy {
135
135
  columns.includes(key) ||
136
136
  (map !== undefined && columns.some((name) => map.map(name) === key)));
137
137
  };
138
- fields = (record, include) => Object.entries(record)
139
- .filter(([key, value]) => include(key) && this.writable(key) && value !== null && value !== undefined)
138
+ fields = (record, include, omitNull = true) => Object.entries(record)
139
+ .filter(([key, value]) => include(key) && this.writable(key) && value !== undefined && (!omitNull || value !== null))
140
140
  .sort(([left], [right]) => left.localeCompare(right))
141
141
  .map(([name, value]) => ({ name, value: this.cell(name, value, true) }));
142
142
  cell = (property, value, fieldsApplied) => fieldsApplied
@@ -15,6 +15,7 @@ export declare class CRUDRepository<T = Cells> extends ReadonlyRepository<T> imp
15
15
  private readonly createProperties?;
16
16
  private readonly updateProperties?;
17
17
  protected createZeros?: Cells;
18
+ private readonly schema;
18
19
  private readonly crudStatements;
19
20
  private readonly tree;
20
21
  private readonly write;
@@ -41,4 +42,5 @@ export declare class CRUDRepository<T = Cells> extends ReadonlyRepository<T> imp
41
42
  private reverseConvert;
42
43
  private idWhere;
43
44
  protected enrichmentInto: (name: string) => boolean;
45
+ private enrichmentSource;
44
46
  }
@@ -11,14 +11,31 @@ import { DefaultWriteEnrichmentStrategy } from "./DefaultWriteEnrichmentStrategy
11
11
  import { UnitOfWorkTransaction } from "./UnitOfWorkTransaction.js";
12
12
  import { RepositoryTree } from "./RepositoryTree.js";
13
13
  import { RepositoryTreeNode } from "./RepositoryTreeNode.js";
14
+ import { Schema } from "./Schema.js";
15
+ import { SchemaDefault } from "./SchemaDefault.js";
14
16
  const assigned = (value) => value !== null && value !== "" && value !== 0 && value !== 0n;
15
17
  const listed = (list, col, fallback) => list === undefined ? fallback : list.includes(col);
18
+ const languageZero = (value) => value === "" || value === false;
19
+ const applyEmptyDefaults = (record, schema, ids, properties, fillMissing, skip) => {
20
+ const filled = { ...record };
21
+ for (const [name, column] of Object.entries(schema.columns)) {
22
+ if (ids.includes(name) || !listed(properties, name, true) || column.hasDefault || skip(name))
23
+ continue;
24
+ const zero = SchemaDefault.of(column);
25
+ if (filled[name] === "")
26
+ filled[name] = zero;
27
+ else if (fillMissing && filled[name] === undefined && languageZero(zero))
28
+ filled[name] = zero;
29
+ }
30
+ return filled;
31
+ };
32
+ const zerosFromSchema = (schema, ids, createProperties, skip) => applyEmptyDefaults({}, schema, ids, createProperties, true, skip);
16
33
  const writable = (key, columns, column) => columns.length === 0 || columns.includes(key) || columns.some((name) => column(name) === key);
17
- const fieldsOf = (record, include, extras, cell, enrichmentInto, columns, column) => Object.entries(record)
34
+ const fieldsOf = (record, include, extras, cell, enrichmentInto, columns, column, omitNull = true) => Object.entries(record)
18
35
  .filter(([key, value]) => include(key) &&
19
36
  (extras.has(key) || (writable(key, columns, column) && !enrichmentInto(key))) &&
20
- value !== null &&
21
- value !== undefined)
37
+ value !== undefined &&
38
+ (!omitNull || value !== null))
22
39
  .sort(([left], [right]) => left.localeCompare(right))
23
40
  .map(([name, value]) => ({ name, value: cell(name, value, true) }));
24
41
  const isCells = (id) => typeof id === "object" && id !== null;
@@ -38,6 +55,7 @@ export class CRUDRepository extends ReadonlyRepository {
38
55
  createProperties;
39
56
  updateProperties;
40
57
  createZeros;
58
+ schema;
41
59
  crudStatements;
42
60
  tree;
43
61
  write;
@@ -51,6 +69,7 @@ export class CRUDRepository extends ReadonlyRepository {
51
69
  this.ids = opened.built.ids;
52
70
  this.createProperties = options.createProperties;
53
71
  this.updateProperties = options.updateProperties;
72
+ this.schema = options.schema ?? new Schema({});
54
73
  this.crudStatements = opened.statements;
55
74
  this.tree = new RepositoryTree(new RepositoryTreeNode(this, ""));
56
75
  this.write = options.write ?? new DefaultWriteEnrichmentStrategy();
@@ -155,8 +174,8 @@ export class CRUDRepository extends ReadonlyRepository {
155
174
  idValue = (item) => scalarOf(item[this.ids[0]]);
156
175
  save = async (item, byWhere) => {
157
176
  const extras = lookupKeys(item);
158
- const record = this.reverseConvert(item);
159
- const fields = fieldsOf(record, (col) => extras.has(col) || listed(this.updateProperties, col, !this.ids.includes(col)), extras, this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column);
177
+ const record = applyEmptyDefaults(this.reverseConvert(item), this.schema, this.ids, this.updateProperties, false, (name) => this.enrichmentSource(name));
178
+ const fields = fieldsOf(record, (col) => extras.has(col) || listed(this.updateProperties, col, !this.ids.includes(col)), extras, this.cell, this.enrichmentInto, this.crudStatements.readColumns, this.column, false);
160
179
  if (fields.length === 0)
161
180
  throw new DatasourceError("Update requires at least one non-Id field");
162
181
  const statement = this.crudStatements.update(fields, byWhere ?? this.idWhere(record), (name, value) => this.cell(name, value, byWhere === undefined));
@@ -182,7 +201,14 @@ export class CRUDRepository extends ReadonlyRepository {
182
201
  throw new DatasourceError("Composite id requires a row keyed by each Id column");
183
202
  return this.idWhere({ [this.ids[0]]: id });
184
203
  };
185
- fillCreateZeros = (record) => this.createZeros === undefined ? record : { ...this.createZeros, ...record };
204
+ fillCreateZeros = (record) => {
205
+ const skip = (name) => this.enrichmentSource(name);
206
+ const zeros = this.createZeros !== undefined && Object.keys(this.createZeros).length > 0
207
+ ? this.createZeros
208
+ : zerosFromSchema(this.schema, this.ids, this.createProperties, skip);
209
+ const filled = Object.keys(zeros).length === 0 ? { ...record } : { ...zeros, ...record };
210
+ return applyEmptyDefaults(filled, this.schema, this.ids, this.createProperties, false, skip);
211
+ };
186
212
  reverseConvert = (item) => {
187
213
  const row = this.objectConverter.reverseConvert(item);
188
214
  const converted = this.rowConverter ? this.rowConverter.reverseConvert(row) : row;
@@ -200,6 +226,7 @@ export class CRUDRepository extends ReadonlyRepository {
200
226
  : parts.reduce((left, right) => new Operator.And(left, right)));
201
227
  };
202
228
  enrichmentInto = (name) => enrichmentsOf(this).some((entry) => entry.enrichment.destinationProperty === name);
229
+ enrichmentSource = (name) => enrichmentsOf(this).some((entry) => entry.enrichment.sourceProperty === name);
203
230
  }
204
231
  const writerOf = (via) => "create" in via && typeof via.create === "function" ? via : undefined;
205
232
  const lastEnrichment = (host) => enrichmentsOf(host).at(-1);
@@ -49,7 +49,7 @@ export class Datasource {
49
49
  const built = createCRUDRepositoryOptions(type, this.driver.rowTypeConverters);
50
50
  const types = this.driver.rowTypeConverters;
51
51
  const schema = options?.schema ?? new Schema({});
52
- const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, schema.columns);
52
+ const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, schema.types());
53
53
  return {
54
54
  ...(options ?? built),
55
55
  entity: options?.entity ?? built.entity,
@@ -63,7 +63,7 @@ export class Datasource {
63
63
  const built = createReadonlyRepositoryMappingOptions(type, this.driver.rowTypeConverters);
64
64
  const types = this.driver.rowTypeConverters;
65
65
  const schema = options?.schema ?? new Schema({});
66
- const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, schema.columns);
66
+ const rows = options?.rowConverter ?? RowTypeConverter.from(this.driver.rowTypeConverters, schema.types());
67
67
  return {
68
68
  ...(options ?? built),
69
69
  entity: options?.entity ?? built.entity,
package/dist/Schema.d.ts CHANGED
@@ -1,4 +1,10 @@
1
+ export type SchemaColumn = {
2
+ type: string;
3
+ nullable: boolean;
4
+ hasDefault: boolean;
5
+ };
1
6
  export declare class Schema {
2
- readonly columns: Readonly<Record<string, string>>;
3
- constructor(columns: Readonly<Record<string, string>>);
7
+ readonly columns: Readonly<Record<string, SchemaColumn>>;
8
+ constructor(columns: Readonly<Record<string, SchemaColumn>>);
9
+ types: () => Record<string, string>;
4
10
  }
package/dist/Schema.js CHANGED
@@ -3,4 +3,5 @@ export class Schema {
3
3
  constructor(columns) {
4
4
  this.columns = columns;
5
5
  }
6
+ types = () => Object.fromEntries(Object.entries(this.columns).map(([name, column]) => [name, column.type]));
6
7
  }
@@ -0,0 +1,5 @@
1
+ import type { Cell } from "./converters/Cell.js";
2
+ import type { SchemaColumn } from "./Schema.js";
3
+ export declare const SchemaDefault: {
4
+ of: (column: SchemaColumn) => Cell;
5
+ };
@@ -0,0 +1,11 @@
1
+ const required = {
2
+ string: "",
3
+ boolean: false,
4
+ };
5
+ export const SchemaDefault = {
6
+ of: (column) => column.nullable
7
+ ? null
8
+ : column.type.endsWith("integer")
9
+ ? 0
10
+ : (required[column.type] ?? null),
11
+ };
@@ -1,5 +1,5 @@
1
1
  import { DatasourceError } from "./DatasourceError.js";
2
- import { applySelectFieldAlias, asIndexOf, functionOf, segments, selectFieldAliases } from "./SelectFieldAliases.js";
2
+ import { applySelectFieldAlias, asIndexOf, functionOf, segments, selectFieldAliases } from "./statements/SelectFieldAliases.js";
3
3
  import { DeleteStatement } from "./statements/DeleteStatement.js";
4
4
  import { InsertStatement } from "./statements/InsertStatement.js";
5
5
  import { SelectInsertedIdStatement } from "./statements/SelectInsertedIdStatement.js";
package/dist/index.d.ts CHANGED
@@ -54,12 +54,6 @@ export { ReadonlyRepositoryBuilder } from "./ReadonlyRepositoryBuilder.js";
54
54
  export { CRUDRepositoryBuilder } from "./CRUDRepositoryBuilder.js";
55
55
  export { RepositoryBuilderOptionsFactory } from "./RepositoryBuilderOptionsFactory.js";
56
56
  export { StatementMappers } from "./StatementMappers.js";
57
- export { NestedSelectBuilder } from "./NestedSelectBuilder.js";
58
- export type { NestedSelectBuilderOptions } from "./NestedSelectBuilderOptions.js";
59
- export type { NestedSelectRelation } from "./NestedSelectRelation.js";
60
- export type { NestedSelectRelationKind } from "./NestedSelectRelationKind.js";
61
- export type { NestedSelectStatement } from "./NestedSelectStatement.js";
62
- export type { NestedSelectType } from "./NestedSelectType.js";
63
57
  export { ReadonlyRepositoryStatements } from "./ReadonlyRepositoryStatements.js";
64
58
  export { CRUDRepositoryStatements } from "./CRUDRepositoryStatements.js";
65
59
  export type { BuiltRepository } from "./BuiltRepository.js";
@@ -74,7 +68,7 @@ export { UnitOfWorkTransaction } from "./UnitOfWorkTransaction.js";
74
68
  export { IsolationLevel } from "./IsolationLevel.js";
75
69
  export type { IsolationLevel as IsolationLevelName } from "./IsolationLevel.js";
76
70
  export type { Comparison } from "./statements/index.js";
77
- export { ArrayExpression, CheckConstraint, ColumnDefault, Condition, ConditionRenderer, ExpressionRenderer, CreateIndexStatement, CreateTableStatement, CreateTableStatementTextRenderer, CreateIndexStatementTextRenderer, DropTableStatement, DropTableStatementTextRenderer, AlterTableStatement, AlterTableStatementTextRenderer, DropIndexStatement, DropIndexStatementTextRenderer, DropTriggerStatement, DropTriggerStatementTextRenderer, CreateTriggerStatement, CreateTriggerStatementTextRenderer, IdentityInsertStatement, IdentityInsertStatementTextRenderer, SqlSeedStatement, ForeignKeyStatement, CreateCreateStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateNestedReadStoredProcedureStatement, CreateUpdateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateCreateStoredProcedureStatementTextRenderer, CreateReadStoredProcedureStatementTextRenderer, CreateNestedReadStoredProcedureStatementTextRenderer, CreateUpdateStoredProcedureStatementTextRenderer, CreateDeleteStoredProcedureStatementTextRenderer, CreateBulkCreateStoredProcedureStatementTextRenderer, CreateBulkUpdateStoredProcedureStatementTextRenderer, CreateBulkDeleteStoredProcedureStatementTextRenderer, DropRoutineStatement, DropRoutineStatementTextRenderer, SelectStatementTextRenderer, InsertStatementTextRenderer, UpdateStatementTextRenderer, DeleteStatementTextRenderer, BulkInsertStatementTextRenderer, BulkUpdateStatementTextRenderer, BulkDeleteStatementTextRenderer, ExecuteStoredProcedureStatementTextRenderer, ReturnStatementTextRenderer, SelectInsertedIdStatementTextRenderer, DefaultStatementTextRenderer, DeleteStatement, ForeignKey, Identifier, InsertStatement, BulkInsertStatement, BulkUpdateStatement, BulkDeleteStatement, Literal, NotNull, Operator, eq, ne, gt, lt, ge, le, PrimaryKey, Aggregate, OrderBy, SelectStatement, StatementParser, StatementRenderer, StatementTextRenderer, StatementNaming, KeywordCasing, constraintNameOf, sqlProcedureStatementsOf, procedureNameOf, Native, n, Subquery, ReferentialAction, ExpressionEvaluator, StatementEvaluator, ExecuteStoredProcedureStatement, SelectInsertedIdStatement, ReturnStatement, UniqueConstraint, UpdateStatement, WhereText, Where, Join, } from "./statements/index.js";
71
+ export { ArrayExpression, CheckConstraint, ColumnDefault, Condition, ConditionRenderer, ExpressionRenderer, CreateIndexStatement, CreateTableStatement, CreateTableStatementTextRenderer, CreateIndexStatementTextRenderer, DropTableStatement, DropTableStatementTextRenderer, AlterTableStatement, AlterTableStatementTextRenderer, DropIndexStatement, DropIndexStatementTextRenderer, DropTriggerStatement, DropTriggerStatementTextRenderer, CreateTriggerStatement, CreateTriggerStatementTextRenderer, IdentityInsertStatement, IdentityInsertStatementTextRenderer, SqlSeedStatement, ForeignKeyStatement, CreateCreateStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateNestedReadStoredProcedureStatement, CreateUpdateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateCreateStoredProcedureStatementTextRenderer, CreateReadStoredProcedureStatementTextRenderer, CreateNestedReadStoredProcedureStatementTextRenderer, CreateUpdateStoredProcedureStatementTextRenderer, CreateDeleteStoredProcedureStatementTextRenderer, CreateBulkCreateStoredProcedureStatementTextRenderer, CreateBulkUpdateStoredProcedureStatementTextRenderer, CreateBulkDeleteStoredProcedureStatementTextRenderer, DropRoutineStatement, DropRoutineStatementTextRenderer, SelectStatementTextRenderer, InsertStatementTextRenderer, UpdateStatementTextRenderer, DeleteStatementTextRenderer, BulkInsertStatementTextRenderer, BulkUpdateStatementTextRenderer, BulkDeleteStatementTextRenderer, ExecuteStoredProcedureStatementTextRenderer, ReturnStatementTextRenderer, SelectInsertedIdStatementTextRenderer, DefaultStatementTextRenderer, DeleteStatement, ForeignKey, Identifier, InsertStatement, BulkInsertStatement, BulkUpdateStatement, BulkDeleteStatement, Literal, NotNull, Operator, eq, ne, gt, lt, ge, le, PrimaryKey, Aggregate, OrderBy, SelectStatement, NestedSelectBuilder, StatementParser, StatementRenderer, StatementTextRenderer, StatementNaming, KeywordCasing, constraintNameOf, sqlProcedureStatementsOf, procedureNameOf, Native, n, Subquery, ReferentialAction, ExpressionEvaluator, StatementEvaluator, ExecuteStoredProcedureStatement, SelectInsertedIdStatement, ReturnStatement, UniqueConstraint, UpdateStatement, WhereText, Where, Join, } from "./statements/index.js";
78
72
  export type { IDatasourceFactory } from "./IDatasourceFactory.js";
79
73
  export type { Parameter } from "./Parameter.js";
80
74
  export { ParameterDirection } from "./ParameterDirection.js";
@@ -115,7 +109,7 @@ export { FlatToNestedMapper } from "./mappers/FlatToNestedMapper.js";
115
109
  export { ChangeCasingMapper } from "./mappers/ChangeCasingMapper.js";
116
110
  export { DictionaryMapper } from "./mappers/DictionaryMapper.js";
117
111
  export { DeterministicTypeMapper } from "./mappers/DeterministicTypeMapper.js";
118
- export { Schema } from "./Schema.js";
112
+ export { Schema, type SchemaColumn } from "./Schema.js";
119
113
  export { IdentityMapper } from "./mappers/IdentityMapper.js";
120
114
  export { PluralizerMapper } from "./mappers/PluralizerMapper.js";
121
115
  export { TransformMapper } from "./mappers/TransformMapper.js";
@@ -124,7 +118,7 @@ export { RowTypeMapper } from "./mappers/RowTypeMapper.js";
124
118
  export { parametersToRecord } from "./parametersToRecord.js";
125
119
  export { ProviderNotImplementedError } from "./ProviderNotImplementedError.js";
126
120
  export { ProviderNotInstalledError } from "./ProviderNotInstalledError.js";
127
- export type { Column, JoinKind, SelectStatementOptions, ReferentialAction as ReferentialActionName, SortDirection, ProcedureSource, ProcedureByGroup } from "./statements/index.js";
121
+ export type { Column, JoinKind, SelectStatementOptions, ReferentialAction as ReferentialActionName, SortDirection, ProcedureSource, ProcedureByGroup, NestedSelectBuilderOptions, NestedSelectRelation, NestedSelectRelationKind, NestedSelectStatement, NestedSelectType } from "./statements/index.js";
128
122
  export type { Field, Scalar } from "./builders/index.js";
129
123
  export { readerToAsyncIterable } from "./readerToAsyncIterable.js";
130
124
  export { StubCommand, StubConnection, StubDatasource, StubReader, StubRepository, StubTransaction, createStubDatasource, } from "./stubs/index.js";
package/dist/index.js CHANGED
@@ -31,7 +31,6 @@ export { ReadonlyRepositoryBuilder } from "./ReadonlyRepositoryBuilder.js";
31
31
  export { CRUDRepositoryBuilder } from "./CRUDRepositoryBuilder.js";
32
32
  export { RepositoryBuilderOptionsFactory } from "./RepositoryBuilderOptionsFactory.js";
33
33
  export { StatementMappers } from "./StatementMappers.js";
34
- export { NestedSelectBuilder } from "./NestedSelectBuilder.js";
35
34
  export { ReadonlyRepositoryStatements } from "./ReadonlyRepositoryStatements.js";
36
35
  export { CRUDRepositoryStatements } from "./CRUDRepositoryStatements.js";
37
36
  export { createCRUDRepositoryOptions } from "./createCRUDRepositoryOptions.js";
@@ -39,7 +38,7 @@ export { createReadonlyRepositoryMappingOptions } from "./createReadonlyReposito
39
38
  export { Response } from "./Response.js";
40
39
  export { UnitOfWorkTransaction } from "./UnitOfWorkTransaction.js";
41
40
  export { IsolationLevel } from "./IsolationLevel.js";
42
- export { ArrayExpression, CheckConstraint, ColumnDefault, Condition, ConditionRenderer, ExpressionRenderer, CreateIndexStatement, CreateTableStatement, CreateTableStatementTextRenderer, CreateIndexStatementTextRenderer, DropTableStatement, DropTableStatementTextRenderer, AlterTableStatement, AlterTableStatementTextRenderer, DropIndexStatement, DropIndexStatementTextRenderer, DropTriggerStatement, DropTriggerStatementTextRenderer, CreateTriggerStatement, CreateTriggerStatementTextRenderer, IdentityInsertStatement, IdentityInsertStatementTextRenderer, SqlSeedStatement, ForeignKeyStatement, CreateCreateStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateNestedReadStoredProcedureStatement, CreateUpdateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateCreateStoredProcedureStatementTextRenderer, CreateReadStoredProcedureStatementTextRenderer, CreateNestedReadStoredProcedureStatementTextRenderer, CreateUpdateStoredProcedureStatementTextRenderer, CreateDeleteStoredProcedureStatementTextRenderer, CreateBulkCreateStoredProcedureStatementTextRenderer, CreateBulkUpdateStoredProcedureStatementTextRenderer, CreateBulkDeleteStoredProcedureStatementTextRenderer, DropRoutineStatement, DropRoutineStatementTextRenderer, SelectStatementTextRenderer, InsertStatementTextRenderer, UpdateStatementTextRenderer, DeleteStatementTextRenderer, BulkInsertStatementTextRenderer, BulkUpdateStatementTextRenderer, BulkDeleteStatementTextRenderer, ExecuteStoredProcedureStatementTextRenderer, ReturnStatementTextRenderer, SelectInsertedIdStatementTextRenderer, DefaultStatementTextRenderer, DeleteStatement, ForeignKey, Identifier, InsertStatement, BulkInsertStatement, BulkUpdateStatement, BulkDeleteStatement, Literal, NotNull, Operator, eq, ne, gt, lt, ge, le, PrimaryKey, Aggregate, OrderBy, SelectStatement, StatementParser, StatementRenderer, StatementTextRenderer, StatementNaming, KeywordCasing, constraintNameOf, sqlProcedureStatementsOf, procedureNameOf, Native, n, Subquery, ReferentialAction, ExpressionEvaluator, StatementEvaluator, ExecuteStoredProcedureStatement, SelectInsertedIdStatement, ReturnStatement, UniqueConstraint, UpdateStatement, WhereText, Where, Join, } from "./statements/index.js";
41
+ export { ArrayExpression, CheckConstraint, ColumnDefault, Condition, ConditionRenderer, ExpressionRenderer, CreateIndexStatement, CreateTableStatement, CreateTableStatementTextRenderer, CreateIndexStatementTextRenderer, DropTableStatement, DropTableStatementTextRenderer, AlterTableStatement, AlterTableStatementTextRenderer, DropIndexStatement, DropIndexStatementTextRenderer, DropTriggerStatement, DropTriggerStatementTextRenderer, CreateTriggerStatement, CreateTriggerStatementTextRenderer, IdentityInsertStatement, IdentityInsertStatementTextRenderer, SqlSeedStatement, ForeignKeyStatement, CreateCreateStoredProcedureStatement, CreateReadStoredProcedureStatement, CreateNestedReadStoredProcedureStatement, CreateUpdateStoredProcedureStatement, CreateDeleteStoredProcedureStatement, CreateBulkCreateStoredProcedureStatement, CreateBulkUpdateStoredProcedureStatement, CreateBulkDeleteStoredProcedureStatement, CreateCreateStoredProcedureStatementTextRenderer, CreateReadStoredProcedureStatementTextRenderer, CreateNestedReadStoredProcedureStatementTextRenderer, CreateUpdateStoredProcedureStatementTextRenderer, CreateDeleteStoredProcedureStatementTextRenderer, CreateBulkCreateStoredProcedureStatementTextRenderer, CreateBulkUpdateStoredProcedureStatementTextRenderer, CreateBulkDeleteStoredProcedureStatementTextRenderer, DropRoutineStatement, DropRoutineStatementTextRenderer, SelectStatementTextRenderer, InsertStatementTextRenderer, UpdateStatementTextRenderer, DeleteStatementTextRenderer, BulkInsertStatementTextRenderer, BulkUpdateStatementTextRenderer, BulkDeleteStatementTextRenderer, ExecuteStoredProcedureStatementTextRenderer, ReturnStatementTextRenderer, SelectInsertedIdStatementTextRenderer, DefaultStatementTextRenderer, DeleteStatement, ForeignKey, Identifier, InsertStatement, BulkInsertStatement, BulkUpdateStatement, BulkDeleteStatement, Literal, NotNull, Operator, eq, ne, gt, lt, ge, le, PrimaryKey, Aggregate, OrderBy, SelectStatement, NestedSelectBuilder, StatementParser, StatementRenderer, StatementTextRenderer, StatementNaming, KeywordCasing, constraintNameOf, sqlProcedureStatementsOf, procedureNameOf, Native, n, Subquery, ReferentialAction, ExpressionEvaluator, StatementEvaluator, ExecuteStoredProcedureStatement, SelectInsertedIdStatement, ReturnStatement, UniqueConstraint, UpdateStatement, WhereText, Where, Join, } from "./statements/index.js";
43
42
  export { ParameterDirection } from "./ParameterDirection.js";
44
43
  export { ArrayReader } from "./ArrayReader.js";
45
44
  export { FirstResultReader } from "./FirstResultReader.js";
@@ -1,4 +1,4 @@
1
- import type { NestedSelectStatement } from "../NestedSelectStatement.js";
1
+ import type { NestedSelectStatement } from "./NestedSelectStatement.js";
2
2
  import type { ICreateNestedReadStoredProcedureStatement } from "./ICreateNestedReadStoredProcedureStatement.js";
3
3
  import type { IStatementVisitor } from "./IStatementVisitor.js";
4
4
  import type { NestedWhereArgument } from "./NestedWhereArgument.js";
@@ -1,4 +1,4 @@
1
- import type { NestedSelectStatement } from "../NestedSelectStatement.js";
1
+ import type { NestedSelectStatement } from "./NestedSelectStatement.js";
2
2
  import type { IStatement } from "./IStatement.js";
3
3
  import type { NestedWhereArgument } from "./NestedWhereArgument.js";
4
4
  export interface ICreateNestedReadStoredProcedureStatement extends IStatement {
@@ -0,0 +1,6 @@
1
+ import type { NestedSelectBuilderOptions } from "./NestedSelectBuilderOptions.js";
2
+ import type { NestedSelectStatement } from "./NestedSelectStatement.js";
3
+ import type { NestedSelectType } from "./NestedSelectType.js";
4
+ export declare class NestedSelectBuilder {
5
+ build(root: NestedSelectType, options?: NestedSelectBuilderOptions): NestedSelectStatement;
6
+ }
@@ -0,0 +1,63 @@
1
+ import { selectFieldAliases } from "./SelectFieldAliases.js";
2
+ import { Join } from "./Join.js";
3
+ import { SelectStatement } from "./SelectStatement.js";
4
+ export class NestedSelectBuilder {
5
+ build(root, options) {
6
+ const stopAt = options?.stopAt ?? [];
7
+ const fields = [];
8
+ const joins = [];
9
+ walk(root, root.name, root.name, "", stopAt, false, fields, joins);
10
+ const aliases = selectFieldAliases(root.name, fields);
11
+ const byPath = Object.fromEntries(fields.map((field, i) => [field, aliases[i]]));
12
+ return {
13
+ select: new SelectStatement({
14
+ table: root.name,
15
+ fields,
16
+ joins,
17
+ where: options?.where,
18
+ }),
19
+ map: mapOf(root, root.name, "", stopAt, byPath),
20
+ };
21
+ }
22
+ }
23
+ const walk = (type, fieldPrefix, joinEntity, stopPrefix, stopAt, outer, fields, joins) => {
24
+ for (const property of type.properties)
25
+ fields.push(`${fieldPrefix}.${property}`);
26
+ for (const relation of type.relations) {
27
+ const path = stopPrefix.length === 0 ? relation.name : `${stopPrefix}.${relation.name}`;
28
+ if (stopped(stopAt, path))
29
+ continue;
30
+ const list = relation.kind === "list";
31
+ joins.push(list || outer
32
+ ? Join.left(relation.target.name, `${joinEntity}.${relation.from}`, `${relation.target.name}.${relation.to}`)
33
+ : Join.inner(relation.target.name, `${joinEntity}.${relation.from}`, `${relation.target.name}.${relation.to}`));
34
+ walk(relation.target, `${fieldPrefix}.${relation.target.name}`, relation.target.name, path, stopAt, outer || list, fields, joins);
35
+ }
36
+ };
37
+ const mapOf = (type, fieldPrefix, stopPrefix, stopAt, aliases) => ({
38
+ key: aliases[`${fieldPrefix}.${type.key}`],
39
+ fields: fieldsOf(type, fieldPrefix, stopPrefix, stopAt, aliases),
40
+ });
41
+ const fieldsOf = (type, fieldPrefix, stopPrefix, stopAt, aliases) => {
42
+ const fields = {};
43
+ for (const property of type.properties) {
44
+ const alias = aliases[`${fieldPrefix}.${property}`];
45
+ fields[alias] = alias;
46
+ }
47
+ for (const relation of type.relations) {
48
+ const path = stopPrefix.length === 0 ? relation.name : `${stopPrefix}.${relation.name}`;
49
+ if (stopped(stopAt, path))
50
+ continue;
51
+ const childPrefix = `${fieldPrefix}.${relation.target.name}`;
52
+ if (relation.kind === "list") {
53
+ fields[relation.name] = {
54
+ key: aliases[`${childPrefix}.${relation.target.key}`],
55
+ fields: fieldsOf(relation.target, childPrefix, path, stopAt, aliases),
56
+ };
57
+ continue;
58
+ }
59
+ Object.assign(fields, fieldsOf(relation.target, childPrefix, path, stopAt, aliases));
60
+ }
61
+ return fields;
62
+ };
63
+ const stopped = (stopAt, path) => stopAt.some((item) => item === path || path.startsWith(`${item}.`));
@@ -0,0 +1,5 @@
1
+ import type { IWhere } from "./IWhere.js";
2
+ export type NestedSelectBuilderOptions = {
3
+ where?: IWhere;
4
+ stopAt?: readonly string[];
5
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { NestedSelectRelationKind } from "./NestedSelectRelationKind.js";
2
+ import type { NestedSelectType } from "./NestedSelectType.js";
3
+ export type NestedSelectRelation = {
4
+ name: string;
5
+ kind: NestedSelectRelationKind;
6
+ from: string;
7
+ to: string;
8
+ target: NestedSelectType;
9
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export type NestedSelectRelationKind = "object" | "list";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { TypeMapGroup } from "../mappers/TypeMapGroup.js";
2
+ import type { SelectStatement } from "./SelectStatement.js";
3
+ export type NestedSelectMapField = string | TypeMapGroup<Record<string, string>>;
4
+ export type NestedSelectStatement = {
5
+ select: SelectStatement;
6
+ map: {
7
+ key: string;
8
+ fields: Record<string, NestedSelectMapField>;
9
+ };
10
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { NestedSelectRelation } from "./NestedSelectRelation.js";
2
+ export type NestedSelectType = {
3
+ name: string;
4
+ key: string;
5
+ properties: readonly string[];
6
+ relations: readonly NestedSelectRelation[];
7
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ export type PathParts = {
2
+ owner: string;
3
+ last: string;
4
+ root: boolean;
5
+ };
6
+ export declare const selectFieldAliases: (table: string, fields: readonly string[], aliases?: readonly (string | undefined)[]) => (string | undefined)[];
7
+ export declare const applySelectFieldAlias: (field: string, alias?: string) => string;
8
+ export declare const pathOf: (field: string, table: string) => PathParts | undefined;
9
+ export declare const functionOf: (field: string) => {
10
+ name: string;
11
+ inner: string;
12
+ } | undefined;
13
+ export declare const asIndexOf: (field: string) => number;
14
+ export declare const segments: (name: string) => string[];
@@ -0,0 +1,62 @@
1
+ export const selectFieldAliases = (table, fields, aliases = []) => {
2
+ const result = fields.map(() => undefined);
3
+ const used = new Set();
4
+ for (const [i] of fields.entries()) {
5
+ const alias = aliases[i];
6
+ if (alias !== undefined) {
7
+ result[i] = alias;
8
+ used.add(alias);
9
+ }
10
+ }
11
+ for (const [i, field] of fields.entries()) {
12
+ const path = pathOf(field, table);
13
+ if (result[i] === undefined && path?.root) {
14
+ result[i] = path.last;
15
+ used.add(path.last);
16
+ }
17
+ }
18
+ for (const [i, field] of fields.entries()) {
19
+ const path = pathOf(field, table);
20
+ if (result[i] === undefined && path && !path.root) {
21
+ const alias = used.has(path.last) ? `${path.owner}${path.last}` : path.last;
22
+ result[i] = alias;
23
+ used.add(alias);
24
+ }
25
+ }
26
+ return result;
27
+ };
28
+ export const applySelectFieldAlias = (field, alias) => alias === undefined ? field : `${field} AS ${alias}`;
29
+ export const pathOf = (field, table) => {
30
+ if (asIndexOf(field) >= 0 || field.includes("("))
31
+ return undefined;
32
+ const parts = segments(field);
33
+ if (parts.length < 2)
34
+ return undefined;
35
+ return {
36
+ owner: parts[parts.length - 2],
37
+ last: parts[parts.length - 1],
38
+ root: parts.length === 2 && parts[0] === table,
39
+ };
40
+ };
41
+ export const functionOf = (field) => {
42
+ const open = field.indexOf("(");
43
+ return open <= 0 || !field.endsWith(")")
44
+ ? undefined
45
+ : { name: field.slice(0, open).trim(), inner: field.slice(open + 1, -1).trim() };
46
+ };
47
+ export const asIndexOf = (field) => field.toUpperCase().indexOf(" AS ");
48
+ export const segments = (name) => {
49
+ const parts = [];
50
+ let quoted = false;
51
+ let start = 0;
52
+ for (let i = 0; i < name.length; i++) {
53
+ if (name[i] === '"')
54
+ quoted = !quoted;
55
+ if (name[i] === "." && !quoted) {
56
+ parts.push(name.slice(start, i));
57
+ start = i + 1;
58
+ }
59
+ }
60
+ parts.push(name.slice(start));
61
+ return parts;
62
+ };
@@ -1,5 +1,5 @@
1
1
  import { orderText } from "./OrderBy.js";
2
- import { applySelectFieldAlias } from "../SelectFieldAliases.js";
2
+ import { applySelectFieldAlias } from "./SelectFieldAliases.js";
3
3
  import { Where } from "./Where.js";
4
4
  export class SelectStatementTextRenderer {
5
5
  text;
@@ -58,6 +58,12 @@ export { ExpressionEvaluator } from "./ExpressionEvaluator.js";
58
58
  export { StatementEvaluator } from "./StatementEvaluator.js";
59
59
  export { WhereText } from "./WhereText.js";
60
60
  export { SelectStatement } from "./SelectStatement.js";
61
+ export { NestedSelectBuilder } from "./NestedSelectBuilder.js";
62
+ export type { NestedSelectBuilderOptions } from "./NestedSelectBuilderOptions.js";
63
+ export type { NestedSelectRelation } from "./NestedSelectRelation.js";
64
+ export type { NestedSelectRelationKind } from "./NestedSelectRelationKind.js";
65
+ export type { NestedSelectStatement } from "./NestedSelectStatement.js";
66
+ export type { NestedSelectType } from "./NestedSelectType.js";
61
67
  export { StatementParser } from "./StatementParser.js";
62
68
  export { StatementTextRenderer } from "./StatementTextRenderer.js";
63
69
  export { StatementNaming } from "./StatementNaming.js";
@@ -14,6 +14,7 @@ export { ExpressionEvaluator } from "./ExpressionEvaluator.js";
14
14
  export { StatementEvaluator } from "./StatementEvaluator.js";
15
15
  export { WhereText } from "./WhereText.js";
16
16
  export { SelectStatement } from "./SelectStatement.js";
17
+ export { NestedSelectBuilder } from "./NestedSelectBuilder.js";
17
18
  export { StatementParser } from "./StatementParser.js";
18
19
  export { StatementTextRenderer } from "./StatementTextRenderer.js";
19
20
  export { StatementNaming } from "./StatementNaming.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deterministic-code/datasources",
3
- "version": "0.1.30",
3
+ "version": "0.1.36",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.js",