pqb 0.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 (122) hide show
  1. package/dist/index.d.ts +3630 -0
  2. package/dist/index.esm.js +4587 -0
  3. package/dist/index.esm.js.map +1 -0
  4. package/dist/index.js +4691 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +59 -0
  7. package/rollup.config.js +35 -0
  8. package/src/adapter.test.ts +10 -0
  9. package/src/adapter.ts +171 -0
  10. package/src/columnSchema/array.ts +21 -0
  11. package/src/columnSchema/boolean.ts +10 -0
  12. package/src/columnSchema/columnType.test.ts +129 -0
  13. package/src/columnSchema/columnType.ts +77 -0
  14. package/src/columnSchema/columnTypes.ts +145 -0
  15. package/src/columnSchema/columnsSchema.test.ts +32 -0
  16. package/src/columnSchema/columnsSchema.ts +100 -0
  17. package/src/columnSchema/commonMethods.ts +130 -0
  18. package/src/columnSchema/dateTime.ts +104 -0
  19. package/src/columnSchema/enum.ts +13 -0
  20. package/src/columnSchema/index.ts +11 -0
  21. package/src/columnSchema/json/array.ts +55 -0
  22. package/src/columnSchema/json/discriminatedUnion.ts +91 -0
  23. package/src/columnSchema/json/enum.ts +29 -0
  24. package/src/columnSchema/json/instanceOf.ts +16 -0
  25. package/src/columnSchema/json/intersection.ts +23 -0
  26. package/src/columnSchema/json/lazy.ts +22 -0
  27. package/src/columnSchema/json/literal.ts +12 -0
  28. package/src/columnSchema/json/map.ts +29 -0
  29. package/src/columnSchema/json/nativeEnum.ts +30 -0
  30. package/src/columnSchema/json/nullable.ts +33 -0
  31. package/src/columnSchema/json/nullish.ts +30 -0
  32. package/src/columnSchema/json/object.ts +206 -0
  33. package/src/columnSchema/json/optional.ts +28 -0
  34. package/src/columnSchema/json/record.ts +40 -0
  35. package/src/columnSchema/json/scalarTypes.ts +117 -0
  36. package/src/columnSchema/json/set.ts +34 -0
  37. package/src/columnSchema/json/tuple.ts +40 -0
  38. package/src/columnSchema/json/typeBase.ts +202 -0
  39. package/src/columnSchema/json/union.ts +16 -0
  40. package/src/columnSchema/json.ts +64 -0
  41. package/src/columnSchema/number.ts +122 -0
  42. package/src/columnSchema/string.ts +222 -0
  43. package/src/columnSchema/utils.ts +27 -0
  44. package/src/common.ts +86 -0
  45. package/src/db.test.ts +67 -0
  46. package/src/db.ts +212 -0
  47. package/src/errors.ts +7 -0
  48. package/src/index.ts +18 -0
  49. package/src/operators.test.ts +608 -0
  50. package/src/operators.ts +177 -0
  51. package/src/query.ts +292 -0
  52. package/src/queryDataUtils.ts +50 -0
  53. package/src/queryMethods/aggregate.test.ts +583 -0
  54. package/src/queryMethods/aggregate.ts +878 -0
  55. package/src/queryMethods/callbacks.test.ts +69 -0
  56. package/src/queryMethods/callbacks.ts +55 -0
  57. package/src/queryMethods/clear.test.ts +64 -0
  58. package/src/queryMethods/clear.ts +58 -0
  59. package/src/queryMethods/columnInfo.test.ts +45 -0
  60. package/src/queryMethods/columnInfo.ts +67 -0
  61. package/src/queryMethods/delete.test.ts +135 -0
  62. package/src/queryMethods/delete.ts +50 -0
  63. package/src/queryMethods/for.test.ts +57 -0
  64. package/src/queryMethods/for.ts +99 -0
  65. package/src/queryMethods/from.test.ts +66 -0
  66. package/src/queryMethods/from.ts +58 -0
  67. package/src/queryMethods/get.test.ts +66 -0
  68. package/src/queryMethods/get.ts +88 -0
  69. package/src/queryMethods/having.test.ts +247 -0
  70. package/src/queryMethods/having.ts +99 -0
  71. package/src/queryMethods/insert.test.ts +555 -0
  72. package/src/queryMethods/insert.ts +453 -0
  73. package/src/queryMethods/join.test.ts +150 -0
  74. package/src/queryMethods/join.ts +508 -0
  75. package/src/queryMethods/json.test.ts +398 -0
  76. package/src/queryMethods/json.ts +259 -0
  77. package/src/queryMethods/log.test.ts +172 -0
  78. package/src/queryMethods/log.ts +123 -0
  79. package/src/queryMethods/queryMethods.test.ts +629 -0
  80. package/src/queryMethods/queryMethods.ts +428 -0
  81. package/src/queryMethods/select.test.ts +479 -0
  82. package/src/queryMethods/select.ts +249 -0
  83. package/src/queryMethods/then.ts +236 -0
  84. package/src/queryMethods/transaction.test.ts +66 -0
  85. package/src/queryMethods/transaction.ts +66 -0
  86. package/src/queryMethods/union.test.ts +59 -0
  87. package/src/queryMethods/union.ts +89 -0
  88. package/src/queryMethods/update.test.ts +417 -0
  89. package/src/queryMethods/update.ts +350 -0
  90. package/src/queryMethods/upsert.test.ts +56 -0
  91. package/src/queryMethods/upsert.ts +43 -0
  92. package/src/queryMethods/where.test.ts +1594 -0
  93. package/src/queryMethods/where.ts +450 -0
  94. package/src/queryMethods/window.test.ts +66 -0
  95. package/src/queryMethods/window.ts +108 -0
  96. package/src/queryMethods/with.test.ts +191 -0
  97. package/src/queryMethods/with.ts +92 -0
  98. package/src/quote.ts +36 -0
  99. package/src/relations.ts +194 -0
  100. package/src/sql/aggregate.ts +80 -0
  101. package/src/sql/columnInfo.ts +22 -0
  102. package/src/sql/common.ts +42 -0
  103. package/src/sql/delete.ts +41 -0
  104. package/src/sql/distinct.ts +19 -0
  105. package/src/sql/fromAndAs.ts +51 -0
  106. package/src/sql/having.ts +140 -0
  107. package/src/sql/index.ts +2 -0
  108. package/src/sql/insert.ts +102 -0
  109. package/src/sql/join.ts +242 -0
  110. package/src/sql/orderBy.ts +41 -0
  111. package/src/sql/select.ts +153 -0
  112. package/src/sql/toSql.ts +153 -0
  113. package/src/sql/truncate.ts +13 -0
  114. package/src/sql/types.ts +355 -0
  115. package/src/sql/update.ts +62 -0
  116. package/src/sql/where.ts +314 -0
  117. package/src/sql/window.ts +38 -0
  118. package/src/sql/with.ts +32 -0
  119. package/src/test-utils.ts +172 -0
  120. package/src/utils.ts +140 -0
  121. package/tsconfig.build.json +6 -0
  122. package/tsconfig.json +8 -0
@@ -0,0 +1,191 @@
1
+ import { WithOptions } from '../sql';
2
+ import { expectQueryNotMutated, expectSql, User } from '../test-utils';
3
+ import { columnTypes, NumberColumn } from '../columnSchema';
4
+ import { raw } from '../common';
5
+
6
+ describe('with', () => {
7
+ const options: (
8
+ | undefined
9
+ | (Omit<WithOptions, 'columns'> & { columns?: boolean | string[] })
10
+ )[] = [
11
+ undefined,
12
+ { columns: true },
13
+ { columns: ['custom', 'columns', 'list'] },
14
+ { recursive: true },
15
+ { materialized: true },
16
+ { notMaterialized: true },
17
+ ];
18
+
19
+ const getExpectedWithSql = (
20
+ sql: string,
21
+ columns: string[],
22
+ opts: typeof options[number],
23
+ ) => {
24
+ return `
25
+ WITH${opts?.recursive ? ' RECURSIVE' : ''} "withAlias"${
26
+ opts?.columns
27
+ ? `(${(opts.columns === true ? columns : opts.columns)
28
+ .map((column) => `"${column}"`)
29
+ .join(', ')})`
30
+ : ''
31
+ } AS ${
32
+ opts?.materialized
33
+ ? 'MATERIALIZED '
34
+ : opts?.notMaterialized
35
+ ? 'NOT MATERIALIZED '
36
+ : ''
37
+ } (
38
+ ${sql}
39
+ )
40
+ SELECT *
41
+ FROM "withAlias"
42
+ `;
43
+ };
44
+
45
+ const columnShape = { one: columnTypes.integer(), two: columnTypes.text() };
46
+
47
+ it('accepts raw parameter preceded by columns shape', () => {
48
+ const q = User.all();
49
+
50
+ options.forEach((options) => {
51
+ const args: Parameters<typeof q.with> = [
52
+ 'withAlias',
53
+ columnShape,
54
+ raw(`(VALUES (1, 'two')) t(one, two)`),
55
+ ];
56
+
57
+ if (options) {
58
+ (args as unknown[]).splice(1, 0, options);
59
+ }
60
+
61
+ expectSql(
62
+ q
63
+ .with(...args)
64
+ .from('withAlias')
65
+ .toSql(),
66
+ getExpectedWithSql(
67
+ `(VALUES (1, 'two')) t(one, two)`,
68
+ ['one', 'two'],
69
+ options,
70
+ ),
71
+ );
72
+ });
73
+
74
+ expectQueryNotMutated(q);
75
+ });
76
+
77
+ it('accepts query', () => {
78
+ const q = User.all();
79
+
80
+ options.forEach((options) => {
81
+ const args: Parameters<typeof q.with> = ['withAlias', User.all()];
82
+
83
+ if (options) {
84
+ (args as unknown[]).splice(1, 0, options);
85
+ }
86
+
87
+ expectSql(
88
+ q
89
+ .with(...args)
90
+ .from('withAlias')
91
+ .toSql(),
92
+ getExpectedWithSql(
93
+ 'SELECT * FROM "user"',
94
+ Object.keys(User.shape),
95
+ options,
96
+ ),
97
+ );
98
+ });
99
+
100
+ expectQueryNotMutated(q);
101
+ });
102
+
103
+ it('accepts callback for query builder', () => {
104
+ const q = User.all();
105
+
106
+ options.forEach((options) => {
107
+ const args: Parameters<typeof q.with> = [
108
+ 'withAlias',
109
+ (qb) => qb.select({ one: raw<NumberColumn>('1') }),
110
+ ];
111
+
112
+ if (options) {
113
+ (args as unknown[]).splice(1, 0, options);
114
+ }
115
+
116
+ expectSql(
117
+ q
118
+ .with(...args)
119
+ .from('withAlias')
120
+ .toSql(),
121
+ getExpectedWithSql(
122
+ `SELECT 1 AS "one"`,
123
+ // columns: true will produce empty columns list because there is no way to get it from query builder result
124
+ [],
125
+ options,
126
+ ),
127
+ );
128
+ });
129
+
130
+ expectQueryNotMutated(q);
131
+ });
132
+
133
+ it('can be referenced in .join', () => {
134
+ const q = User.all();
135
+
136
+ const received1 = q
137
+ .with('withAlias', User.all())
138
+ .join('withAlias', 'id', '=', 'user.id')
139
+ .select('withAlias.id')
140
+ .toSql();
141
+
142
+ const received2 = q
143
+ .with('withAlias', User.all())
144
+ .join('withAlias', 'withAlias.id', '=', 'user.id')
145
+ .select('withAlias.id')
146
+ .toSql();
147
+
148
+ const received3 = q
149
+ .with('withAlias', User.all())
150
+ .join('withAlias', raw(`"withAlias"."id" = "user"."id"`))
151
+ .select('withAlias.id')
152
+ .toSql();
153
+
154
+ const received4 = q
155
+ .with('withAlias', User.all())
156
+ .join('withAlias', (qb) => qb.on('withAlias.id', '=', 'user.id'))
157
+ .select('withAlias.id')
158
+ .toSql();
159
+
160
+ const expected = `
161
+ WITH "withAlias" AS (
162
+ SELECT * FROM "user"
163
+ )
164
+ SELECT "withAlias"."id" FROM "user"
165
+ JOIN "withAlias" ON "withAlias"."id" = "user"."id"
166
+ `;
167
+
168
+ expectSql(received1, expected);
169
+ expectSql(received2, expected);
170
+ expectSql(received3, expected);
171
+ expectSql(received4, expected);
172
+
173
+ expectQueryNotMutated(q);
174
+ });
175
+
176
+ it('can be used in .from', () => {
177
+ const q = User.all();
178
+
179
+ expectSql(
180
+ q.with('withAlias', User.all()).from('withAlias').select('id').toSql(),
181
+ `
182
+ WITH "withAlias" AS (
183
+ SELECT * FROM "user"
184
+ )
185
+ SELECT "withAlias"."id" FROM "withAlias"
186
+ `,
187
+ );
188
+
189
+ expectQueryNotMutated(q);
190
+ });
191
+ });
@@ -0,0 +1,92 @@
1
+ import { WithOptions } from '../sql';
2
+ import { ColumnShapeOutput, ColumnsShape, ColumnTypes } from '../columnSchema';
3
+ import { EMPTY_OBJECT, isRaw, RawExpression } from '../common';
4
+ import { AddQueryWith, Query } from '../query';
5
+ import { Db } from '../db';
6
+ import { pushQueryValue, setQueryObjectValue } from '../queryDataUtils';
7
+
8
+ type WithArgsOptions = Omit<WithOptions, 'columns'> & {
9
+ columns?: boolean | string[];
10
+ };
11
+
12
+ type WithArgs =
13
+ | [string, ColumnsShape, RawExpression]
14
+ | [string, WithArgsOptions, ColumnsShape, RawExpression]
15
+ | [string, Query | ((qb: Db) => Query)]
16
+ | [string, WithArgsOptions, Query | ((qb: Db) => Query)];
17
+
18
+ type WithShape<Args extends WithArgs> = Args[1] extends Query
19
+ ? Args[1]['result']
20
+ : Args[1] extends (qb: Db) => Query
21
+ ? ReturnType<Args[1]>['result']
22
+ : Args[2] extends Query
23
+ ? Args[2]['result']
24
+ : Args[2] extends (qb: Db) => Query
25
+ ? ReturnType<Args[2]>['result']
26
+ : Args[1] extends ColumnsShape
27
+ ? Args[1]
28
+ : Args[2] extends ColumnsShape
29
+ ? Args[2]
30
+ : Args[2] extends (t: ColumnTypes) => ColumnsShape
31
+ ? ReturnType<Args[2]>
32
+ : never;
33
+
34
+ type WithResult<
35
+ T extends Query,
36
+ Args extends WithArgs,
37
+ Shape extends ColumnsShape,
38
+ > = AddQueryWith<
39
+ T,
40
+ {
41
+ table: Args[0];
42
+ shape: Shape;
43
+ type: ColumnShapeOutput<Shape>;
44
+ }
45
+ >;
46
+
47
+ export class With {
48
+ with<
49
+ T extends Query,
50
+ Args extends WithArgs,
51
+ Shape extends ColumnsShape = WithShape<Args>,
52
+ >(this: T, ...args: Args): WithResult<T, Args, Shape> {
53
+ return this.clone()._with<T, Args, Shape>(...args);
54
+ }
55
+
56
+ _with<
57
+ T extends Query,
58
+ Args extends WithArgs,
59
+ Shape extends ColumnsShape = WithShape<Args>,
60
+ >(this: T, ...args: Args): WithResult<T, Args, Shape> {
61
+ let options =
62
+ (args.length === 3 && !isRaw(args[2])) || args.length === 4
63
+ ? (args[1] as WithArgsOptions | WithOptions)
64
+ : undefined;
65
+
66
+ const last = args[args.length - 1] as
67
+ | Query
68
+ | ((qb: Db) => Query)
69
+ | RawExpression;
70
+
71
+ const query = typeof last === 'function' ? last(this.queryBuilder) : last;
72
+
73
+ const shape =
74
+ args.length === 4 ? (args[2] as ColumnsShape) : (query as Query).shape;
75
+
76
+ if (options?.columns === true) {
77
+ options = {
78
+ ...options,
79
+ columns: Object.keys(shape),
80
+ };
81
+ }
82
+
83
+ pushQueryValue(this, 'with', [args[0], options || EMPTY_OBJECT, query]);
84
+
85
+ return setQueryObjectValue(
86
+ this,
87
+ 'withShapes',
88
+ args[0],
89
+ shape,
90
+ ) as unknown as WithResult<T, Args, Shape>;
91
+ }
92
+ }
package/src/quote.ts ADDED
@@ -0,0 +1,36 @@
1
+ const singleQuoteRegex = /'/g;
2
+ const doubleQuoteRegex = /"/g;
3
+
4
+ // eslint-disable-next-line
5
+ type Value = any
6
+
7
+ const quoteValue = (value: Value): string => {
8
+ const type = typeof value;
9
+ if (type === 'number') return String(value);
10
+ else if (type === 'string')
11
+ return `"${(value as string)
12
+ .replace(doubleQuoteRegex, '\\"')
13
+ .replace(singleQuoteRegex, "''")}"`;
14
+ else if (type === 'boolean') return value ? 'true' : 'false';
15
+ else if (value instanceof Date) return `"${value.toISOString()}"`;
16
+ else if (Array.isArray(value)) return quoteArray(value);
17
+ else if (type === null || type === undefined) return 'NULL';
18
+ else
19
+ return `"${JSON.stringify(value)
20
+ .replace(doubleQuoteRegex, '\\"')
21
+ .replace(singleQuoteRegex, "''")}"`;
22
+ };
23
+
24
+ const quoteArray = (array: Value[]) => `'{${array.map(quoteValue).join(',')}}'`;
25
+
26
+ export const quote = (value: Value): string => {
27
+ const type = typeof value;
28
+ if (type === 'number') return `${value}`;
29
+ else if (type === 'string')
30
+ return `'${(value as string).replace(singleQuoteRegex, "''")}'`;
31
+ else if (type === 'boolean') return value ? 'true' : 'false';
32
+ else if (value instanceof Date) return `'${value.toISOString()}'`;
33
+ else if (Array.isArray(value)) return quoteArray(value);
34
+ else if (value === null || value === undefined) return 'NULL';
35
+ else return `'${JSON.stringify(value).replace(singleQuoteRegex, "''")}'`;
36
+ };
@@ -0,0 +1,194 @@
1
+ import { defaultsKey, Query, QueryBase, QueryWithTable } from './query';
2
+ import { WhereArg } from './queryMethods/where';
3
+ import { MaybeArray } from './utils';
4
+ import { UpdateData } from './queryMethods/update';
5
+
6
+ export type NestedInsertOneItem = {
7
+ create?: Record<string, unknown>;
8
+ connect?: WhereArg<QueryBase>;
9
+ };
10
+
11
+ export type NestedInsertManyItems = {
12
+ create?: Record<string, unknown>[];
13
+ connect?: WhereArg<QueryBase>[];
14
+ connectOrCreate?: {
15
+ where: WhereArg<QueryBase>;
16
+ create: Record<string, unknown>;
17
+ }[];
18
+ };
19
+
20
+ export type NestedInsertItem = NestedInsertOneItem | NestedInsertManyItems;
21
+
22
+ export type BelongsToNestedInsert = (
23
+ query: Query,
24
+ relationData: NestedInsertOneItem[],
25
+ ) => Promise<Record<string, unknown>[]>;
26
+
27
+ export type HasOneNestedInsert = (
28
+ query: Query,
29
+ data: [
30
+ selfData: Record<string, unknown>,
31
+ relationData: NestedInsertOneItem,
32
+ ][],
33
+ ) => Promise<void>;
34
+
35
+ export type HasManyNestedInsert = (
36
+ query: Query,
37
+ data: [
38
+ selfData: Record<string, unknown>,
39
+ relationData: NestedInsertManyItems,
40
+ ][],
41
+ ) => Promise<void>;
42
+
43
+ export type NestedUpdateOneItem = {
44
+ disconnect?: boolean;
45
+ set?: WhereArg<QueryBase>;
46
+ delete?: boolean;
47
+ update?: UpdateData<Query>;
48
+ upsert?: {
49
+ update: UpdateData<Query>;
50
+ create: Record<string, unknown>;
51
+ };
52
+ create: Record<string, unknown>;
53
+ };
54
+
55
+ export type NestedUpdateManyItems = {
56
+ disconnect?: MaybeArray<WhereArg<QueryBase>>;
57
+ set?: MaybeArray<WhereArg<QueryBase>>;
58
+ delete?: MaybeArray<WhereArg<QueryBase>>;
59
+ update?: {
60
+ where: MaybeArray<WhereArg<QueryBase>>;
61
+ data: UpdateData<Query>;
62
+ };
63
+ create: Record<string, unknown>[];
64
+ };
65
+
66
+ export type NestedUpdateItem = NestedUpdateOneItem | NestedUpdateManyItems;
67
+
68
+ export type BelongsToNestedUpdate = (
69
+ q: Query,
70
+ update: Record<string, unknown>,
71
+ params: NestedUpdateOneItem,
72
+ state: {
73
+ updateLater?: Record<string, unknown>;
74
+ updateLaterPromises?: Promise<void>[];
75
+ },
76
+ ) => boolean;
77
+
78
+ export type HasOneNestedUpdate = (
79
+ query: Query,
80
+ data: Record<string, unknown>[],
81
+ relationData: NestedUpdateOneItem,
82
+ ) => Promise<void>;
83
+
84
+ export type HasManyNestedUpdate = (
85
+ query: Query,
86
+ data: Record<string, unknown>[],
87
+ relationData: NestedUpdateManyItems,
88
+ ) => Promise<void>;
89
+
90
+ export type BaseRelation = {
91
+ type: string;
92
+ key: string;
93
+ model: QueryWithTable;
94
+ joinQuery: Query;
95
+ nestedCreateQuery: Query;
96
+ nestedInsert?:
97
+ | BelongsToNestedInsert
98
+ | HasOneNestedInsert
99
+ | HasManyNestedInsert;
100
+ nestedUpdate?:
101
+ | BelongsToNestedUpdate
102
+ | HasOneNestedUpdate
103
+ | HasManyNestedUpdate;
104
+ primaryKey: string;
105
+ options: {
106
+ scope?(q: QueryWithTable): QueryWithTable;
107
+ required?: boolean;
108
+ };
109
+ };
110
+
111
+ export interface BelongsToRelation extends BaseRelation {
112
+ type: 'belongsTo';
113
+ returns: 'one';
114
+ options: BaseRelation['options'] & {
115
+ primaryKey: string;
116
+ foreignKey: string;
117
+ };
118
+ }
119
+
120
+ export interface HasOneRelation extends BaseRelation {
121
+ type: 'hasOne';
122
+ returns: 'one';
123
+ options: BaseRelation['options'] &
124
+ (
125
+ | {
126
+ primaryKey: string;
127
+ foreignKey: string;
128
+ }
129
+ | {
130
+ through: string;
131
+ source: string;
132
+ }
133
+ );
134
+ }
135
+
136
+ export interface HasManyRelation extends BaseRelation {
137
+ type: 'hasMany';
138
+ returns: 'many';
139
+ options: BaseRelation['options'] &
140
+ (
141
+ | {
142
+ primaryKey: string;
143
+ foreignKey: string;
144
+ }
145
+ | {
146
+ through: string;
147
+ source: string;
148
+ }
149
+ );
150
+ }
151
+
152
+ export interface HasAndBelongsToManyRelation extends BaseRelation {
153
+ type: 'hasAndBelongsToMany';
154
+ returns: 'many';
155
+ options: BaseRelation['options'] & {
156
+ primaryKey: string;
157
+ foreignKey: string;
158
+ associationPrimaryKey: string;
159
+ associationForeignKey: string;
160
+ joinTable: string;
161
+ };
162
+ }
163
+
164
+ export type Relation =
165
+ | BelongsToRelation
166
+ | HasOneRelation
167
+ | HasManyRelation
168
+ | HasAndBelongsToManyRelation;
169
+
170
+ export type RelationsBase = Record<never, Relation>;
171
+
172
+ export type relationQueryKey = typeof relationQueryKey;
173
+ export const relationQueryKey = Symbol('relationQuery');
174
+
175
+ export type isRequiredRelationKey = typeof isRequiredRelationKey;
176
+ export const isRequiredRelationKey = Symbol('isRequiredRelation');
177
+
178
+ export type RelationQueryBase = Query & {
179
+ [relationQueryKey]: string;
180
+ [isRequiredRelationKey]: boolean;
181
+ };
182
+
183
+ export type RelationQuery<
184
+ RelationName extends PropertyKey = string,
185
+ Params extends Record<string, unknown> = never,
186
+ Populate extends string = never,
187
+ T extends Query = Query,
188
+ Required extends boolean = boolean,
189
+ Q extends RelationQueryBase = Omit<T, 'tableAlias'> & {
190
+ tableAlias: RelationName extends string ? RelationName : never;
191
+ [isRequiredRelationKey]: Required;
192
+ [relationQueryKey]: string;
193
+ },
194
+ > = ((params: Params) => Q & { [defaultsKey]: Pick<T['type'], Populate> }) & Q;
@@ -0,0 +1,80 @@
1
+ import { AggregateItem } from './types';
2
+ import { addValue, expressionToSql, q } from './common';
3
+ import { EMPTY_OBJECT, Expression, getRaw, isRaw } from '../common';
4
+ import { windowToSql } from './window';
5
+ import { pushOrderBySql } from './orderBy';
6
+ import { whereToSql } from './where';
7
+ import { Query } from '../query';
8
+
9
+ export const aggregateToSql = (
10
+ model: Pick<
11
+ Query,
12
+ 'whereQueryBuilder' | 'onQueryBuilder' | 'as' | 'shape' | 'relations'
13
+ >,
14
+ values: unknown[],
15
+ item: AggregateItem,
16
+ quotedAs?: string,
17
+ ) => {
18
+ const sql: string[] = [`${item.function}(`];
19
+
20
+ const options = item.options || EMPTY_OBJECT;
21
+
22
+ if (options.distinct && !options.withinGroup) sql.push('DISTINCT ');
23
+
24
+ if (typeof item.arg === 'object') {
25
+ if (Array.isArray(item.arg)) {
26
+ sql.push(
27
+ `${expressionToSql(item.arg[0], values, quotedAs)}, ${addValue(
28
+ values,
29
+ item.arg[1],
30
+ )}`,
31
+ );
32
+ } else if (isRaw(item.arg)) {
33
+ sql.push(getRaw(item.arg, values));
34
+ } else {
35
+ const args: string[] = [];
36
+ for (const key in item.arg) {
37
+ args.push(
38
+ // ::text is needed to bypass "could not determine data type of parameter" postgres error
39
+ `${addValue(values, key)}::text, ${expressionToSql(
40
+ item.arg[key as keyof typeof item.arg] as unknown as Expression,
41
+ values,
42
+ quotedAs,
43
+ )}`,
44
+ );
45
+ }
46
+ sql.push(args.join(', '));
47
+ }
48
+ } else if (item.arg) {
49
+ sql.push(expressionToSql(item.arg, values, quotedAs));
50
+ }
51
+
52
+ if (options.withinGroup) sql.push(') WITHIN GROUP (');
53
+ else if (options.order) sql.push(' ');
54
+
55
+ if (options.order) pushOrderBySql(sql, values, quotedAs, options.order);
56
+
57
+ sql.push(')');
58
+
59
+ if (options.filter || options.filterOr) {
60
+ const whereSql = whereToSql(
61
+ model,
62
+ {
63
+ and: options.filter ? [options.filter] : undefined,
64
+ or: options.filterOr?.map((item) => [item]),
65
+ },
66
+ values,
67
+ quotedAs,
68
+ );
69
+
70
+ sql.push(` FILTER (WHERE ${whereSql})`);
71
+ }
72
+
73
+ if (options.over) {
74
+ sql.push(` OVER ${windowToSql(options.over, values, quotedAs)}`);
75
+ }
76
+
77
+ if (options.as) sql.push(` AS ${q(options.as)}`);
78
+
79
+ return sql.join('');
80
+ };
@@ -0,0 +1,22 @@
1
+ import { ColumnInfoQueryData } from './types';
2
+ import { addValue } from './common';
3
+
4
+ export const pushColumnInfoSql = (
5
+ sql: string[],
6
+ values: unknown[],
7
+ table: string,
8
+ query: ColumnInfoQueryData,
9
+ ) => {
10
+ sql.push(
11
+ `SELECT * FROM information_schema.columns WHERE table_name = ${addValue(
12
+ values,
13
+ table,
14
+ )} AND table_catalog = current_database() AND table_schema = ${
15
+ query.schema || 'current_schema()'
16
+ }`,
17
+ );
18
+
19
+ if (query.column) {
20
+ sql.push(`AND column_name = ${addValue(values, query.column)}`);
21
+ }
22
+ };
@@ -0,0 +1,42 @@
1
+ // quote table or column
2
+ import { Query } from '../query';
3
+ import { Expression, getRaw, isRaw } from '../common';
4
+
5
+ export const q = (sql: string) => `"${sql}"`;
6
+
7
+ // quote column with table or as
8
+ export const qc = (column: string, quotedAs?: string) =>
9
+ quotedAs ? `${quotedAs}.${q(column)}` : column;
10
+
11
+ export const quoteFullColumn = (fullColumn: string, quotedAs?: string) => {
12
+ const index = fullColumn.indexOf('.');
13
+ if (index !== -1) {
14
+ return `${q(fullColumn.slice(0, index))}.${q(fullColumn.slice(index + 1))}`;
15
+ } else if (quotedAs) {
16
+ return `${quotedAs}.${q(fullColumn)}`;
17
+ } else {
18
+ return q(fullColumn);
19
+ }
20
+ };
21
+
22
+ export const expressionToSql = <T extends Query>(
23
+ expr: Expression<T>,
24
+ values: unknown[],
25
+ quotedAs?: string,
26
+ ) => {
27
+ return typeof expr === 'object' && isRaw(expr)
28
+ ? getRaw(expr, values)
29
+ : quoteFullColumn(expr as string, quotedAs);
30
+ };
31
+
32
+ export const quoteSchemaAndTable = (
33
+ schema: string | undefined,
34
+ table: string,
35
+ ) => {
36
+ return schema ? `${q(schema)}.${q(table)}` : q(table);
37
+ };
38
+
39
+ export const addValue = (values: unknown[], value: unknown) => {
40
+ values.push(value);
41
+ return `$${values.length}`;
42
+ };
@@ -0,0 +1,41 @@
1
+ import { Query } from '../query';
2
+ import { DeleteQueryData } from './types';
3
+ import { pushWhereSql } from './where';
4
+ import { pushReturningSql } from './insert';
5
+ import { processJoinItem } from './join';
6
+
7
+ export const pushDeleteSql = (
8
+ sql: string[],
9
+ values: unknown[],
10
+ model: Query,
11
+ query: DeleteQueryData,
12
+ quotedAs: string,
13
+ ) => {
14
+ sql.push(`DELETE FROM ${quotedAs}`);
15
+
16
+ let conditions: string | undefined;
17
+ if (query.join?.length) {
18
+ const items = query.join.map((item) =>
19
+ processJoinItem(model, query, values, item.args, quotedAs),
20
+ );
21
+
22
+ sql.push(`USING ${items.map((item) => item.target).join(', ')}`);
23
+
24
+ conditions = items
25
+ .map((item) => item.conditions)
26
+ .filter(Boolean)
27
+ .join(' AND ');
28
+ }
29
+
30
+ pushWhereSql(sql, model, query, values, quotedAs);
31
+
32
+ if (conditions?.length) {
33
+ if (query.and?.length || query.or?.length) {
34
+ sql.push('AND', conditions);
35
+ } else {
36
+ sql.push('WHERE', conditions);
37
+ }
38
+ }
39
+
40
+ pushReturningSql(sql, model, query, values, quotedAs);
41
+ };