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 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
- connectionString: 'postgres://user:pass@localhost:5432/your_database',
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
- console.log('Complex WHERE Products:', products);
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(): any[];
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: any[] | undefined, cacheTTL: number | undefined): Promise<QueryResult>;
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, params);
36
+ return this.client.query(query, normalized);
36
37
  }
37
38
  if (this.pool) {
38
- return this.pool.query(query, params);
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
- * 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
- 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
- Object.entries(obj).forEach(([key, value]) => {
23
- if (value === null) {
24
- this.parts.push(`${key} IS NULL`);
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 value === 'object' && value !== null && 'op' in value) {
28
- const { op, value: v } = value;
29
- const placeholder = this.ctx.add(v);
30
- this.parts.push(`${key} ${op} ${placeholder}`);
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(value);
34
- this.parts.push(`${key} = ${placeholder}`);
37
+ const placeholder = this.ctx.add(condition);
38
+ this.add(`${key} = ${placeholder}`);
35
39
  });
36
40
  return this;
37
41
  }
38
- /**
39
- * Adds a raw SQL expression to the conditions.
40
- * @param expression - The raw SQL expression.
41
- * @returns The current ConditionBuilder instance.
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.parts.push(expression);
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.parts.push(`(${built.replace(/^WHERE\s/, '')})`);
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
- const clean = built.replace(/^WHERE\s/, '');
72
- this.parts.push(`OR (${clean})`);
73
- }
106
+ if (!built)
107
+ return this;
108
+ this.add(`(${built.replace(/^WHERE\s/, '')})`, 'OR');
74
109
  return this;
75
110
  }
76
- /**
77
- * Builds the SQL condition string.
78
- * @param prefix - The prefix for the condition (e.g., 'WHERE', 'HAVING'). Defaults to 'WHERE'.
79
- * @returns The built SQL condition string, or an empty string if no conditions were added.
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
- const normalized = [];
121
+ let sql = '';
85
122
  this.parts.forEach((part, index) => {
86
123
  if (index === 0) {
87
- normalized.push(part);
88
- return;
89
- }
90
- if (part.startsWith('OR ')) {
91
- normalized.push(part);
124
+ sql += part.expression;
92
125
  }
93
126
  else {
94
- normalized.push(`AND ${part}`);
127
+ sql += ` ${part.type} ${part.expression}`;
95
128
  }
96
129
  });
97
- return `${prefix} ${normalized.join(' ')}`;
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: Partial<T>): this;
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
- this.fields = fields.map(String);
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(): any[];
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: any[] | undefined, cacheTTL: number | undefined): Promise<QueryResult>;
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, params);
34
+ return this.client.query(query, normalized);
34
35
  }
35
36
  if (this.pool) {
36
- return this.pool.query(query, params);
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
- * 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
- 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
- Object.entries(obj).forEach(([key, value]) => {
21
- if (value === null) {
22
- this.parts.push(`${key} IS NULL`);
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 value === 'object' && value !== null && 'op' in value) {
26
- const { op, value: v } = value;
27
- const placeholder = this.ctx.add(v);
28
- this.parts.push(`${key} ${op} ${placeholder}`);
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(value);
32
- this.parts.push(`${key} = ${placeholder}`);
32
+ const placeholder = this.ctx.add(condition);
33
+ this.add(`${key} = ${placeholder}`);
33
34
  });
34
35
  return this;
35
36
  }
36
- /**
37
- * Adds a raw SQL expression to the conditions.
38
- * @param expression - The raw SQL expression.
39
- * @returns The current ConditionBuilder instance.
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.parts.push(expression);
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.parts.push(`(${built.replace(/^WHERE\s/, '')})`);
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
- const clean = built.replace(/^WHERE\s/, '');
70
- this.parts.push(`OR (${clean})`);
71
- }
101
+ if (!built)
102
+ return this;
103
+ this.add(`(${built.replace(/^WHERE\s/, '')})`, 'OR');
72
104
  return this;
73
105
  }
74
- /**
75
- * Builds the SQL condition string.
76
- * @param prefix - The prefix for the condition (e.g., 'WHERE', 'HAVING'). Defaults to 'WHERE'.
77
- * @returns The built SQL condition string, or an empty string if no conditions were added.
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
- const normalized = [];
116
+ let sql = '';
83
117
  this.parts.forEach((part, index) => {
84
118
  if (index === 0) {
85
- normalized.push(part);
86
- return;
87
- }
88
- if (part.startsWith('OR ')) {
89
- normalized.push(part);
119
+ sql += part.expression;
90
120
  }
91
121
  else {
92
- normalized.push(`AND ${part}`);
122
+ sql += ` ${part.type} ${part.expression}`;
93
123
  }
94
124
  });
95
- return `${prefix} ${normalized.join(' ')}`;
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: Partial<T>): this;
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
- this.fields = fields.map(String);
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
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pg-query-sdk",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "PostgreSQL SDK with Query Builder and Executor",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/esm/index.d.ts",