pg-query-sdk 1.0.2 → 1.0.4
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.
- package/README.md +15 -17
- package/dist/cjs/builders/QueryBuilder.d.ts +1 -1
- package/dist/cjs/core/ParamContext.d.ts +4 -1
- package/dist/cjs/core/ParamContext.js +6 -1
- package/dist/cjs/core/QueryExecutor.d.ts +1 -1
- package/dist/cjs/core/QueryExecutor.js +3 -2
- package/dist/cjs/query/ConditionBuilder.d.ts +4 -31
- package/dist/cjs/query/ConditionBuilder.js +88 -55
- package/dist/cjs/query/QueryBuilder.d.ts +5 -3
- package/dist/cjs/query/QueryBuilder.js +5 -1
- package/dist/esm/builders/QueryBuilder.d.ts +1 -1
- package/dist/esm/core/ParamContext.d.ts +4 -1
- package/dist/esm/core/ParamContext.js +6 -1
- package/dist/esm/core/QueryExecutor.d.ts +1 -1
- package/dist/esm/core/QueryExecutor.js +3 -2
- package/dist/esm/query/ConditionBuilder.d.ts +4 -31
- package/dist/esm/query/ConditionBuilder.js +85 -55
- package/dist/esm/query/QueryBuilder.d.ts +5 -3
- package/dist/esm/query/QueryBuilder.js +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,8 +42,6 @@ const db = new Database({
|
|
|
42
42
|
});
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
---
|
|
46
|
-
|
|
47
45
|
## 🛠 Core Functionalities
|
|
48
46
|
|
|
49
47
|
### 1️⃣ QueryBuilder: Fluent SQL `SELECT` Query Construction
|
|
@@ -79,27 +77,27 @@ The `ConditionBuilder` facilitates the creation of complex conditional logic wit
|
|
|
79
77
|
import {Database} from 'pg-query-sdk';
|
|
80
78
|
|
|
81
79
|
const db = new Database({
|
|
82
|
-
|
|
80
|
+
connectionString: 'postgres://user:pass@localhost:5432/your_database',
|
|
83
81
|
});
|
|
84
82
|
|
|
85
83
|
async function complexWhereExample() {
|
|
86
|
-
const products = await db.table('products')
|
|
87
|
-
.select(['name', 'price'])
|
|
88
|
-
.where(conditions => {
|
|
89
|
-
conditions
|
|
90
|
-
.where({ category: 'electronics' })
|
|
91
|
-
.orGroup(group => {
|
|
92
|
-
group
|
|
93
|
-
.where({ price: { op: '<', value: 100 } })
|
|
94
|
-
.where({ stock: { op: '>', value: 0 } });
|
|
95
|
-
});
|
|
96
|
-
})
|
|
97
|
-
.execute();
|
|
98
84
|
|
|
99
|
-
|
|
85
|
+
const products = await db
|
|
86
|
+
.table('products')
|
|
87
|
+
.select(['name', 'price'])
|
|
88
|
+
.where(conditions => {
|
|
89
|
+
conditions
|
|
90
|
+
.where({category: 'electronics'})
|
|
91
|
+
.orGroup(group => {
|
|
92
|
+
group.where({price: {op: '<', value: 100}})
|
|
93
|
+
.where({stock: {op: '>', value: 0}})
|
|
94
|
+
})
|
|
95
|
+
}).execute()
|
|
96
|
+
|
|
97
|
+
console.log('Complex WHERE Products:', products)
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
complexWhereExample()
|
|
100
|
+
complexWhereExample()
|
|
103
101
|
```
|
|
104
102
|
|
|
105
103
|
### 3️⃣ QueryExecutor: Direct Query Execution
|
|
@@ -131,7 +131,7 @@ export default class QueryBuilder<T = any> {
|
|
|
131
131
|
*/
|
|
132
132
|
build(): {
|
|
133
133
|
query: string;
|
|
134
|
-
params: any[];
|
|
134
|
+
params: readonly (string | number | boolean | any[] | Date | Buffer<ArrayBufferLike> | null)[];
|
|
135
135
|
};
|
|
136
136
|
/**
|
|
137
137
|
* Executes the built SQL query and returns the results.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Dialect } from "../dialects/Dialect";
|
|
2
|
+
type SQLParam = string | number | boolean | Date | null | Buffer | any[];
|
|
2
3
|
/**
|
|
3
4
|
* Manages parameters for SQL queries, ensuring proper dialect-specific placeholders.
|
|
4
5
|
*/
|
|
@@ -20,5 +21,7 @@ export default class ParamContext {
|
|
|
20
21
|
* Retrieves the list of accumulated parameters.
|
|
21
22
|
* @returns An array of parameters.
|
|
22
23
|
*/
|
|
23
|
-
getParams():
|
|
24
|
+
getParams(): readonly SQLParam[];
|
|
25
|
+
clone(): ParamContext;
|
|
24
26
|
}
|
|
27
|
+
export {};
|
|
@@ -26,7 +26,12 @@ class ParamContext {
|
|
|
26
26
|
* @returns An array of parameters.
|
|
27
27
|
*/
|
|
28
28
|
getParams() {
|
|
29
|
-
return this.params;
|
|
29
|
+
return Object.freeze([...this.params]);
|
|
30
|
+
}
|
|
31
|
+
clone() {
|
|
32
|
+
const ctx = new ParamContext(this.dialect);
|
|
33
|
+
ctx.params = [...this.params];
|
|
34
|
+
return ctx;
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
exports.default = ParamContext;
|
|
@@ -21,7 +21,7 @@ export default class QueryExecutor {
|
|
|
21
21
|
* @param cacheTTL - Time-to-live for caching (not currently used in this implementation).
|
|
22
22
|
* @returns A Promise that resolves to the QueryResult.
|
|
23
23
|
*/
|
|
24
|
-
execute(query: string, params
|
|
24
|
+
execute(query: string, params?: readonly any[], cacheTTL?: number): Promise<QueryResult>;
|
|
25
25
|
/**
|
|
26
26
|
* Returns the underlying PostgreSQL Pool instance.
|
|
27
27
|
* @returns The Pool instance or undefined if not initialized with a connection string.
|
|
@@ -31,11 +31,12 @@ class QueryExecutor {
|
|
|
31
31
|
* @returns A Promise that resolves to the QueryResult.
|
|
32
32
|
*/
|
|
33
33
|
async execute(query, params = [], cacheTTL) {
|
|
34
|
+
const normalized = [...params];
|
|
34
35
|
if (this.client) {
|
|
35
|
-
return this.client.query(query,
|
|
36
|
+
return this.client.query(query, normalized);
|
|
36
37
|
}
|
|
37
38
|
if (this.pool) {
|
|
38
|
-
return this.pool.query(query,
|
|
39
|
+
return this.pool.query(query, normalized);
|
|
39
40
|
}
|
|
40
41
|
throw new Error('Executor not initialized');
|
|
41
42
|
}
|
|
@@ -5,40 +5,13 @@ import ParamContext from '../core/ParamContext';
|
|
|
5
5
|
export default class ConditionBuilder {
|
|
6
6
|
private ctx;
|
|
7
7
|
private parts;
|
|
8
|
-
/**
|
|
9
|
-
* Creates an instance of ConditionBuilder.
|
|
10
|
-
* @param ctx - The ParamContext to manage query parameters.
|
|
11
|
-
*/
|
|
12
8
|
constructor(ctx: ParamContext);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @param obj - An object where keys are column names and values are the desired values or an object with `op` and `value`.
|
|
17
|
-
* @returns The current ConditionBuilder instance.
|
|
18
|
-
*/
|
|
19
|
-
where(obj: Record<string, any>): this;
|
|
20
|
-
/**
|
|
21
|
-
* Adds a raw SQL expression to the conditions.
|
|
22
|
-
* @param expression - The raw SQL expression.
|
|
23
|
-
* @returns The current ConditionBuilder instance.
|
|
24
|
-
*/
|
|
9
|
+
where(obj: Record<string, any> | ((qb: ConditionBuilder) => void)): this;
|
|
10
|
+
private handleOperator;
|
|
11
|
+
private add;
|
|
25
12
|
raw(expression: string): this;
|
|
26
|
-
/**
|
|
27
|
-
* Adds a group of conditions connected by AND.
|
|
28
|
-
* @param cb - A callback function that receives a nested ConditionBuilder to define conditions within the group.
|
|
29
|
-
* @returns The current ConditionBuilder instance.
|
|
30
|
-
*/
|
|
31
13
|
andGroup(cb: (qb: ConditionBuilder) => void): this;
|
|
32
|
-
/**
|
|
33
|
-
* Adds a group of conditions connected by OR.
|
|
34
|
-
* @param cb - A callback function that receives a nested ConditionBuilder to define conditions within the group.
|
|
35
|
-
* @returns The current ConditionBuilder instance.
|
|
36
|
-
*/
|
|
37
14
|
orGroup(cb: (qb: ConditionBuilder) => void): this;
|
|
38
|
-
|
|
39
|
-
* Builds the SQL condition string.
|
|
40
|
-
* @param prefix - The prefix for the condition (e.g., 'WHERE', 'HAVING'). Defaults to 'WHERE'.
|
|
41
|
-
* @returns The built SQL condition string, or an empty string if no conditions were added.
|
|
42
|
-
*/
|
|
15
|
+
clone(): ConditionBuilder;
|
|
43
16
|
build(prefix?: string): string;
|
|
44
17
|
}
|
|
@@ -1,100 +1,133 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const QueryBuilder_1 = __importDefault(require("../builders/QueryBuilder"));
|
|
3
7
|
/**
|
|
4
8
|
* A builder for constructing SQL WHERE and HAVING clauses.
|
|
5
9
|
*/
|
|
6
10
|
class ConditionBuilder {
|
|
7
|
-
/**
|
|
8
|
-
* Creates an instance of ConditionBuilder.
|
|
9
|
-
* @param ctx - The ParamContext to manage query parameters.
|
|
10
|
-
*/
|
|
11
11
|
constructor(ctx) {
|
|
12
12
|
this.ctx = ctx;
|
|
13
13
|
this.parts = [];
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
16
|
-
* Adds one or more WHERE conditions based on an object.
|
|
17
|
-
* Supports direct equality, `null` checks, and custom operators.
|
|
18
|
-
* @param obj - An object where keys are column names and values are the desired values or an object with `op` and `value`.
|
|
19
|
-
* @returns The current ConditionBuilder instance.
|
|
20
|
-
*/
|
|
21
15
|
where(obj) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
if (typeof obj === 'function') {
|
|
17
|
+
const nested = new ConditionBuilder(this.ctx);
|
|
18
|
+
obj(nested);
|
|
19
|
+
const built = nested.build();
|
|
20
|
+
if (built) {
|
|
21
|
+
this.add(`(${built.replace(/^WHERE\s/, '')})`);
|
|
22
|
+
}
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
Object.entries(obj).forEach(([key, condition]) => {
|
|
26
|
+
if (condition === null) {
|
|
27
|
+
this.add(`${key} IS NULL`);
|
|
25
28
|
return;
|
|
26
29
|
}
|
|
27
|
-
if (typeof
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
if (typeof condition === 'object'
|
|
31
|
+
&& condition !== null
|
|
32
|
+
&& 'op' in condition) {
|
|
33
|
+
const { op, value } = condition;
|
|
34
|
+
this.handleOperator(key, op, value);
|
|
31
35
|
return;
|
|
32
36
|
}
|
|
33
|
-
const placeholder = this.ctx.add(
|
|
34
|
-
this.
|
|
37
|
+
const placeholder = this.ctx.add(condition);
|
|
38
|
+
this.add(`${key} = ${placeholder}`);
|
|
35
39
|
});
|
|
36
40
|
return this;
|
|
37
41
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
handleOperator(key, op, value) {
|
|
43
|
+
const allowed = [
|
|
44
|
+
'=', '>', '<', '>=', '<=', '!=', '<>',
|
|
45
|
+
'LIKE', 'ILIKE',
|
|
46
|
+
'IN', 'NOT IN',
|
|
47
|
+
'BETWEEN', 'EXISTS'
|
|
48
|
+
];
|
|
49
|
+
if (!allowed.includes(op)) {
|
|
50
|
+
throw new Error(`Invalid operator ${op}`);
|
|
51
|
+
}
|
|
52
|
+
switch (op) {
|
|
53
|
+
case 'IN':
|
|
54
|
+
case 'NOT IN': {
|
|
55
|
+
if (!Array.isArray(value)) {
|
|
56
|
+
throw new Error(`${op} expects array`);
|
|
57
|
+
}
|
|
58
|
+
const placeholders = value.map(v => this.ctx.add(v)).join(', ');
|
|
59
|
+
this.add(`${key} ${op} (${placeholders})`);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case 'BETWEEN': {
|
|
63
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
64
|
+
throw new Error('BETWEEN expects [min,max]');
|
|
65
|
+
}
|
|
66
|
+
const p1 = this.ctx.add(value[0]);
|
|
67
|
+
const p2 = this.ctx.add(value[1]);
|
|
68
|
+
this.add(`${key} BETWEEN ${p1} AND ${p2}`);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case 'EXISTS': {
|
|
72
|
+
if (!(value instanceof QueryBuilder_1.default)) {
|
|
73
|
+
throw new Error('EXISTS expects QueryBuilder');
|
|
74
|
+
}
|
|
75
|
+
const { query, params } = value.build();
|
|
76
|
+
params.forEach(p => this.ctx.add(p));
|
|
77
|
+
this.add(`EXISTS (${query})`);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
default: {
|
|
81
|
+
const placeholder = this.ctx.add(value);
|
|
82
|
+
this.add(`${key} ${op} ${placeholder}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
add(expression, type = 'AND') {
|
|
87
|
+
this.parts.push({ type, expression });
|
|
88
|
+
}
|
|
43
89
|
raw(expression) {
|
|
44
|
-
this.
|
|
90
|
+
this.add(expression);
|
|
45
91
|
return this;
|
|
46
92
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Adds a group of conditions connected by AND.
|
|
49
|
-
* @param cb - A callback function that receives a nested ConditionBuilder to define conditions within the group.
|
|
50
|
-
* @returns The current ConditionBuilder instance.
|
|
51
|
-
*/
|
|
52
93
|
andGroup(cb) {
|
|
53
94
|
const nested = new ConditionBuilder(this.ctx);
|
|
54
95
|
cb(nested);
|
|
55
96
|
const built = nested.build();
|
|
56
|
-
if (built)
|
|
57
|
-
this
|
|
58
|
-
}
|
|
97
|
+
if (!built)
|
|
98
|
+
return this;
|
|
99
|
+
this.add(`(${built.replace(/^WHERE\s/, '')})`, 'AND');
|
|
59
100
|
return this;
|
|
60
101
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Adds a group of conditions connected by OR.
|
|
63
|
-
* @param cb - A callback function that receives a nested ConditionBuilder to define conditions within the group.
|
|
64
|
-
* @returns The current ConditionBuilder instance.
|
|
65
|
-
*/
|
|
66
102
|
orGroup(cb) {
|
|
67
103
|
const nested = new ConditionBuilder(this.ctx);
|
|
68
104
|
cb(nested);
|
|
69
105
|
const built = nested.build();
|
|
70
|
-
if (built)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
106
|
+
if (!built)
|
|
107
|
+
return this;
|
|
108
|
+
this.add(`(${built.replace(/^WHERE\s/, '')})`, 'OR');
|
|
74
109
|
return this;
|
|
75
110
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
111
|
+
clone() {
|
|
112
|
+
const cloned = new ConditionBuilder(this.ctx);
|
|
113
|
+
cloned.parts = this.parts.map(p => ({
|
|
114
|
+
...p
|
|
115
|
+
}));
|
|
116
|
+
return cloned;
|
|
117
|
+
}
|
|
81
118
|
build(prefix = 'WHERE') {
|
|
82
119
|
if (!this.parts.length)
|
|
83
120
|
return '';
|
|
84
|
-
|
|
121
|
+
let sql = '';
|
|
85
122
|
this.parts.forEach((part, index) => {
|
|
86
123
|
if (index === 0) {
|
|
87
|
-
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (part.startsWith('OR ')) {
|
|
91
|
-
normalized.push(part);
|
|
124
|
+
sql += part.expression;
|
|
92
125
|
}
|
|
93
126
|
else {
|
|
94
|
-
|
|
127
|
+
sql += ` ${part.type} ${part.expression}`;
|
|
95
128
|
}
|
|
96
129
|
});
|
|
97
|
-
return `${prefix} ${
|
|
130
|
+
return `${prefix} ${sql}`;
|
|
98
131
|
}
|
|
99
132
|
}
|
|
100
133
|
exports.default = ConditionBuilder;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import ConditionBuilder from './ConditionBuilder';
|
|
2
2
|
import QueryExecutor from '../core/QueryExecutor';
|
|
3
3
|
import { Dialect } from '../dialects/Dialect';
|
|
4
|
+
type WhereInput<T> = Partial<T> | Record<string, any> | Array<Record<string, any>>;
|
|
4
5
|
/**
|
|
5
6
|
* A fluent SQL query builder for constructing and executing database queries.
|
|
6
7
|
* @template T The expected type of the results.
|
|
@@ -66,7 +67,7 @@ export default class QueryBuilder<T = any> {
|
|
|
66
67
|
* @param fields - An array of field names or keys of T.
|
|
67
68
|
* @returns The current QueryBuilder instance.
|
|
68
69
|
*/
|
|
69
|
-
select(fields: (keyof T | string)[]): this;
|
|
70
|
+
select(fields: (keyof T | string)[] | keyof T | string): this;
|
|
70
71
|
/**
|
|
71
72
|
* Adds a join clause to the query.
|
|
72
73
|
* @param type - The type of join (INNER, LEFT, RIGHT).
|
|
@@ -105,7 +106,7 @@ export default class QueryBuilder<T = any> {
|
|
|
105
106
|
* @param obj - An object where keys are column names and values are the desired values.
|
|
106
107
|
* @returns The current QueryBuilder instance.
|
|
107
108
|
*/
|
|
108
|
-
where(obj:
|
|
109
|
+
where(obj: WhereInput<T>): this;
|
|
109
110
|
/**
|
|
110
111
|
* Adds a raw WHERE expression.
|
|
111
112
|
* @param expression - The raw SQL expression for the WHERE clause.
|
|
@@ -187,7 +188,7 @@ export default class QueryBuilder<T = any> {
|
|
|
187
188
|
*/
|
|
188
189
|
build(): {
|
|
189
190
|
query: string;
|
|
190
|
-
params: any[];
|
|
191
|
+
params: readonly (string | number | boolean | any[] | Date | Buffer<ArrayBufferLike> | null)[];
|
|
191
192
|
};
|
|
192
193
|
/**
|
|
193
194
|
* Executes the built SQL query and returns the results.
|
|
@@ -195,3 +196,4 @@ export default class QueryBuilder<T = any> {
|
|
|
195
196
|
*/
|
|
196
197
|
execute(): Promise<T[]>;
|
|
197
198
|
}
|
|
199
|
+
export {};
|
|
@@ -52,7 +52,8 @@ class QueryBuilder {
|
|
|
52
52
|
* @returns The current QueryBuilder instance.
|
|
53
53
|
*/
|
|
54
54
|
select(fields) {
|
|
55
|
-
|
|
55
|
+
const normalized = Array.isArray(fields) ? fields : [fields];
|
|
56
|
+
this.fields = normalized.map(String);
|
|
56
57
|
return this;
|
|
57
58
|
}
|
|
58
59
|
/**
|
|
@@ -229,6 +230,9 @@ class QueryBuilder {
|
|
|
229
230
|
qb.limitCount = this.limitCount;
|
|
230
231
|
qb.offsetCount = this.offsetCount;
|
|
231
232
|
qb.ctes = [...this.ctes];
|
|
233
|
+
qb.condition = this.condition.clone();
|
|
234
|
+
qb.havingCondition = this.havingCondition.clone();
|
|
235
|
+
qb.ctx = this.ctx.clone();
|
|
232
236
|
return qb;
|
|
233
237
|
}
|
|
234
238
|
/**
|
|
@@ -131,7 +131,7 @@ export default class QueryBuilder<T = any> {
|
|
|
131
131
|
*/
|
|
132
132
|
build(): {
|
|
133
133
|
query: string;
|
|
134
|
-
params: any[];
|
|
134
|
+
params: readonly (string | number | boolean | any[] | Date | Buffer<ArrayBufferLike> | null)[];
|
|
135
135
|
};
|
|
136
136
|
/**
|
|
137
137
|
* Executes the built SQL query and returns the results.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Dialect } from "../dialects/Dialect";
|
|
2
|
+
type SQLParam = string | number | boolean | Date | null | Buffer | any[];
|
|
2
3
|
/**
|
|
3
4
|
* Manages parameters for SQL queries, ensuring proper dialect-specific placeholders.
|
|
4
5
|
*/
|
|
@@ -20,5 +21,7 @@ export default class ParamContext {
|
|
|
20
21
|
* Retrieves the list of accumulated parameters.
|
|
21
22
|
* @returns An array of parameters.
|
|
22
23
|
*/
|
|
23
|
-
getParams():
|
|
24
|
+
getParams(): readonly SQLParam[];
|
|
25
|
+
clone(): ParamContext;
|
|
24
26
|
}
|
|
27
|
+
export {};
|
|
@@ -24,6 +24,11 @@ export default class ParamContext {
|
|
|
24
24
|
* @returns An array of parameters.
|
|
25
25
|
*/
|
|
26
26
|
getParams() {
|
|
27
|
-
return this.params;
|
|
27
|
+
return Object.freeze([...this.params]);
|
|
28
|
+
}
|
|
29
|
+
clone() {
|
|
30
|
+
const ctx = new ParamContext(this.dialect);
|
|
31
|
+
ctx.params = [...this.params];
|
|
32
|
+
return ctx;
|
|
28
33
|
}
|
|
29
34
|
}
|
|
@@ -21,7 +21,7 @@ export default class QueryExecutor {
|
|
|
21
21
|
* @param cacheTTL - Time-to-live for caching (not currently used in this implementation).
|
|
22
22
|
* @returns A Promise that resolves to the QueryResult.
|
|
23
23
|
*/
|
|
24
|
-
execute(query: string, params
|
|
24
|
+
execute(query: string, params?: readonly any[], cacheTTL?: number): Promise<QueryResult>;
|
|
25
25
|
/**
|
|
26
26
|
* Returns the underlying PostgreSQL Pool instance.
|
|
27
27
|
* @returns The Pool instance or undefined if not initialized with a connection string.
|
|
@@ -29,11 +29,12 @@ export default class QueryExecutor {
|
|
|
29
29
|
* @returns A Promise that resolves to the QueryResult.
|
|
30
30
|
*/
|
|
31
31
|
async execute(query, params = [], cacheTTL) {
|
|
32
|
+
const normalized = [...params];
|
|
32
33
|
if (this.client) {
|
|
33
|
-
return this.client.query(query,
|
|
34
|
+
return this.client.query(query, normalized);
|
|
34
35
|
}
|
|
35
36
|
if (this.pool) {
|
|
36
|
-
return this.pool.query(query,
|
|
37
|
+
return this.pool.query(query, normalized);
|
|
37
38
|
}
|
|
38
39
|
throw new Error('Executor not initialized');
|
|
39
40
|
}
|
|
@@ -5,40 +5,13 @@ import ParamContext from '../core/ParamContext';
|
|
|
5
5
|
export default class ConditionBuilder {
|
|
6
6
|
private ctx;
|
|
7
7
|
private parts;
|
|
8
|
-
/**
|
|
9
|
-
* Creates an instance of ConditionBuilder.
|
|
10
|
-
* @param ctx - The ParamContext to manage query parameters.
|
|
11
|
-
*/
|
|
12
8
|
constructor(ctx: ParamContext);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @param obj - An object where keys are column names and values are the desired values or an object with `op` and `value`.
|
|
17
|
-
* @returns The current ConditionBuilder instance.
|
|
18
|
-
*/
|
|
19
|
-
where(obj: Record<string, any>): this;
|
|
20
|
-
/**
|
|
21
|
-
* Adds a raw SQL expression to the conditions.
|
|
22
|
-
* @param expression - The raw SQL expression.
|
|
23
|
-
* @returns The current ConditionBuilder instance.
|
|
24
|
-
*/
|
|
9
|
+
where(obj: Record<string, any> | ((qb: ConditionBuilder) => void)): this;
|
|
10
|
+
private handleOperator;
|
|
11
|
+
private add;
|
|
25
12
|
raw(expression: string): this;
|
|
26
|
-
/**
|
|
27
|
-
* Adds a group of conditions connected by AND.
|
|
28
|
-
* @param cb - A callback function that receives a nested ConditionBuilder to define conditions within the group.
|
|
29
|
-
* @returns The current ConditionBuilder instance.
|
|
30
|
-
*/
|
|
31
13
|
andGroup(cb: (qb: ConditionBuilder) => void): this;
|
|
32
|
-
/**
|
|
33
|
-
* Adds a group of conditions connected by OR.
|
|
34
|
-
* @param cb - A callback function that receives a nested ConditionBuilder to define conditions within the group.
|
|
35
|
-
* @returns The current ConditionBuilder instance.
|
|
36
|
-
*/
|
|
37
14
|
orGroup(cb: (qb: ConditionBuilder) => void): this;
|
|
38
|
-
|
|
39
|
-
* Builds the SQL condition string.
|
|
40
|
-
* @param prefix - The prefix for the condition (e.g., 'WHERE', 'HAVING'). Defaults to 'WHERE'.
|
|
41
|
-
* @returns The built SQL condition string, or an empty string if no conditions were added.
|
|
42
|
-
*/
|
|
15
|
+
clone(): ConditionBuilder;
|
|
43
16
|
build(prefix?: string): string;
|
|
44
17
|
}
|
|
@@ -1,97 +1,127 @@
|
|
|
1
|
+
import QueryBuilder from "../builders/QueryBuilder";
|
|
1
2
|
/**
|
|
2
3
|
* A builder for constructing SQL WHERE and HAVING clauses.
|
|
3
4
|
*/
|
|
4
5
|
export default class ConditionBuilder {
|
|
5
|
-
/**
|
|
6
|
-
* Creates an instance of ConditionBuilder.
|
|
7
|
-
* @param ctx - The ParamContext to manage query parameters.
|
|
8
|
-
*/
|
|
9
6
|
constructor(ctx) {
|
|
10
7
|
this.ctx = ctx;
|
|
11
8
|
this.parts = [];
|
|
12
9
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Adds one or more WHERE conditions based on an object.
|
|
15
|
-
* Supports direct equality, `null` checks, and custom operators.
|
|
16
|
-
* @param obj - An object where keys are column names and values are the desired values or an object with `op` and `value`.
|
|
17
|
-
* @returns The current ConditionBuilder instance.
|
|
18
|
-
*/
|
|
19
10
|
where(obj) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
if (typeof obj === 'function') {
|
|
12
|
+
const nested = new ConditionBuilder(this.ctx);
|
|
13
|
+
obj(nested);
|
|
14
|
+
const built = nested.build();
|
|
15
|
+
if (built) {
|
|
16
|
+
this.add(`(${built.replace(/^WHERE\s/, '')})`);
|
|
17
|
+
}
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
Object.entries(obj).forEach(([key, condition]) => {
|
|
21
|
+
if (condition === null) {
|
|
22
|
+
this.add(`${key} IS NULL`);
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
-
if (typeof
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
if (typeof condition === 'object'
|
|
26
|
+
&& condition !== null
|
|
27
|
+
&& 'op' in condition) {
|
|
28
|
+
const { op, value } = condition;
|
|
29
|
+
this.handleOperator(key, op, value);
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
|
-
const placeholder = this.ctx.add(
|
|
32
|
-
this.
|
|
32
|
+
const placeholder = this.ctx.add(condition);
|
|
33
|
+
this.add(`${key} = ${placeholder}`);
|
|
33
34
|
});
|
|
34
35
|
return this;
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
handleOperator(key, op, value) {
|
|
38
|
+
const allowed = [
|
|
39
|
+
'=', '>', '<', '>=', '<=', '!=', '<>',
|
|
40
|
+
'LIKE', 'ILIKE',
|
|
41
|
+
'IN', 'NOT IN',
|
|
42
|
+
'BETWEEN', 'EXISTS'
|
|
43
|
+
];
|
|
44
|
+
if (!allowed.includes(op)) {
|
|
45
|
+
throw new Error(`Invalid operator ${op}`);
|
|
46
|
+
}
|
|
47
|
+
switch (op) {
|
|
48
|
+
case 'IN':
|
|
49
|
+
case 'NOT IN': {
|
|
50
|
+
if (!Array.isArray(value)) {
|
|
51
|
+
throw new Error(`${op} expects array`);
|
|
52
|
+
}
|
|
53
|
+
const placeholders = value.map(v => this.ctx.add(v)).join(', ');
|
|
54
|
+
this.add(`${key} ${op} (${placeholders})`);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case 'BETWEEN': {
|
|
58
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
59
|
+
throw new Error('BETWEEN expects [min,max]');
|
|
60
|
+
}
|
|
61
|
+
const p1 = this.ctx.add(value[0]);
|
|
62
|
+
const p2 = this.ctx.add(value[1]);
|
|
63
|
+
this.add(`${key} BETWEEN ${p1} AND ${p2}`);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case 'EXISTS': {
|
|
67
|
+
if (!(value instanceof QueryBuilder)) {
|
|
68
|
+
throw new Error('EXISTS expects QueryBuilder');
|
|
69
|
+
}
|
|
70
|
+
const { query, params } = value.build();
|
|
71
|
+
params.forEach(p => this.ctx.add(p));
|
|
72
|
+
this.add(`EXISTS (${query})`);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
default: {
|
|
76
|
+
const placeholder = this.ctx.add(value);
|
|
77
|
+
this.add(`${key} ${op} ${placeholder}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
add(expression, type = 'AND') {
|
|
82
|
+
this.parts.push({ type, expression });
|
|
83
|
+
}
|
|
41
84
|
raw(expression) {
|
|
42
|
-
this.
|
|
85
|
+
this.add(expression);
|
|
43
86
|
return this;
|
|
44
87
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Adds a group of conditions connected by AND.
|
|
47
|
-
* @param cb - A callback function that receives a nested ConditionBuilder to define conditions within the group.
|
|
48
|
-
* @returns The current ConditionBuilder instance.
|
|
49
|
-
*/
|
|
50
88
|
andGroup(cb) {
|
|
51
89
|
const nested = new ConditionBuilder(this.ctx);
|
|
52
90
|
cb(nested);
|
|
53
91
|
const built = nested.build();
|
|
54
|
-
if (built)
|
|
55
|
-
this
|
|
56
|
-
}
|
|
92
|
+
if (!built)
|
|
93
|
+
return this;
|
|
94
|
+
this.add(`(${built.replace(/^WHERE\s/, '')})`, 'AND');
|
|
57
95
|
return this;
|
|
58
96
|
}
|
|
59
|
-
/**
|
|
60
|
-
* Adds a group of conditions connected by OR.
|
|
61
|
-
* @param cb - A callback function that receives a nested ConditionBuilder to define conditions within the group.
|
|
62
|
-
* @returns The current ConditionBuilder instance.
|
|
63
|
-
*/
|
|
64
97
|
orGroup(cb) {
|
|
65
98
|
const nested = new ConditionBuilder(this.ctx);
|
|
66
99
|
cb(nested);
|
|
67
100
|
const built = nested.build();
|
|
68
|
-
if (built)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
101
|
+
if (!built)
|
|
102
|
+
return this;
|
|
103
|
+
this.add(`(${built.replace(/^WHERE\s/, '')})`, 'OR');
|
|
72
104
|
return this;
|
|
73
105
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
106
|
+
clone() {
|
|
107
|
+
const cloned = new ConditionBuilder(this.ctx);
|
|
108
|
+
cloned.parts = this.parts.map(p => ({
|
|
109
|
+
...p
|
|
110
|
+
}));
|
|
111
|
+
return cloned;
|
|
112
|
+
}
|
|
79
113
|
build(prefix = 'WHERE') {
|
|
80
114
|
if (!this.parts.length)
|
|
81
115
|
return '';
|
|
82
|
-
|
|
116
|
+
let sql = '';
|
|
83
117
|
this.parts.forEach((part, index) => {
|
|
84
118
|
if (index === 0) {
|
|
85
|
-
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (part.startsWith('OR ')) {
|
|
89
|
-
normalized.push(part);
|
|
119
|
+
sql += part.expression;
|
|
90
120
|
}
|
|
91
121
|
else {
|
|
92
|
-
|
|
122
|
+
sql += ` ${part.type} ${part.expression}`;
|
|
93
123
|
}
|
|
94
124
|
});
|
|
95
|
-
return `${prefix} ${
|
|
125
|
+
return `${prefix} ${sql}`;
|
|
96
126
|
}
|
|
97
127
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import ConditionBuilder from './ConditionBuilder';
|
|
2
2
|
import QueryExecutor from '../core/QueryExecutor';
|
|
3
3
|
import { Dialect } from '../dialects/Dialect';
|
|
4
|
+
type WhereInput<T> = Partial<T> | Record<string, any> | Array<Record<string, any>>;
|
|
4
5
|
/**
|
|
5
6
|
* A fluent SQL query builder for constructing and executing database queries.
|
|
6
7
|
* @template T The expected type of the results.
|
|
@@ -66,7 +67,7 @@ export default class QueryBuilder<T = any> {
|
|
|
66
67
|
* @param fields - An array of field names or keys of T.
|
|
67
68
|
* @returns The current QueryBuilder instance.
|
|
68
69
|
*/
|
|
69
|
-
select(fields: (keyof T | string)[]): this;
|
|
70
|
+
select(fields: (keyof T | string)[] | keyof T | string): this;
|
|
70
71
|
/**
|
|
71
72
|
* Adds a join clause to the query.
|
|
72
73
|
* @param type - The type of join (INNER, LEFT, RIGHT).
|
|
@@ -105,7 +106,7 @@ export default class QueryBuilder<T = any> {
|
|
|
105
106
|
* @param obj - An object where keys are column names and values are the desired values.
|
|
106
107
|
* @returns The current QueryBuilder instance.
|
|
107
108
|
*/
|
|
108
|
-
where(obj:
|
|
109
|
+
where(obj: WhereInput<T>): this;
|
|
109
110
|
/**
|
|
110
111
|
* Adds a raw WHERE expression.
|
|
111
112
|
* @param expression - The raw SQL expression for the WHERE clause.
|
|
@@ -187,7 +188,7 @@ export default class QueryBuilder<T = any> {
|
|
|
187
188
|
*/
|
|
188
189
|
build(): {
|
|
189
190
|
query: string;
|
|
190
|
-
params: any[];
|
|
191
|
+
params: readonly (string | number | boolean | any[] | Date | Buffer<ArrayBufferLike> | null)[];
|
|
191
192
|
};
|
|
192
193
|
/**
|
|
193
194
|
* Executes the built SQL query and returns the results.
|
|
@@ -195,3 +196,4 @@ export default class QueryBuilder<T = any> {
|
|
|
195
196
|
*/
|
|
196
197
|
execute(): Promise<T[]>;
|
|
197
198
|
}
|
|
199
|
+
export {};
|
|
@@ -47,7 +47,8 @@ export default class QueryBuilder {
|
|
|
47
47
|
* @returns The current QueryBuilder instance.
|
|
48
48
|
*/
|
|
49
49
|
select(fields) {
|
|
50
|
-
|
|
50
|
+
const normalized = Array.isArray(fields) ? fields : [fields];
|
|
51
|
+
this.fields = normalized.map(String);
|
|
51
52
|
return this;
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
@@ -224,6 +225,9 @@ export default class QueryBuilder {
|
|
|
224
225
|
qb.limitCount = this.limitCount;
|
|
225
226
|
qb.offsetCount = this.offsetCount;
|
|
226
227
|
qb.ctes = [...this.ctes];
|
|
228
|
+
qb.condition = this.condition.clone();
|
|
229
|
+
qb.havingCondition = this.havingCondition.clone();
|
|
230
|
+
qb.ctx = this.ctx.clone();
|
|
227
231
|
return qb;
|
|
228
232
|
}
|
|
229
233
|
/**
|