@entity-access/entity-access 1.0.2 → 1.0.6

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 (69) hide show
  1. package/.github/workflows/node.yml +7 -2
  2. package/.vscode/settings.json +1 -0
  3. package/README.md +57 -27
  4. package/package.json +2 -2
  5. package/src/common/EntityAccessError.ts +10 -0
  6. package/src/common/IDisposable.ts +25 -0
  7. package/src/common/ImmutableObject.ts +53 -0
  8. package/src/common/Logger.ts +59 -0
  9. package/src/common/TypeInfo.ts +3 -0
  10. package/src/common/cache/TimedCache.ts +2 -2
  11. package/src/common/usingAsync.ts +42 -12
  12. package/src/compiler/QueryCompiler.ts +28 -30
  13. package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +23 -0
  14. package/src/compiler/sql-server/SqlServerSqlMethodTransformer.ts +23 -0
  15. package/src/decorators/ForeignKey.ts +1 -1
  16. package/src/decorators/IClassOf.ts +2 -1
  17. package/src/decorators/IColumn.ts +2 -0
  18. package/src/decorators/parser/NameParser.ts +15 -0
  19. package/src/di/di.ts +224 -0
  20. package/src/drivers/base/BaseDriver.ts +28 -3
  21. package/src/drivers/sql-server/ExpressionToSqlServer.ts +34 -9
  22. package/src/drivers/sql-server/SqlServerDriver.ts +7 -16
  23. package/src/entity-query/EntityType.ts +45 -3
  24. package/src/model/EntityContext.ts +167 -22
  25. package/src/model/EntityQuery.ts +118 -59
  26. package/src/model/EntitySource.ts +38 -46
  27. package/src/model/IFilterWithParameter.ts +5 -0
  28. package/src/model/SourceExpression.ts +21 -25
  29. package/src/model/{ChangeEntry.ts → changes/ChangeEntry.ts} +35 -5
  30. package/src/model/{ChangeSet.ts → changes/ChangeSet.ts} +16 -11
  31. package/src/model/events/ContextEvents.ts +26 -0
  32. package/src/model/events/EntityEvents.ts +96 -0
  33. package/src/model/{IdentityService.ts → identity/IdentityService.ts} +9 -1
  34. package/src/model/identity/RelationMapper.ts +71 -0
  35. package/src/model/symbols.ts +1 -0
  36. package/src/model/verification/VerificationSession.ts +173 -0
  37. package/src/query/ast/DebugStringVisitor.ts +175 -0
  38. package/src/query/ast/ExpressionToSql.ts +277 -119
  39. package/src/query/ast/Expressions.ts +130 -13
  40. package/src/query/ast/IStringTransformer.ts +19 -5
  41. package/src/query/ast/ParameterScope.ts +97 -0
  42. package/src/query/ast/ReplaceParameter.ts +40 -0
  43. package/src/query/ast/Types.ts +0 -0
  44. package/src/query/ast/Visitor.ts +26 -5
  45. package/src/query/expander/QueryExpander.ts +147 -0
  46. package/src/query/parser/ArrowToExpression.ts +134 -19
  47. package/src/query/parser/BabelVisitor.ts +31 -43
  48. package/src/query/parser/NotSupportedError.ts +5 -0
  49. package/src/query/parser/Restructure.ts +66 -0
  50. package/src/query/parser/TransformVisitor.ts +83 -0
  51. package/src/sql/ISql.ts +10 -0
  52. package/src/tests/db-tests/tests/select-items.ts +12 -0
  53. package/src/tests/expressions/left-joins/child-joins.ts +54 -34
  54. package/src/tests/expressions/sanitize/sanitize-test.ts +17 -0
  55. package/src/tests/expressions/select/select.ts +24 -0
  56. package/src/tests/expressions/simple/parse-arrow.ts +10 -0
  57. package/src/tests/model/ShoppingContext.ts +7 -3
  58. package/src/tests/model/createContext.ts +68 -17
  59. package/src/tests/security/ShoppingContextEvents.ts +20 -0
  60. package/src/tests/security/events/OrderEvents.ts +72 -0
  61. package/src/tests/security/events/ProductEvents.ts +92 -0
  62. package/src/tests/security/events/UserEvents.ts +28 -0
  63. package/src/tests/security/events/UserInfo.ts +7 -0
  64. package/src/tests/security/tests/include-items.ts +19 -0
  65. package/src/tests/security/tests/place-order.ts +104 -0
  66. package/test.js +11 -4
  67. package/tsconfig.json +2 -0
  68. package/src/decorators/parser/MemberParser.ts +0 -8
  69. package/src/model/EntitySchema.ts +0 -21
package/src/di/di.ts ADDED
@@ -0,0 +1,224 @@
1
+ import { IDisposable, disposeDisposable } from "../common/IDisposable.js";
2
+ import { IAbstractClassOf, IClassOf } from "../decorators/IClassOf.js";
3
+
4
+ import "reflect-metadata";
5
+ import EntityContext from "../model/EntityContext.js";
6
+ import { BaseDriver } from "../drivers/base/BaseDriver.js";
7
+
8
+ export type ServiceKind = "Singleton" | "Transient" | "Scoped";
9
+
10
+ const registrations = new Map<any,IServiceDescriptor>();
11
+
12
+ export const injectServiceTypesSymbol = Symbol("injectServiceTypes");
13
+ export const injectServiceKeysSymbol = Symbol("injectServiceKeys");
14
+
15
+ const registrationsSymbol = Symbol("registrations");
16
+
17
+ const serviceProvider = Symbol("serviceProvider");
18
+
19
+ const parentServiceProvider = Symbol("parentServiceProvider");
20
+
21
+ export class ServiceProvider implements IDisposable {
22
+
23
+ public static global = new ServiceProvider();
24
+
25
+ public static resolve<T>(serviceOwner: any, type: IClassOf<T>): T {
26
+ const sp = (serviceOwner[serviceProvider] ?? this.global) as ServiceProvider;
27
+ return sp.resolve(type);
28
+ }
29
+
30
+ static create<T>(serviceOwner, type: IClassOf<T>): T {
31
+ const sp = (serviceOwner[serviceProvider] ?? this.global) as ServiceProvider;
32
+ return sp.createFromType(type);
33
+ }
34
+
35
+
36
+ private map: Map<any,any> = new Map();
37
+ private disposables: IDisposable[];
38
+
39
+ constructor(parent?: ServiceProvider) {
40
+ this[serviceProvider] = this;
41
+ this[parentServiceProvider] = parent;
42
+ }
43
+
44
+ add<T1, T extends T1>(type: IAbstractClassOf<T1> | IClassOf<T1>, instance: T) {
45
+ this.getRegistration(type, true);
46
+ this.map.set(type, instance);
47
+ instance[serviceProvider] = this;
48
+ return instance;
49
+ }
50
+
51
+
52
+ createScope() {
53
+ return new ServiceProvider(this);
54
+ }
55
+
56
+ create<T>(type: IClassOf<T>): T {
57
+ return this.createFromType(type);
58
+ }
59
+
60
+
61
+ resolve(type) {
62
+ let instance;
63
+ const sd = this.getRegistration(type);
64
+ switch(sd.kind) {
65
+ case "Scoped":
66
+ if (!this[parentServiceProvider]) {
67
+ throw new Error(`Unable to create scoped service ${type?.name ?? type} in global scope.`);
68
+ }
69
+ instance = this.map.get(type);
70
+ if (!instance) {
71
+ instance = this.createFromDescriptor(sd);
72
+ this.map.set(type, instance);
73
+ instance[serviceProvider] = this;
74
+ if (instance[Symbol.disposable] || instance[Symbol.asyncDisposable]) {
75
+ (this.disposables ??= []).push(instance);
76
+ }
77
+ }
78
+ return instance;
79
+ case "Singleton":
80
+ let sp = this;
81
+ while (sp[parentServiceProvider]) {
82
+ sp = sp[parentServiceProvider];
83
+ }
84
+ instance = sp.map.get(type);
85
+ if (!instance) {
86
+ instance = sp.createFromDescriptor(sd);
87
+ instance[serviceProvider] = sp;
88
+ sp.map.set(type, instance);
89
+ if (instance[Symbol.disposable] || instance[Symbol.asyncDisposable]) {
90
+ (sp.disposables ??= []).push(instance);
91
+ }
92
+ }
93
+ return instance;
94
+ case "Transient":
95
+ instance = sp.createFromDescriptor(sd);
96
+ instance[serviceProvider] = sp;
97
+ return instance;
98
+ }
99
+ }
100
+
101
+ dispose() {
102
+ this[Symbol.disposable]();
103
+ }
104
+
105
+ [Symbol.disposable]() {
106
+ const disposables = this.disposables;
107
+ if (!disposables) {
108
+ return;
109
+ }
110
+ for (const iterator of disposables) {
111
+ disposeDisposable(iterator);
112
+ }
113
+ }
114
+
115
+ private getRegistration(type: any, add = false) {
116
+ let sd = registrations.get(type);
117
+ if (!sd) {
118
+
119
+ if (add) {
120
+ const registration: IServiceDescriptor = { key: type, kind: "Scoped"};
121
+ registrations.set(type, registration);
122
+ return registration;
123
+ }
124
+
125
+ // we need to go through all services
126
+ // to find the derived type
127
+ for (const [key, value] of registrations.entries()) {
128
+ if (key instanceof type) {
129
+ // we found the match..
130
+ registrations.set(type, { ...value, key: type });
131
+ sd = value;
132
+ }
133
+ }
134
+ if (!sd) {
135
+ throw new Error(`No service registered for ${type?.name ?? type}`);
136
+ }
137
+ }
138
+ return sd;
139
+ }
140
+
141
+ private createFromDescriptor(sd: IServiceDescriptor): any {
142
+ if(sd.factory) {
143
+ return sd.factory(this);
144
+ }
145
+ return this.createFromType(sd.key);
146
+ }
147
+
148
+ private createFromType(type): any {
149
+ const injectTypes = type[injectServiceTypesSymbol] as any[];
150
+ const injectServices = injectTypes
151
+ ? injectTypes.map((x) => this.resolve(x))
152
+ : [];
153
+ const instance = new type(... injectServices);
154
+ instance[serviceProvider] = this;
155
+ // initialize properties...
156
+ const keys = type.prototype[injectServiceKeysSymbol];
157
+ if (keys) {
158
+ for (const key in keys) {
159
+ if (Object.prototype.hasOwnProperty.call(keys, key)) {
160
+ const element = keys[key];
161
+ instance[key] = this.resolve(element);
162
+ }
163
+ }
164
+ }
165
+ return instance;
166
+ }
167
+
168
+ }
169
+
170
+ export interface IServiceDescriptor {
171
+
172
+ key: any;
173
+ kind: ServiceKind;
174
+ instance?: any;
175
+ factory?: (sp: ServiceProvider) => any;
176
+ }
177
+
178
+
179
+ export const ServiceCollection = {
180
+ register(kind: ServiceKind, key, factory?: (sp: ServiceProvider) => any) {
181
+ registrations.set(key, { kind, key, factory});
182
+ },
183
+ [registrationsSymbol]: registrations
184
+ };
185
+
186
+ export default function Inject(target, key, index?: number) {
187
+
188
+ if (index !== void 0) {
189
+ const plist = (Reflect as any).getMetadata("design:paramtypes", target, key);
190
+ const serviceTypes = target[injectServiceTypesSymbol] ??= [];
191
+ serviceTypes[index] = plist[index];
192
+ return;
193
+ }
194
+
195
+ const pType = (Reflect as any).getMetadata("design:type", target, key);
196
+ (target[injectServiceKeysSymbol] ??= {})[key] = pType;
197
+ Object.defineProperty(target, key, {
198
+ get() {
199
+ const result = ServiceProvider.resolve(this, pType);
200
+ // get is compatible with AtomWatcher
201
+ // as it will ignore getter and it will
202
+ // not try to set a binding refresher
203
+ Object.defineProperty(target, key, {
204
+ get: () => result
205
+ });
206
+ return result;
207
+ },
208
+ configurable: true
209
+ });
210
+
211
+
212
+ }
213
+
214
+ export function Register(kind: ServiceKind, factory?: (sp: ServiceProvider) => any) {
215
+ return function(target) {
216
+ ServiceCollection.register(kind, target, factory);
217
+ };
218
+ }
219
+
220
+ export const RegisterSingleton = Register("Singleton");
221
+
222
+ export const RegisterScoped = Register("Scoped");
223
+
224
+ export const RegisterTransient = Register("Transient");
@@ -1,8 +1,8 @@
1
1
  import QueryCompiler from "../../compiler/QueryCompiler.js";
2
2
  import EntityType from "../../entity-query/EntityType.js";
3
3
  import Migrations from "../../migrations/Migrations.js";
4
- import ChangeEntry from "../../model/ChangeEntry.js";
5
- import { BinaryExpression, Constant, Expression, InsertStatement, QuotedLiteral, ReturnUpdated, TableLiteral, UpdateStatement, ValuesStatement } from "../../query/ast/Expressions.js";
4
+ import ChangeEntry from "../../model/changes/ChangeEntry.js";
5
+ import { BinaryExpression, Constant, DeleteStatement, Expression, InsertStatement, QuotedLiteral, ReturnUpdated, TableLiteral, UpdateStatement, ValuesStatement } from "../../query/ast/Expressions.js";
6
6
 
7
7
  export const disposableSymbol: unique symbol = (Symbol as any).dispose ??= Symbol("disposable");
8
8
 
@@ -47,7 +47,6 @@ export interface IQueryResult {
47
47
  }
48
48
 
49
49
  export abstract class BaseDriver {
50
-
51
50
  abstract get compiler(): QueryCompiler;
52
51
 
53
52
  constructor(public readonly connectionString: IDbConnectionString) {}
@@ -131,4 +130,30 @@ export abstract class BaseDriver {
131
130
  });
132
131
  }
133
132
 
133
+ createDeleteExpression(type: EntityType, entity: any) {
134
+ let where: Expression;
135
+ for (const iterator of type.keys) {
136
+ const key = entity[iterator.name];
137
+ if (!key) {
138
+ return null;
139
+ }
140
+ const compare = BinaryExpression.create({
141
+ left: QuotedLiteral.create({ literal: iterator.columnName }),
142
+ operator: "=",
143
+ right: Constant.create({ value: key })
144
+ });
145
+ where = where
146
+ ? BinaryExpression.create({ left: where, operator: "AND", right: compare })
147
+ : compare;
148
+ }
149
+ if (!where) {
150
+ return null;
151
+ }
152
+ return DeleteStatement.create({
153
+ table: type.fullyQualifiedName,
154
+ where
155
+ });
156
+ }
157
+
158
+
134
159
  }
@@ -1,10 +1,10 @@
1
1
  import ExpressionToSql from "../../query/ast/ExpressionToSql.js";
2
2
  import { Identifier, InsertStatement, OrderByExpression, ReturnUpdated, SelectStatement, ValuesStatement } from "../../query/ast/Expressions.js";
3
- import { ITextOrFunctionArray, prepare } from "../../query/ast/IStringTransformer.js";
3
+ import { ITextQuery, prepare } from "../../query/ast/IStringTransformer.js";
4
4
 
5
5
  export default class ExpressionToSqlServer extends ExpressionToSql {
6
6
 
7
- visitReturnUpdated(e: ReturnUpdated): ITextOrFunctionArray {
7
+ visitReturnUpdated(e: ReturnUpdated): ITextQuery {
8
8
  if (!e) {
9
9
  return [];
10
10
  }
@@ -22,7 +22,7 @@ export default class ExpressionToSqlServer extends ExpressionToSql {
22
22
  return prepare ` OUTPUT ${fields}`;
23
23
  }
24
24
 
25
- visitInsertStatement(e: InsertStatement): ITextOrFunctionArray {
25
+ visitInsertStatement(e: InsertStatement): ITextQuery {
26
26
  const returnValues = this.visit(e.returnValues);
27
27
  if (e.values instanceof ValuesStatement) {
28
28
 
@@ -45,7 +45,14 @@ export default class ExpressionToSqlServer extends ExpressionToSql {
45
45
 
46
46
  }
47
47
 
48
- visitSelectStatement(e: SelectStatement): ITextOrFunctionArray {
48
+ visitSelectStatement(e: SelectStatement): ITextQuery {
49
+
50
+ this.prepareStatement(e);
51
+
52
+ const orderBy = e.orderBy?.length > 0 ? prepare `\n\t\tORDER BY ${this.visitArray(e.orderBy)}` : "";
53
+ const where = e.where ? prepare `\n\tWHERE ${this.visit(e.where)}` : "";
54
+ const joins = e.joins?.length > 0 ? prepare `\n\t\t${this.visitArray(e.joins, "\n")}` : [];
55
+
49
56
  const fields = this.visitArray(e.fields, ",\n\t\t");
50
57
 
51
58
  const showTop = e.limit && !e.offset;
@@ -64,15 +71,33 @@ export default class ExpressionToSqlServer extends ExpressionToSql {
64
71
  const topValue = Number(e.limit);
65
72
  const top = showTop ? prepare ` TOP (${() => topValue}) ` : "";
66
73
 
67
- const orderBy = e.orderBy?.length > 0 ? prepare `\n\t\tORDER BY ${this.visitArray(e.orderBy)}` : "";
68
- const source = this.visit(e.source);
69
- const where = e.where ? prepare `\n\tWHERE ${this.visit(e.where)}` : "";
70
- const as = e.as ? prepare ` AS ${this.visit(e.as)}` : "";
71
- const joins = e.joins?.length > 0 ? prepare `\n\t\t${this.visitArray(e.joins)}` : [];
74
+ let source: ITextQuery;
75
+ let as: ITextQuery | "";
76
+ if (e.source.type === "ValuesStatement") {
77
+ const v = e.source as ValuesStatement;
78
+ const rows = v.values.map((x) => prepare `(${this.visitArray(x)})`);
79
+ source = prepare `(VALUES ${rows}) as ${this.visit(e.as)}(${this.visitArray(v.fields)})`;
80
+ as = [];
81
+ } else {
82
+ source = this.visit(e.source);
83
+ as = e.as ? prepare ` AS ${this.visit(e.as)}` : "";
84
+ }
85
+
86
+ // const as = e.as ? prepare ` AS ${this.visit(e.as)}` : "";
72
87
  const offset = showFetch ? prepare ` OFFSET ${Number(e.offset).toString()} ROWS ` : "";
73
88
  const next = showFetch ? prepare ` FETCH NEXT ${Number(e.limit).toString()} ROWS ONLY` : "";
74
89
  return prepare `SELECT ${top}
75
90
  ${fields}
76
91
  FROM ${source}${as}${joins}${where}${orderBy}${offset}${next}`;
77
92
  }
93
+
94
+ visitValuesStatement(e: ValuesStatement): ITextQuery {
95
+ const rows = [];
96
+ for (const rowValues of e.values) {
97
+ rows.push(prepare `(${ this.visitArray(rowValues) })`);
98
+ }
99
+ const fields = e.fields ? prepare ` as x11(${this.visitArray(e.fields)})` : "";
100
+ return prepare `(VALUES ${rows}) ${fields}`;
101
+
102
+ }
78
103
  }
@@ -4,13 +4,11 @@ import Migrations from "../../migrations/Migrations.js";
4
4
  import { BaseDriver, IDbConnectionString, IDbReader, IQuery, IRecord, disposableSymbol, toQuery } from "../base/BaseDriver.js";
5
5
  import sql from "mssql";
6
6
  import SqlServerQueryCompiler from "./SqlServerQueryCompiler.js";
7
- import SqlServerSqlMethodTransformer from "../../compiler/sql-server/SqlServerSqlMethodTransformer.js";
8
7
  import SqlServerAutomaticMigrations from "../../migrations/sql-server/SqlServerAutomaticMigrations.js";
9
8
  import { SqlServerLiteral } from "./SqlServerLiteral.js";
10
- import usingAsync from "../../common/usingAsync.js";
11
9
  import TimedCache from "../../common/cache/TimedCache.js";
12
10
 
13
- export type ISqlServerConnectionString = sql.config;
11
+ export type ISqlServerConnectionString = IDbConnectionString & sql.config;
14
12
 
15
13
  const namedPool = new TimedCache<string, sql.ConnectionPool>();
16
14
 
@@ -24,14 +22,8 @@ export default class SqlServerDriver extends BaseDriver {
24
22
  private transaction: sql.Transaction;
25
23
 
26
24
  constructor(private readonly config: ISqlServerConnectionString) {
27
- super({
28
- database: config.database,
29
- host: config.server ??= (config as any).host,
30
- port: config.port,
31
- password: config.password,
32
- user: config.user,
33
- ... config,
34
- });
25
+ super(config);
26
+ config.server = config.host;
35
27
  }
36
28
 
37
29
  public async executeReader(command: IQuery, signal?: AbortSignal): Promise<IDbReader> {
@@ -61,7 +53,6 @@ export default class SqlServerDriver extends BaseDriver {
61
53
  }
62
54
 
63
55
  try {
64
- console.log(command.text);
65
56
  const r = await rq.query(command.text);
66
57
  return { rows: r.recordset ?? [r.output], updated: r.rowsAffected [0]};
67
58
  } catch (error) {
@@ -135,10 +126,11 @@ export default class SqlServerDriver extends BaseDriver {
135
126
  }
136
127
 
137
128
  private newConnection() {
138
- const key = this.config.server + "//" + this.config.database + "/" + this.config.user;
139
- return namedPool.getOrCreateAsync(this.config.server + "://" + this.config.database,
129
+ const config = this.config;
130
+ const key = config.server + "//" + config.database + "/" + config.user;
131
+ return namedPool.getOrCreateAsync(config.server + "://" + config.database,
140
132
  () => {
141
- const pool = new sql.ConnectionPool(this.config);
133
+ const pool = new sql.ConnectionPool(config);
142
134
  const oldClose = pool.close;
143
135
  pool.close = ((c) => {
144
136
  namedPool.delete(key);
@@ -188,7 +180,6 @@ class SqlReader implements IDbReader {
188
180
  this.processPendingRows();
189
181
  });
190
182
 
191
- console.log(`Executing ${(command as any).text}`);
192
183
  void rq.query((command as any).text);
193
184
 
194
185
  do {
@@ -1,9 +1,9 @@
1
1
  import type { IColumn, IEntityRelation } from "../decorators/IColumn.js";
2
2
  import { IClassOf } from "../decorators/IClassOf.js";
3
3
  import { Query } from "../query/Query.js";
4
- import NameParser from "../decorators/parser/MemberParser.js";
4
+ import NameParser from "../decorators/parser/NameParser.js";
5
5
  import SchemaRegistry from "../decorators/SchemaRegistry.js";
6
- import { QuotedLiteral, TableLiteral } from "../query/ast/Expressions.js";
6
+ import { Expression, ExpressionAs, QuotedLiteral, SelectStatement, TableLiteral } from "../query/ast/Expressions.js";
7
7
  import InstanceCache from "../common/cache/InstanceCache.js";
8
8
 
9
9
 
@@ -40,6 +40,9 @@ export default class EntityType {
40
40
  private columnMap: Map<string, IColumn> = new Map();
41
41
  private relationMap: Map<string, IEntityRelation> = new Map();
42
42
 
43
+ private selectAll: SelectStatement;
44
+ private selectOne: SelectStatement;
45
+
43
46
  public getProperty(name: string) {
44
47
  const field = this.fieldMap.get(name);
45
48
  const relation = this.relationMap.get(name);
@@ -103,14 +106,53 @@ export default class EntityType {
103
106
  relatedTypeClass: this.typeClass,
104
107
  dotNotCreateIndex: true,
105
108
  fkColumn,
106
- isCollection: true,
109
+ isInverseRelation: true,
107
110
  relatedRelation: relation,
108
111
  relatedEntity: this
109
112
  };
110
113
  relatedType.relationMap.set(inverseRelation.name, inverseRelation);
111
114
  relatedType.relations.push(inverseRelation);
112
115
  inverseRelation.relatedRelation = relation;
116
+ relation.relatedRelation = inverseRelation;
113
117
 
114
118
  }
115
119
 
120
+ public selectAllFields() {
121
+ if (this.selectAll) {
122
+ return { ... this.selectAll };
123
+ }
124
+ const source = this.fullyQualifiedName;
125
+ const as = Expression.parameter(this.name[0] + "1");
126
+ const fields = this.columns.map((c) => c.name !== c.columnName
127
+ ? ExpressionAs.create({
128
+ expression: Expression.member(as, c.columnName),
129
+ alias: QuotedLiteral.create({ literal: c.name })
130
+ })
131
+ : Expression.member(as, c.columnName));
132
+ this.selectAll = SelectStatement.create({
133
+ source,
134
+ model: this,
135
+ as,
136
+ fields
137
+ });
138
+ return { ... this.selectAll };
139
+ }
140
+
141
+ public selectOneNumber() {
142
+ if (this.selectOne) {
143
+ return { ... this.selectOne };
144
+ }
145
+ const source = this.fullyQualifiedName;
146
+ const as = Expression.parameter(this.name[0] + "1");
147
+ const fields = [
148
+ Expression.identifier("1")
149
+ ];
150
+ this.selectOne = SelectStatement.create({
151
+ source,
152
+ model: this,
153
+ as,
154
+ fields
155
+ });
156
+ return { ... this.selectOne };
157
+ }
116
158
  }