@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
package/README.md ADDED
@@ -0,0 +1,247 @@
1
+ # Entity Access
2
+
3
+ Inspired from Entity Framework Core, Entity Access is ORM for JavaScript runtime such as Node, YantraJS.
4
+
5
+
6
+ # Project Status
7
+ 1. Beta - Postgres Driver
8
+ 2. Alpha - Sql Server Driver
9
+
10
+ ## Features
11
+ 1. Unit of Work and Repository Pattern
12
+ 2. Arrow function based query features with automatic joins.
13
+ 3. Automatic Migrations for missing schema - this is done for fast development and deployment.
14
+ 4. Sql functions such as LIKE
15
+ 5. Postgres Driver
16
+ 6. Sql Server Driver
17
+ 7. Automatic parameterization to safeguard sql injection attacks.
18
+
19
+ ## Upcoming Features
20
+ 1. Include
21
+ 2. Projection - Split query mode only, single level only.
22
+ 3. GroupBy
23
+
24
+ ### Unit of Work
25
+
26
+ ```typescript
27
+ const db = new ShoppingContext();
28
+ db.orders.add({
29
+ orderDate: new Date(),
30
+ userID,
31
+ orderItems: [
32
+ db.orderItems.add({
33
+ productID,
34
+ amount
35
+ })
36
+ ]
37
+ });
38
+
39
+ // save all in single transaction
40
+ await db.saveChanges();
41
+ ```
42
+
43
+ ### Arrow function based query features
44
+
45
+ Arrow function based query provides many benefits over tagged template literals or other fluent methods to build queries.
46
+ 1. Arrow functions are easy to visualize.
47
+ 2. You will get intellisense help to complete the query.
48
+ 3. You will get errors if the wrong data types are compared or used in computations.
49
+ 4. Change of property name will automatically refactor as typescript will keep references of where the property is used.
50
+
51
+ Simple Query
52
+ ```typescript
53
+ const db = new ShoppingContext();
54
+
55
+ /// first parameter is set of parameters to pass to the query
56
+ /// the reason it is first, is to help in intellisense.
57
+
58
+ /// second parameter is an arrow function which returns a filter
59
+ const q = db.orders.where({ userID }, (params) => (order) => order.userID === p.userID);
60
+ ```
61
+
62
+ Query with Like operator
63
+ ```typescript
64
+ /// Find all orders for specified customer
65
+ /// Sql functions
66
+ const userName = `akash%`;
67
+ const q = db.orders.where({ userName },
68
+ (params) =>
69
+ (order) =>
70
+ Sql.text.like(
71
+ order.customer.userName,
72
+ p.userName
73
+ )
74
+ );
75
+
76
+ // note that the join will be performed automatically
77
+ ```
78
+
79
+ ### Typed Configurations
80
+ ```typescript
81
+ class ShoppingContext {
82
+ products = this.model.register(Product);
83
+ orders = this.model.register(Order);
84
+ orderItems = this.model.register(OrderItem);
85
+ }
86
+
87
+ @Table("Products")
88
+ class Product {
89
+
90
+ @Column({ key: true, autoGenerated: true })
91
+ productID: number;
92
+
93
+ orderItems: OrderItem[];
94
+ }
95
+
96
+ @Table("OrderItems")
97
+ class OrderItem {
98
+
99
+ @Column({ key: true, autoGenerated: true })
100
+ orderItemID: number;
101
+
102
+ @Column()
103
+ productID: number;
104
+
105
+ @Column()
106
+ orderID: number;
107
+
108
+ /**
109
+ * Following configuration declares everything
110
+ * that will give compilation error if configured wrong.
111
+ */
112
+ @ForeignKey({
113
+ key: (orderItem) => orderItem.productID,
114
+ related: Product,
115
+ relatedProperty:(product) => product.orderItems
116
+ })
117
+ product: Product;
118
+
119
+ }
120
+
121
+ ```
122
+
123
+ ## Query Examples
124
+
125
+ ### Compare operators
126
+
127
+ #### Equality
128
+ Both strict and non strict equality will result in
129
+ simple equality comparison in SQL. Database provider
130
+ may or may not convert them correctly, so we recommend
131
+ using helper functions to convert before comparison.
132
+ ```typescript
133
+ // find all customer from orderID
134
+ const q = db.customers
135
+ // first we will send parameters
136
+ .where({ orderID },
137
+ // second we will write an arrow
138
+ // accepting parameters
139
+ (p) =>
140
+ // this is the arrow which will
141
+ // be converted to SQL
142
+ // you can write very limited set of
143
+ // expressions in this arrow function
144
+ (x) => x.orders.some(
145
+ // This will results in exists or join
146
+ // based on what level of nested
147
+ // foreign key references are available
148
+ (order) => order.orderID === p.orderID )
149
+ )
150
+
151
+ ```
152
+
153
+ Above expression will result in following filter expression
154
+ ```sql
155
+ EXISTS (
156
+ SELECT 1
157
+ FROM Orders as o1
158
+ WHERE x.customerID = o1.orderID
159
+ AND o1.orderID = $1
160
+ )
161
+ ```
162
+
163
+ #### Like
164
+
165
+ To use `LIKE` operator, `Sql.text.like` method must be used
166
+ as it is. Query compiler will only match everything starting
167
+ with `Sql.` and it will inject available operator conversion.
168
+
169
+ You don't have to worry about sql injection as each parameter
170
+ passed will be sent as a sql parameter and not as a literal.
171
+
172
+ ```typescript
173
+ const prefix = `${name}%`;
174
+ db.customers.where({ prefix },
175
+ (p) =>
176
+ (customer) => Sql.text.like(customer.firstName, p.prefix)
177
+ || Sql.text.like(customer.lastName p.prefix)
178
+ )
179
+ ```
180
+
181
+ #### Sql Text Functions
182
+ For other sql text functions you can use `Sql.text.startsWith`, `Sql.text.endsWith`, `Sql.text.left`... etc as shown below.
183
+ ```typescript
184
+ db.customers.where({ prefix },
185
+ (p) =>
186
+ (customer) => Sql.text.startsWith(customer.firstName, p.prefix)
187
+ || Sql.text.startsWith(customer.lastName p.prefix)
188
+ )
189
+ ```
190
+
191
+ #### Sql date functions
192
+ Just as text functions you can also use date functions as shown below.
193
+ ```typescript
194
+ const year = (new Date()).getFullYear();
195
+ // get count of all orders of this year...
196
+ db.orders.where({ year },
197
+ (p) =>
198
+ (order) => Sql.date.yearOf(order.orderDate) === p.year
199
+ )
200
+
201
+ // above example is only for illustrations only, it will not use index.
202
+ // for index usage, please consider window function shown below.
203
+ const start:Date = /* start date */;
204
+ const end:Date = /* start date */;
205
+ // get count of all orders of this year...
206
+ db.orders.where({ start, end },
207
+ (p) =>
208
+ (order) => p.start <= order.orderDate && order.orderDate >= p.end
209
+ )
210
+
211
+ ```
212
+
213
+ ### OrderBy
214
+ ```typescript
215
+ q.orderBy({}, (p) => (x) => x.orderDate)
216
+ .thenBy({}, (p) => (x) => x.customer.firstName)
217
+ ```
218
+
219
+ ### Limit/Offset
220
+ ```typescript
221
+ q = q.orderByDescending({}, (p) => (x) => x.orderDate)
222
+ .thenBy({}, (p) => (x) => x.customer.firstName)
223
+ .limit(50)
224
+ .offset(50);
225
+ ```
226
+
227
+ ### Enumerate
228
+ ```typescript
229
+ for await(const product of q.enumerate()) {
230
+ //
231
+ }
232
+ ```
233
+
234
+ ### First / First or Fail
235
+ ```typescript
236
+ // it will return first product or null
237
+ const firstProduct = await q.first();
238
+
239
+ // it will throw and exception if product was not
240
+ // found
241
+ const firstProduct = await q.firstOrFail();
242
+ ```
243
+
244
+ ### Count
245
+ ```typescript
246
+ const total = await q.count();
247
+ ```
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@entity-access/entity-access",
3
+ "version": "1.0.1",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "node --enable-source-maps ./test.js",
8
+ "test-db": "node --enable-source-maps ./test.js test-db"
9
+ },
10
+ "type": "module",
11
+ "author": "",
12
+ "license": "ISC",
13
+ "dependencies": {
14
+ "@babel/parser": "^7.22.5",
15
+ "@babel/types": "^7.22.5",
16
+ "pg": "^8.11.1",
17
+ "pg-cursor": "^2.10.1",
18
+ "reflect-metadata": "^0.1.13",
19
+ "tcp-port-used": "^1.0.2"
20
+ },
21
+ "devDependencies": {
22
+ "@types/mssql": "^8.1.2",
23
+ "@types/node": "^20.3.2",
24
+ "@types/pg": "^8.10.2",
25
+ "@types/pg-cursor": "^2.7.0",
26
+ "@typescript-eslint/eslint-plugin": "^5.59.9",
27
+ "@typescript-eslint/parser": "^5.59.9",
28
+ "eslint": "^8.42.0",
29
+ "eslint-config-prettier": "^8.8.0",
30
+ "eslint-config-standard": "^17.1.0",
31
+ "eslint-plugin-import": "^2.27.5",
32
+ "eslint-plugin-jsdoc": "^46.2.6",
33
+ "eslint-plugin-node": "^11.1.0",
34
+ "eslint-plugin-prefer-arrow": "^1.2.3",
35
+ "eslint-plugin-promise": "^6.1.1",
36
+ "eslint-plugin-react": "^7.32.2",
37
+ "mssql": "^9.1.1"
38
+ }
39
+ }
@@ -0,0 +1,2 @@
1
+ import { readdir } from "fs/promises";
2
+ import PostgreSqlDriver from "./drivers/postgres/PostgreSqlDriver.js";
@@ -0,0 +1,14 @@
1
+ export default function InstanceCache(target: any, key: string): void {
2
+
3
+ Object.defineProperty(target, key, {
4
+ get() {
5
+ const value = this[key];
6
+ Object.defineProperty(this, key, {
7
+ value,
8
+ });
9
+ return value;
10
+ },
11
+ configurable: true
12
+ });
13
+
14
+ }
@@ -0,0 +1,74 @@
1
+ export default class TimedCache<TKey = any, T = any> {
2
+
3
+ private map: Map<TKey,{ value: any, expire: number, ttl: number, dispose?: (item: any) => any }> = new Map();
4
+
5
+ constructor(private ttl = 15000) {
6
+ // intentional
7
+ setInterval((x) => x.clear(), this.ttl, this);
8
+ }
9
+
10
+ delete(key: any) {
11
+ this.map.delete(key);
12
+ }
13
+
14
+ getOrCreate(key: TKey, factory: (k: TKey) => T, ttl: number = 15000) {
15
+ let item = this.map.get(key);
16
+ if (!item) {
17
+ item = { value: factory(key), ttl, expire: Date.now() + ttl };
18
+ this.map.set(key, item);
19
+ } else {
20
+ item.expire = Date.now() + ttl;
21
+ }
22
+ return item.value as T;
23
+ }
24
+
25
+ getOrCreateAsync(
26
+ key: TKey,
27
+ factory: (k: TKey) => Promise<T>,
28
+ ttl: number = 15000,
29
+ dispose?: ((item: T) => any)
30
+ ): Promise<T> {
31
+ let item = this.map.get(key);
32
+ if (!item) {
33
+ item = { value: factory(key), ttl, expire: Date.now() + ttl, dispose };
34
+ this.map.set(key, item);
35
+ // we need to make sure we do not cache
36
+ // the promise if it fails
37
+ item.value.catch(() => {
38
+ this.map.delete(key);
39
+ });
40
+ if (dispose) {
41
+ item.value.then((r) => {
42
+ item.dispose = () => dispose(r);
43
+ });
44
+ }
45
+ } else {
46
+ item.expire = Date.now() + ttl;
47
+ }
48
+ return item.value;
49
+ }
50
+
51
+ private clear(): void {
52
+ const expired = [];
53
+ const now = Date.now();
54
+ for (const [key, value] of this.map.entries()) {
55
+ if(value.expire < now) {
56
+ expired.push(key);
57
+ // call dispose..
58
+ try {
59
+ const r = value.dispose?.(value.value);
60
+ if (r?.then) {
61
+ r.catch(() => void 0);
62
+ }
63
+ } catch (error) {
64
+ console.error(error);
65
+ }
66
+ }
67
+ }
68
+ for (const key of expired) {
69
+ this.map.delete(key);
70
+ }
71
+ }
72
+
73
+
74
+ }
@@ -0,0 +1,2 @@
1
+ export const modelSymbol = Symbol("model");
2
+ export const contextSymbol = Symbol("context");
@@ -0,0 +1,24 @@
1
+
2
+ export interface IDisposable {
3
+
4
+ end?():any;
5
+ dispose?(): any;
6
+ close?(): any;
7
+
8
+ }
9
+
10
+
11
+ export default async function usingAsync<T extends IDisposable>(d: T, fx: (p:T) => Promise<any>) {
12
+ try {
13
+ const r = fx(d);
14
+ if (r?.then) {
15
+ await r;
16
+ }
17
+ return r;
18
+ } finally {
19
+ const r = d.dispose?.() ?? d.end?.() ?? d.close?.();
20
+ if (r?.then) {
21
+ await r;
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,30 @@
1
+ import { ISql } from "../sql/ISql.js";
2
+
3
+ type IFunction = ( ... a: any[]) => any;
4
+
5
+ type others = Omit<ISql, "in">;
6
+
7
+ type IStringReturn<T extends IFunction> = (... p: Parameters<T>) => string;
8
+
9
+ type transformed<T> = {
10
+ [P in keyof T]: T[P] extends IFunction ? IStringReturn<T[P]> : transformed<T[P]>;
11
+ };
12
+
13
+ export type ISqlHelpers = transformed<ISql>;
14
+
15
+ export const flattenHelpers = (f, name, target = {}) => {
16
+ for (const key in f) {
17
+ if (Object.prototype.hasOwnProperty.call(f, key)) {
18
+ const element = f[key];
19
+ if (typeof element === "function") {
20
+ target[name + "." + key] = element;
21
+ continue;
22
+ }
23
+ if (typeof element !== "object") {
24
+ continue;
25
+ }
26
+ flattenHelpers(element, name + "." + key, target);
27
+ }
28
+ }
29
+ return target;
30
+ };
@@ -0,0 +1,88 @@
1
+ import { IClassOf } from "../decorators/IClassOf.js";
2
+ import ExpressionToSql from "../query/ast/ExpressionToSql.js";
3
+ import { ISqlMethodTransformer, IStringTransformer, ITextOrFunctionArray } from "../query/ast/IStringTransformer.js";
4
+ import { Expression, PlaceholderExpression } from "../query/ast/Expressions.js";
5
+ import SqlLiteral from "../query/ast/SqlLiteral.js";
6
+ import ArrowToExpression from "../query/parser/ArrowToExpression.js";
7
+ import PostgreSqlMethodTransformer from "./postgres/PostgreSqlMethodTransformer.js";
8
+ import EntityType from "../entity-query/EntityType.js";
9
+ import { EntitySource } from "../model/EntitySource.js";
10
+ import { IEntityQuery } from "../model/IFilterWithParameter.js";
11
+ import { SourceExpression } from "../model/SourceExpression.js";
12
+
13
+ export default class QueryCompiler {
14
+
15
+ public static instance = new QueryCompiler();
16
+
17
+ public readonly arrowToExpression: typeof ArrowToExpression;
18
+ public readonly expressionToSql: typeof ExpressionToSql;
19
+
20
+ public readonly quotedLiteral: IStringTransformer;
21
+ public readonly escapeLiteral: IStringTransformer;
22
+
23
+ public readonly sqlMethodTransformer: ISqlMethodTransformer;
24
+
25
+ constructor(
26
+ {
27
+ arrowToExpression = ArrowToExpression,
28
+ expressionToSql = ExpressionToSql,
29
+ quotedLiteral = JSON.stringify,
30
+ escapeLiteral = SqlLiteral.escapeLiteral,
31
+ sqlMethodTransformer = PostgreSqlMethodTransformer
32
+ }: Partial<QueryCompiler> = {}
33
+ ) {
34
+ this.arrowToExpression = arrowToExpression;
35
+ this.expressionToSql = expressionToSql;
36
+ this.escapeLiteral = escapeLiteral;
37
+ this.quotedLiteral = quotedLiteral;
38
+ this.sqlMethodTransformer = sqlMethodTransformer;
39
+ }
40
+
41
+ public execute<P = any, T = any>(parameters: P, fx: (p: P) => (x: T) => any, source?: SourceExpression) {
42
+ const { param, target , body } = this.arrowToExpression.transform(fx);
43
+ const exp = new this.expressionToSql(source, param, target, this);
44
+ const query = exp.visit(body);
45
+ return this.invoke(query, parameters);
46
+ }
47
+
48
+ public compileToExpression(source: SourceExpression, p: any, fx: (p1) => (x) => any) {
49
+ const { param, target , body } = this.arrowToExpression.transform(fx);
50
+ const exp = new this.expressionToSql(source, param, target, this);
51
+ const visited = exp.visit(body);
52
+ return PlaceholderExpression.create({
53
+ expression: () => visited.map((x) => typeof x === "function" ? () => x(p) : x)
54
+ });
55
+ }
56
+
57
+ public compile( source: SourceExpression , fx: (p) => (x) => any) {
58
+ const { param, target , body } = this.arrowToExpression.transform(fx);
59
+ const exp = new this.expressionToSql(source, param, target, this);
60
+ return exp.visit(body);
61
+ }
62
+
63
+ public compileExpression(exp: Expression) {
64
+ const toSql = new this.expressionToSql(null, void 0, void 0, this);
65
+ const query = toSql.visit(exp);
66
+ return this.invoke(query);
67
+ }
68
+
69
+ private invoke(query: ITextOrFunctionArray, p: any = {}) {
70
+ let text = "";
71
+ const values = [];
72
+ for (const iterator of query) {
73
+ if (typeof iterator === "string") {
74
+ text += iterator;
75
+ continue;
76
+ }
77
+ let value = iterator;
78
+ if (typeof iterator === "function") {
79
+ value = iterator(p);
80
+ }
81
+
82
+ values.push(value);
83
+ text += "$" + values.length;
84
+ }
85
+ return { text, values };
86
+ }
87
+
88
+ }
@@ -0,0 +1,83 @@
1
+
2
+ import { prepareAny } from "../../query/ast/IStringTransformer.js";
3
+ import { ISqlHelpers, flattenHelpers } from "../ISqlHelpers.js";
4
+
5
+ export const PostgreSqlHelper: ISqlHelpers = {
6
+ in(a, array) {
7
+ return prepareAny `${a} IN ${array}`;
8
+ },
9
+ date: {
10
+ addDays(d, n) {
11
+ return prepareAny `(${d} + (${n} * interval '1 day'))`;
12
+ },
13
+ addHours(d, n) {
14
+ return prepareAny `(${d} + (${n} * interval '1 hour'))`;
15
+ },
16
+ addMinutes(d, n) {
17
+ return prepareAny `(${d} + (${n} * interval '1 minute'))`;
18
+ },
19
+ addMonths(d, n) {
20
+ return prepareAny `(${d} + (${n} * interval '1 month'))`;
21
+ },
22
+ addSeconds(d, n) {
23
+ return prepareAny `(${d} + (${n} * interval '1 second'))`;
24
+ },
25
+ addYears(d, n) {
26
+ return prepareAny `(${d} + (${n} * interval '1 year'))`;
27
+ },
28
+ dayOf(d) {
29
+ return prepareAny `DATE_PART(${d}, day)`;
30
+ },
31
+ hourOf(d) {
32
+ return prepareAny `DATE_PART(${d}, hour)`;
33
+ },
34
+ minuteOf(d) {
35
+ return prepareAny `DATE_PART(${d}, minute)`;
36
+ },
37
+ monthOf(d) {
38
+ return prepareAny `DATE_PART(${d}, month)`;
39
+ },
40
+ secondOf(d) {
41
+ return prepareAny `DATE_PART(${d}, second)`;
42
+ },
43
+ yearOf(d) {
44
+ return prepareAny `DATE_PART(${d}, year)`;
45
+ },
46
+ },
47
+ text: {
48
+ concat(...p) {
49
+ return prepareAny `CONCAT(${p.join(",")})`;
50
+ },
51
+ endsWith(text, test) {
52
+ return prepareAny `strpos(${text}, ${test}) = length(${text}) - length(${test})`;
53
+ },
54
+ iLike(text, test) {
55
+ return prepareAny `(${text} iLike ${test})`;
56
+ },
57
+ indexOf(text, test) {
58
+ return prepareAny `strpos(${text}, ${test})`;
59
+ },
60
+ left(text, length) {
61
+ return prepareAny `left(${text}, ${length})`;
62
+ },
63
+ like(text, test) {
64
+ return prepareAny `(${text} LIKE ${test})`;
65
+ },
66
+ right(text, length) {
67
+ return prepareAny `right(${text}, ${length})`;
68
+ },
69
+ startsWith(text, test) {
70
+ return prepareAny `starts_with(${text}, ${test})`;
71
+ },
72
+ }
73
+ };
74
+
75
+ const names = flattenHelpers(PostgreSqlHelper, "Sql");
76
+
77
+ export default function PostgreSqlMethodTransformer(callee: string, args: any[]): string {
78
+ const name = names[callee];
79
+ if (!name) {
80
+ return;
81
+ }
82
+ return names[callee]?.(... args);
83
+ }
@@ -0,0 +1,83 @@
1
+
2
+ import { prepareAny } from "../../query/ast/IStringTransformer.js";
3
+ import { ISqlHelpers, flattenHelpers } from "../ISqlHelpers.js";
4
+
5
+ export const SqlServerSqlHelper: ISqlHelpers = {
6
+ in(a, array) {
7
+ return prepareAny `${a} IN ${array}`;
8
+ },
9
+ date: {
10
+ addDays(d, n) {
11
+ return prepareAny `DateAdd(DAY, ${d}, ${n})`;
12
+ },
13
+ addHours(d, n) {
14
+ return prepareAny `DateAdd(HOUR, ${d}, ${n})`;
15
+ },
16
+ addMinutes(d, n) {
17
+ return prepareAny `DateAdd(MINUTE, ${d}, ${n})`;
18
+ },
19
+ addMonths(d, n) {
20
+ return prepareAny `DateAdd(MONTH, ${d}, ${n})`;
21
+ },
22
+ addSeconds(d, n) {
23
+ return prepareAny `DateAdd(SECOND, ${d}, ${n})`;
24
+ },
25
+ addYears(d, n) {
26
+ return prepareAny `DateAdd(YEAR, ${d}, ${n})`;
27
+ },
28
+ dayOf(d) {
29
+ return prepareAny `DATE_PART(day, ${d})`;
30
+ },
31
+ hourOf(d) {
32
+ return prepareAny `DATE_PART(hour, ${d})`;
33
+ },
34
+ minuteOf(d) {
35
+ return prepareAny `DATE_PART(minute, ${d})`;
36
+ },
37
+ monthOf(d) {
38
+ return prepareAny `DATE_PART(month, ${d})`;
39
+ },
40
+ secondOf(d) {
41
+ return prepareAny `DATE_PART(second, ${d})`;
42
+ },
43
+ yearOf(d) {
44
+ return prepareAny `DATE_PART(year, ${d})`;
45
+ },
46
+ },
47
+ text: {
48
+ concat(...p) {
49
+ return prepareAny `CONCAT(${p.join(",")})`;
50
+ },
51
+ endsWith(text, test) {
52
+ return prepareAny `CHARINDEX(${text}, ${test}) = LEN(${text}) - LEN(${test})`;
53
+ },
54
+ iLike(text, test) {
55
+ return prepareAny `(${text} like ${test})`;
56
+ },
57
+ indexOf(text, test) {
58
+ return prepareAny `CHARINDEX(${text}, ${test})`;
59
+ },
60
+ left(text, length) {
61
+ return prepareAny `left(${text}, ${length})`;
62
+ },
63
+ like(text, test) {
64
+ return prepareAny `(${text} LIKE ${test})`;
65
+ },
66
+ right(text, length) {
67
+ return prepareAny `right(${text}, ${length})`;
68
+ },
69
+ startsWith(text, test) {
70
+ return prepareAny `CHARINDEX(${text}, ${test}) = 1`;
71
+ },
72
+ }
73
+ };
74
+
75
+ const names = flattenHelpers(SqlServerSqlHelper, "Sql");
76
+
77
+ export default function SqlServerSqlMethodTransformer(callee: string, args: any[]): string {
78
+ const name = names[callee];
79
+ if (!name) {
80
+ return;
81
+ }
82
+ return names[callee]?.(... args);
83
+ }