@entity-access/entity-access 1.0.1

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 (70) hide show
  1. package/.eslintrc.cjs +234 -0
  2. package/.github/workflows/node.yml +44 -0
  3. package/.github/workflows/npm-publish.yml +33 -0
  4. package/.vscode/launch.json +20 -0
  5. package/.vscode/settings.json +39 -0
  6. package/LICENSE +201 -0
  7. package/README.md +247 -0
  8. package/package.json +39 -0
  9. package/src/TestRunner.ts +2 -0
  10. package/src/common/cache/InstanceCache.ts +14 -0
  11. package/src/common/cache/TimedCache.ts +74 -0
  12. package/src/common/symbols/symbols.ts +2 -0
  13. package/src/common/usingAsync.ts +24 -0
  14. package/src/compiler/ISqlHelpers.ts +30 -0
  15. package/src/compiler/QueryCompiler.ts +88 -0
  16. package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +83 -0
  17. package/src/compiler/sql-server/SqlServerSqlMethodTransformer.ts +83 -0
  18. package/src/decorators/Column.ts +46 -0
  19. package/src/decorators/ForeignKey.ts +42 -0
  20. package/src/decorators/IClassOf.ts +1 -0
  21. package/src/decorators/IColumn.ts +70 -0
  22. package/src/decorators/ISqlType.ts +70 -0
  23. package/src/decorators/SchemaRegistry.ts +19 -0
  24. package/src/decorators/Table.ts +18 -0
  25. package/src/decorators/parser/MemberParser.ts +8 -0
  26. package/src/drivers/base/BaseDriver.ts +134 -0
  27. package/src/drivers/postgres/PostgreSqlDriver.ts +178 -0
  28. package/src/drivers/sql-server/ExpressionToSqlServer.ts +78 -0
  29. package/src/drivers/sql-server/SqlServerDriver.ts +215 -0
  30. package/src/drivers/sql-server/SqlServerLiteral.ts +12 -0
  31. package/src/drivers/sql-server/SqlServerQueryCompiler.ts +25 -0
  32. package/src/entity-query/EntityType.ts +116 -0
  33. package/src/migrations/Migrations.ts +17 -0
  34. package/src/migrations/postgres/PostgresAutomaticMigrations.ts +97 -0
  35. package/src/migrations/postgres/PostgresMigrations.ts +56 -0
  36. package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +102 -0
  37. package/src/migrations/sql-server/SqlServerMigrations.ts +60 -0
  38. package/src/model/ChangeEntry.ts +154 -0
  39. package/src/model/ChangeSet.ts +88 -0
  40. package/src/model/EntityContext.ts +48 -0
  41. package/src/model/EntityModel.ts +27 -0
  42. package/src/model/EntityQuery.ts +152 -0
  43. package/src/model/EntitySchema.ts +21 -0
  44. package/src/model/EntitySource.ts +98 -0
  45. package/src/model/IFilterWithParameter.ts +38 -0
  46. package/src/model/IdentityService.ts +23 -0
  47. package/src/model/SourceExpression.ts +137 -0
  48. package/src/query/Query.ts +158 -0
  49. package/src/query/ast/ExpressionToSql.ts +348 -0
  50. package/src/query/ast/Expressions.ts +294 -0
  51. package/src/query/ast/IStringTransformer.ts +74 -0
  52. package/src/query/ast/SqlLiteral.ts +25 -0
  53. package/src/query/ast/Visitor.ts +159 -0
  54. package/src/query/parser/ArrowToExpression.ts +160 -0
  55. package/src/query/parser/BabelVisitor.ts +86 -0
  56. package/src/sql/ISql.ts +31 -0
  57. package/src/sql/Sql.ts +4 -0
  58. package/src/tests/TestConfig.ts +6 -0
  59. package/src/tests/db-tests/tests/select-items.ts +37 -0
  60. package/src/tests/db-tests/tests/update-items.ts +17 -0
  61. package/src/tests/drivers/postgres/connection-test.ts +29 -0
  62. package/src/tests/drivers/sql-server/sql-server-test.ts +9 -0
  63. package/src/tests/expressions/left-joins/child-joins.ts +71 -0
  64. package/src/tests/expressions/simple/parse-arrow.ts +46 -0
  65. package/src/tests/expressions/trimInternal.ts +23 -0
  66. package/src/tests/model/ShoppingContext.ts +203 -0
  67. package/src/tests/model/createContext.ts +294 -0
  68. package/src/tests/query/combine.ts +28 -0
  69. package/test.js +107 -0
  70. package/tsconfig.json +22 -0
@@ -0,0 +1,46 @@
1
+ import { IColumn } from "./IColumn.js";
2
+ import { ISqlType } from "./ISqlType.js";
3
+ import SchemaRegistry from "./SchemaRegistry.js";
4
+
5
+ import "reflect-metadata";
6
+
7
+ ;
8
+ export default function Column(cfg: Omit<Omit<IColumn, "name">, "type"> = {}): any {
9
+ return (target, key) => {
10
+ const cn = target.constructor ?? target;
11
+ const model = SchemaRegistry.model(cn);
12
+ const c = cfg as IColumn;
13
+ c.columnName ??= key;
14
+ c.name = key;
15
+ c.nullable ??= false;
16
+ c.order ??= model.columns.length;
17
+ c.type = model;
18
+
19
+ if (c.dataType === void 0) {
20
+ const jsType = (Reflect as any).getMetadata("design:type", target, key);
21
+ c.dataType = typeFrom(c, jsType);
22
+ }
23
+
24
+
25
+ model.addColumn(c);
26
+ };
27
+ }
28
+
29
+ function typeFrom(c: IColumn, jsType: any): ISqlType {
30
+ switch(jsType) {
31
+ case String:
32
+ return "Char";
33
+ case Number:
34
+ if (c.key) {
35
+ return "BigInt";
36
+ }
37
+ return "Double";
38
+ case BigInt:
39
+ return "BigInt";
40
+ case Date:
41
+ return "DateTime";
42
+ case Boolean:
43
+ return "Boolean";
44
+ }
45
+ return "Char";
46
+ }
@@ -0,0 +1,42 @@
1
+ import type { IClassOf } from "./IClassOf.js";
2
+ import SchemaRegistry from "./SchemaRegistry.js";
3
+ import NameParser from "./parser/MemberParser.js";
4
+
5
+
6
+ export default function ForeignKey<T, TRelated>(
7
+ {
8
+ key: name,
9
+ related: c,
10
+ relatedProperty: inv,
11
+ relatedKey: invKey,
12
+ dotNotCreateIndex
13
+ } : {
14
+
15
+ key: (item: T) => any,
16
+
17
+ related: IClassOf<TRelated>,
18
+
19
+ relatedProperty: (item: TRelated) => any,
20
+
21
+ relatedKey?: (item: TRelated) => any,
22
+ dotNotCreateIndex?: boolean,
23
+ }
24
+
25
+ ) {
26
+ return (target: T, key: string): any => {
27
+
28
+ const cn = target.constructor ?? target;
29
+ const type = SchemaRegistry.model(cn);
30
+
31
+ type.addRelation({
32
+ type,
33
+ name: key,
34
+ foreignKey: NameParser.parseMember(name),
35
+ relatedTypeClass: c,
36
+ relatedName: NameParser.parseMember(inv),
37
+ relatedKey: invKey ? NameParser.parseMember(invKey) : void 0,
38
+ dotNotCreateIndex
39
+ });
40
+
41
+ };
42
+ }
@@ -0,0 +1 @@
1
+ export type IClassOf<T> = abstract new (... a: any[]) => T;
@@ -0,0 +1,70 @@
1
+ import EntityType from "../entity-query/EntityType.js";
2
+ import { IClassOf } from "./IClassOf.js";
3
+ import { ISqlType } from "./ISqlType.js";
4
+
5
+
6
+ export interface IColumn {
7
+ name?: string;
8
+ columnName?: string;
9
+ order?: number;
10
+ key?: boolean;
11
+ autoGenerate?: boolean;
12
+ dataType?: ISqlType;
13
+ nullable?: boolean;
14
+ /**
15
+ * If length is specified, it will take exact same length always.
16
+ */
17
+ fixed?: boolean;
18
+ /**
19
+ * If length is not specified, text will be unlimited or variable length with maximum size.
20
+ */
21
+ length?: number;
22
+
23
+ precision?: number;
24
+ scale?: number;
25
+
26
+ type?: EntityType;
27
+
28
+ /**
29
+ * String representation of the default, empty string must be specified as ""
30
+ */
31
+ default?: string;
32
+
33
+ /**
34
+ * This only identifies itself as relation's foreign key, this will be set automatically.
35
+ */
36
+ fkRelation?: IEntityRelation;
37
+ }
38
+
39
+ export interface IEntityRelation {
40
+
41
+ type?: EntityType;
42
+
43
+ /**
44
+ * Name of own field...
45
+ */
46
+ name: string;
47
+
48
+ isCollection?: boolean;
49
+
50
+
51
+ foreignKey: string;
52
+
53
+ relatedTypeClass: IClassOf<any>;
54
+
55
+ fkColumn?: IColumn;
56
+
57
+
58
+ relatedName: string;
59
+
60
+ relatedKey?: string;
61
+
62
+
63
+ relatedEntity?: EntityType;
64
+
65
+ relatedRelation?: IEntityRelation;
66
+
67
+ dotNotCreateIndex?: boolean;
68
+
69
+ }
70
+
@@ -0,0 +1,70 @@
1
+
2
+ export type ISqlType =
3
+ /**
4
+ * BigInt, long, 8 bytes
5
+ */
6
+ "BigInt" |
7
+
8
+ /**
9
+ * Integer, 4 bytes
10
+ */
11
+ "Int" |
12
+
13
+ /**
14
+ * Floating Point number, 4 bytes only
15
+ */
16
+
17
+ "Float" |
18
+
19
+
20
+ /**
21
+ * Numeric with default (18,2) precision ans scale, you can change precision
22
+ */
23
+ "Decimal" |
24
+
25
+ /**
26
+ * Double, floating point number, 8 bytes
27
+ */
28
+ "Double" |
29
+ /**
30
+ * Date and Time, usually 8 bytes
31
+ */
32
+ "DateTime" |
33
+ /**
34
+ * Date and Time with TimeZone, 8+ bytes
35
+ */
36
+ "DateTimeOffset" |
37
+ /**
38
+ * Ascii character, single byte, do not use for unicode
39
+ */
40
+ "AsciiChar" |
41
+
42
+ /**
43
+ * Unicode character
44
+ */
45
+
46
+ "Char" |
47
+ /**
48
+ * Single bit
49
+ */
50
+ "Boolean" |
51
+
52
+ /**
53
+ * uuid
54
+ */
55
+ "UUID" |
56
+
57
+ /**
58
+ * text json
59
+ */
60
+ "JSON" |
61
+
62
+ /**
63
+ * Binary Json
64
+ */
65
+ "JSONB" |
66
+
67
+ /**
68
+ * Byte Array - var binary
69
+ */
70
+ "ByteArray";
@@ -0,0 +1,19 @@
1
+ import EntityType from "../entity-query/EntityType.js";
2
+
3
+
4
+ export default class SchemaRegistry {
5
+
6
+ public static model(type) {
7
+ let model = this.map.get(type);
8
+ if (!model) {
9
+ model = new EntityType();
10
+ // @ts-expect-error readonly for compile time only
11
+ model.typeClass = type;
12
+ this.map.set(type, model);
13
+ }
14
+ return model;
15
+ }
16
+
17
+ private static map: Map<any, EntityType> = new Map();
18
+
19
+ }
@@ -0,0 +1,18 @@
1
+ import SchemaRegistry from "./SchemaRegistry.js";
2
+
3
+ export interface ITable {
4
+ name: string;
5
+ schema?: string;
6
+ }
7
+
8
+ export default function Table<T>(
9
+ name: string,
10
+ schema?: string): any {
11
+ return (target: T) => {
12
+ const model = SchemaRegistry.model(target);
13
+ // @ts-expect-error readonly
14
+ model.name = name;
15
+ // @ts-expect-error readonly
16
+ model.schema = schema;
17
+ };
18
+ }
@@ -0,0 +1,8 @@
1
+ export default class NameParser {
2
+ public static parseMember(text: ((a: any) => any)) {
3
+ const t: string = text.toString();
4
+
5
+ const index = t.lastIndexOf(".");
6
+ return t.substring(index + 1);
7
+ }
8
+ }
@@ -0,0 +1,134 @@
1
+ import QueryCompiler from "../../compiler/QueryCompiler.js";
2
+ import EntityType from "../../entity-query/EntityType.js";
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";
6
+
7
+ export const disposableSymbol: unique symbol = (Symbol as any).dispose ??= Symbol("disposable");
8
+
9
+ interface IDisposable {
10
+ [disposableSymbol]?(): void;
11
+ }
12
+
13
+ export interface IRecord {
14
+ [key: string]: string | boolean | number | Date | Uint8Array | Blob;
15
+ }
16
+
17
+ export interface IDbConnectionString {
18
+ host?: string;
19
+ port?: number;
20
+ user?: string;
21
+ password?: string;
22
+ database?: string;
23
+ }
24
+
25
+ export interface IDbReader extends IDisposable {
26
+ next(min?: number, signal?: AbortSignal): AsyncGenerator<IRecord, any, any>;
27
+ dispose(): Promise<any>;
28
+ }
29
+
30
+ export const toQuery = (text: IQuery): { text: string, values?: any[]} => typeof text === "string"
31
+ ? { text, values: [] }
32
+ : text;
33
+
34
+ export type IQuery = string | {
35
+ text: string;
36
+ values?: any[];
37
+ };
38
+
39
+ export interface IQueryTask {
40
+ query: IQuery;
41
+ postExecution?: ((r: any) => Promise<any>);
42
+ }
43
+
44
+ export interface IQueryResult {
45
+ rows?: any[];
46
+ updated?: number;
47
+ }
48
+
49
+ export abstract class BaseDriver {
50
+
51
+ abstract get compiler(): QueryCompiler;
52
+
53
+ constructor(public readonly connectionString: IDbConnectionString) {}
54
+
55
+ public abstract executeReader(command: IQuery, signal?: AbortSignal): Promise<IDbReader>;
56
+
57
+ public abstract executeQuery(command: IQuery, signal?: AbortSignal): Promise<IQueryResult>;
58
+
59
+ public abstract ensureDatabase(): Promise<any>;
60
+
61
+ public abstract runInTransaction<T = any>(fx?: () => Promise<T>): Promise<T>;
62
+
63
+ /**
64
+ * This migrations only support creation of missing items.
65
+ * However, you can provide events to change existing items.
66
+ */
67
+ public abstract automaticMigrations(): Migrations;
68
+
69
+ createInsertExpression(type: EntityType, entity: any): InsertStatement {
70
+ const returnFields = [] as QuotedLiteral[];
71
+ const fields = [] as QuotedLiteral[];
72
+ const values = [] as Constant[];
73
+ for (const iterator of type.columns) {
74
+ const literal = QuotedLiteral.create({ literal: iterator.columnName });
75
+ if (iterator.autoGenerate) {
76
+ returnFields.push(literal);
77
+ continue;
78
+ }
79
+ const value = entity[iterator.name];
80
+ if (value === void 0) {
81
+ continue;
82
+ }
83
+ fields.push(literal);
84
+ values.push(Constant.create({ value }));
85
+ }
86
+
87
+ const name = QuotedLiteral.create({ literal: type.name });
88
+ const schema = type.schema ? QuotedLiteral.create({ literal: type.schema }) : void 0;
89
+
90
+ return InsertStatement.create({
91
+ table: TableLiteral.create({
92
+ name,
93
+ schema
94
+ }),
95
+ values: ValuesStatement.create({ fields, values: [values] }),
96
+ returnValues: ReturnUpdated.create({
97
+ changes: "INSERTED",
98
+ fields: returnFields
99
+ }),
100
+ });
101
+ }
102
+
103
+ createUpdateExpression(entry: ChangeEntry) {
104
+ const set = [] as BinaryExpression[];
105
+ for (const [key, change] of entry.modified) {
106
+ set.push(BinaryExpression.create({
107
+ left: QuotedLiteral.create({ literal: key.columnName}),
108
+ operator: "=",
109
+ right: Constant.create({ value: change.newValue ?? null })
110
+ }));
111
+ }
112
+ let where = null as Expression;
113
+ for (const iterator of entry.type.keys) {
114
+ const compare = BinaryExpression.create({
115
+ left: QuotedLiteral.create({ literal: iterator.columnName }),
116
+ operator: "=",
117
+ right: Constant.create({ value: entry.entity[iterator.name]})
118
+ });
119
+ where = !where
120
+ ? compare
121
+ : BinaryExpression.create({
122
+ left: where,
123
+ operator: "AND",
124
+ right: compare
125
+ });
126
+ }
127
+ return UpdateStatement.create({
128
+ set,
129
+ table: entry.type.fullyQualifiedName,
130
+ where
131
+ });
132
+ }
133
+
134
+ }
@@ -0,0 +1,178 @@
1
+ /* eslint-disable no-console */
2
+ import QueryCompiler from "../../compiler/QueryCompiler.js";
3
+ import { IColumn } from "../../decorators/IColumn.js";
4
+ import EntityType from "../../entity-query/EntityType.js";
5
+ import Migrations from "../../migrations/Migrations.js";
6
+ import PostgresAutomaticMigrations from "../../migrations/postgres/PostgresAutomaticMigrations.js";
7
+ import { Query } from "../../query/Query.js";
8
+ import { BaseDriver, IDbConnectionString, IDbReader, IQuery, IRecord, toQuery } from "../base/BaseDriver.js";
9
+ import pkg from "pg";
10
+ import Cursor from "pg-cursor";
11
+
12
+ const { Client } = pkg;
13
+
14
+ export interface IPgSqlConnectionString extends IDbConnectionString {
15
+
16
+ user?: string, // default process.env.PGUSER || process.env.USER
17
+ password?: string, // default process.env.PGPASSWORD
18
+ host?: string, // default process.env.PGHOST
19
+ database?: string, // default process.env.PGDATABASE || user
20
+ port?: number, // default process.env.PGPORT
21
+ connectionString?: string, // e.g. postgres://user:password@host:5432/database
22
+ ssl?: any, // passed directly to node.TLSSocket, supports all tls.connect options
23
+ types?: any, // custom type parsers
24
+ statement_timeout?: number, // number of milliseconds before a statement in query will time out, default is no timeout
25
+ query_timeout?: number, // number of milliseconds before a query call will timeout, default is no timeout
26
+ application_name?: string, // The name of the application that created this Client instance
27
+ connectionTimeoutMillis?: number, // number of milliseconds to wait for connection, default is no timeout
28
+ idle_in_transaction_session_timeout?: number // number of milliseconds before terminating any session with an open idle transaction, default is no timeout
29
+ };
30
+
31
+ class DbReader implements IDbReader {
32
+
33
+ constructor(private cursor, private client) {
34
+
35
+ }
36
+
37
+ async *next(min = 100) {
38
+ do {
39
+ const rows = await this.cursor.read(min);
40
+ yield *rows;
41
+ if (rows.length === 0) {
42
+ break;
43
+ }
44
+ } while (true);
45
+ }
46
+
47
+ async dispose() {
48
+ try {
49
+ await this.cursor.close();
50
+ } catch {
51
+ // intentionally left blank
52
+ }
53
+
54
+ try {
55
+ if (this.client) {
56
+ await this.client.end();
57
+ }
58
+ } catch {
59
+ // intentionally left blank
60
+ }
61
+ }
62
+ }
63
+
64
+ export default class PostgreSqlDriver extends BaseDriver {
65
+
66
+ public get compiler() {
67
+ return this.myCompiler;
68
+ }
69
+
70
+ private transaction: pkg.Client;
71
+ private myCompiler = new QueryCompiler();
72
+
73
+ constructor(private readonly config: IPgSqlConnectionString) {
74
+ super(config);
75
+ }
76
+
77
+ public async runInTransaction<T>(fx?: () => Promise<T>): Promise<T> {
78
+ const connection = await this.getConnection();
79
+ let result: T;
80
+ try {
81
+ this.transaction = connection;
82
+ await connection.query("BEGIN");
83
+ result = await fx();
84
+ await connection.query("COMMIT");
85
+ return result;
86
+ } catch (error) {
87
+ await connection.query("ROLLBACK");
88
+ throw error;
89
+ } finally {
90
+ this.transaction = void 0;
91
+ await connection.end();
92
+ }
93
+ }
94
+
95
+ public automaticMigrations(): Migrations {
96
+ return new PostgresAutomaticMigrations();
97
+ }
98
+
99
+ public async executeReader(command: IQuery, signal?: AbortSignal): Promise<IDbReader> {
100
+ const connection = await this.getConnection(signal);
101
+ const q = toQuery(command);
102
+ const cursor = connection.query(new Cursor(q.text, q.values));
103
+ return new DbReader(cursor, this.transaction ? void 0 : connection);
104
+ }
105
+
106
+ public async executeQuery(command: IQuery, signal?: AbortSignal) {
107
+ const connection = await this.getConnection(signal);
108
+ // we need to change parameter styles
109
+ try {
110
+ const q = toQuery(command);
111
+ const result = await connection.query(q.text, q.values);
112
+ return result;
113
+ } finally {
114
+ if (!this.transaction) {
115
+ await connection.end();
116
+ }
117
+ }
118
+ }
119
+
120
+ public ensureDatabase() {
121
+ const create = async () => {
122
+ const defaultDb = "postgres";
123
+ const db = this.config.database;
124
+ this.config.database = defaultDb;
125
+ const connection = await this.getConnection();
126
+ // @ts-expect-error readonly
127
+ this.config = { ... this.config };
128
+ this.config.database = db;
129
+ try {
130
+ const r = await connection.query("SELECT FROM pg_database WHERE datname = $1", [ db ]);
131
+ if(r.rowCount === 1) {
132
+ return;
133
+ }
134
+ await connection.query("CREATE DATABASE " + JSON.stringify(db));
135
+ } finally {
136
+ await connection.end();
137
+ }
138
+ };
139
+ const value = create();
140
+ Object.defineProperty(this, "ensureDatabase", {
141
+ value: () => value,
142
+ });
143
+ return value;
144
+ }
145
+
146
+
147
+ private async getConnection(signal?: AbortSignal) {
148
+
149
+ if (signal?.aborted) {
150
+ throw new Error("Aborted");
151
+ }
152
+
153
+ if (this.transaction) {
154
+ return this.transaction;
155
+ }
156
+ const client = new Client(this.config);
157
+ await client.connect();
158
+ const row = await client.query("SELECT pg_backend_pid() as id");
159
+ const clientId = (row.rows as any).id;
160
+ // there is no support to kill the query running inside
161
+ if (signal) {
162
+ signal.addEventListener("abort", () => this.kill(clientId).catch((error) => console.error(error)));
163
+ }
164
+ return client;
165
+ }
166
+
167
+ private async kill(id) {
168
+ const client = new Client(this.config);
169
+ try {
170
+ await client.connect();
171
+ await client.query("SELECT pg_cancel_backend($1)", [id]);
172
+ } catch (error) {
173
+ console.error(error);
174
+ } finally {
175
+ await client.end();
176
+ }
177
+ }
178
+ }
@@ -0,0 +1,78 @@
1
+ import ExpressionToSql from "../../query/ast/ExpressionToSql.js";
2
+ import { Identifier, InsertStatement, OrderByExpression, ReturnUpdated, SelectStatement, ValuesStatement } from "../../query/ast/Expressions.js";
3
+ import { ITextOrFunctionArray, prepare } from "../../query/ast/IStringTransformer.js";
4
+
5
+ export default class ExpressionToSqlServer extends ExpressionToSql {
6
+
7
+ visitReturnUpdated(e: ReturnUpdated): ITextOrFunctionArray {
8
+ if (!e) {
9
+ return [];
10
+ }
11
+ if (e.fields.length === 0) {
12
+ return [];
13
+ }
14
+ const fields = [];
15
+ let i = 0;
16
+ for (const iterator of e.fields) {
17
+ if (i++) {
18
+ fields.push(",");
19
+ }
20
+ fields.push(prepare `${e.changes}.${this.visit(iterator)}`);
21
+ }
22
+ return prepare ` OUTPUT ${fields}`;
23
+ }
24
+
25
+ visitInsertStatement(e: InsertStatement): ITextOrFunctionArray {
26
+ const returnValues = this.visit(e.returnValues);
27
+ if (e.values instanceof ValuesStatement) {
28
+
29
+ const rows = [];
30
+ for (const iterator of e.values.values) {
31
+ const row = this.visitArray(iterator);
32
+ if (row.length === 0) {
33
+ continue;
34
+ }
35
+ rows.push(prepare `(${ row })`);
36
+ }
37
+
38
+ if (rows.length === 0) {
39
+ return prepare `INSERT INTO ${this.visit(e.table)} ${returnValues}`;
40
+ }
41
+
42
+ return prepare `INSERT INTO ${this.visit(e.table)} (${this.visitArray(e.values.fields)}) ${returnValues} VALUES ${rows}`;
43
+ }
44
+ return prepare `INSERT INTO ${this.visit(e.table)} ${returnValues} ${this.visit(e.values)}`;
45
+
46
+ }
47
+
48
+ visitSelectStatement(e: SelectStatement): ITextOrFunctionArray {
49
+ const fields = this.visitArray(e.fields, ",\n\t\t");
50
+
51
+ const showTop = e.limit && !e.offset;
52
+ const showFetch = e.limit && e.offset;
53
+
54
+ if (e.limit && e.offset) {
55
+ if (!e.orderBy?.length) {
56
+ // lets set default order by... if not set...
57
+ // as sql server needs something to order by...
58
+ e.orderBy = [
59
+ OrderByExpression.create({ target: Identifier.create({ value: "1"}) })
60
+ ];
61
+ }
62
+ }
63
+
64
+ const topValue = Number(e.limit);
65
+ const top = showTop ? prepare ` TOP (${() => topValue}) ` : "";
66
+
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)}` : [];
72
+ const offset = showFetch ? prepare ` OFFSET ${Number(e.offset).toString()} ROWS ` : "";
73
+ const next = showFetch ? prepare ` FETCH NEXT ${Number(e.limit).toString()} ROWS ONLY` : "";
74
+ return prepare `SELECT ${top}
75
+ ${fields}
76
+ FROM ${source}${as}${joins}${where}${orderBy}${offset}${next}`;
77
+ }
78
+ }