pg-query-sdk 1.0.1 → 1.0.3

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
@@ -35,7 +35,7 @@ npm install pg-query-sdk
35
35
  The `Database` class serves as the primary interface for all database interactions. Instantiate it with your PostgreSQL connection string.
36
36
 
37
37
  ```typescript
38
- import Database from 'pg-query-sdk';
38
+ import {Database} from 'pg-query-sdk';
39
39
 
40
40
  const db = new Database({
41
41
  connectionString: 'postgres://user:pass@localhost:5432/your_database',
@@ -51,7 +51,7 @@ const db = new Database({
51
51
  The `QueryBuilder` enables the programmatic construction of SQL `SELECT` statements, accessible via the `db.table()` method.
52
52
 
53
53
  ```typescript
54
- import Database from 'pg-query-sdk';
54
+ import {Database} from 'pg-query-sdk';
55
55
 
56
56
  const db = new Database({
57
57
  connectionString: 'postgres://user:pass@localhost:5432/your_database',
@@ -76,7 +76,7 @@ selectExample();
76
76
  The `ConditionBuilder` facilitates the creation of complex conditional logic within `WHERE` and `HAVING` clauses, typically used in conjunction with the `QueryBuilder`.
77
77
 
78
78
  ```typescript
79
- import Database from 'pg-query-sdk';
79
+ import {Database} from 'pg-query-sdk';
80
80
 
81
81
  const db = new Database({
82
82
  connectionString: 'postgres://user:pass@localhost:5432/your_database',
@@ -129,7 +129,7 @@ directExecuteExample();
129
129
  The SDK provides robust support for managing ACID-compliant transactions, ensuring data integrity.
130
130
 
131
131
  ```typescript
132
- import Database from 'pg-query-sdk';
132
+ import {Database} from 'pg-query-sdk';
133
133
 
134
134
  const db = new Database({
135
135
  connectionString: 'postgres://user:pass@localhost:5432/your_database',
@@ -164,7 +164,7 @@ transactionExample();
164
164
  The abstract `Repository<T>` class offers a foundational ORM layer, providing methods like `findById` and a pre-configured `QueryBuilder` (`qb()`). Custom DML operations (`insert`, `update`, `delete`) should be implemented in concrete repository classes.
165
165
 
166
166
  ```typescript
167
- import Database, { Repository } from 'pg-query-sdk';
167
+ import {Database, Repository } from 'pg-query-sdk';
168
168
  import { QueryExecutor, Dialect } from 'pg-query-sdk';
169
169
 
170
170
  interface User {
@@ -221,7 +221,7 @@ const db = new Database({ /* ... */ });
221
221
  ### ESM
222
222
 
223
223
  ```typescript
224
- import Database from 'pg-query-sdk';
224
+ import {Database} from 'pg-query-sdk';
225
225
  const db = new Database({ /* ... */ });
226
226
  ```
227
227
 
@@ -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
  }
@@ -1,44 +1,23 @@
1
1
  import ParamContext from '../core/ParamContext';
2
+ type Operator = '=' | '>' | '<' | '>=' | '<=' | '!=' | '<>' | 'LIKE' | 'ILIKE' | 'IN' | 'NOT IN' | 'BETWEEN' | 'EXISTS';
3
+ type ConditionValue = any | {
4
+ op: Operator;
5
+ value: any;
6
+ };
2
7
  /**
3
8
  * A builder for constructing SQL WHERE and HAVING clauses.
4
9
  */
5
10
  export default class ConditionBuilder {
6
11
  private ctx;
7
12
  private parts;
8
- /**
9
- * Creates an instance of ConditionBuilder.
10
- * @param ctx - The ParamContext to manage query parameters.
11
- */
12
13
  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
- */
14
+ where(obj: Record<string, ConditionValue>): this;
15
+ private handleOperator;
16
+ private add;
25
17
  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
18
  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
19
  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
- */
20
+ clone(): ConditionBuilder;
43
21
  build(prefix?: string): string;
44
22
  }
23
+ export {};
@@ -1,100 +1,124 @@
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
+ Object.entries(obj).forEach(([key, condition]) => {
17
+ if (condition === null) {
18
+ this.add(`${key} IS NULL`);
25
19
  return;
26
20
  }
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}`);
21
+ if (typeof condition === 'object'
22
+ && condition !== null
23
+ && 'op' in condition) {
24
+ const { op, value } = condition;
25
+ this.handleOperator(key, op, value);
31
26
  return;
32
27
  }
33
- const placeholder = this.ctx.add(value);
34
- this.parts.push(`${key} = ${placeholder}`);
28
+ const placeholder = this.ctx.add(condition);
29
+ this.add(`${key} = ${placeholder}`);
35
30
  });
36
31
  return this;
37
32
  }
38
- /**
39
- * Adds a raw SQL expression to the conditions.
40
- * @param expression - The raw SQL expression.
41
- * @returns The current ConditionBuilder instance.
42
- */
33
+ handleOperator(key, op, value) {
34
+ const allowed = [
35
+ '=', '>', '<', '>=', '<=', '!=', '<>',
36
+ 'LIKE', 'ILIKE',
37
+ 'IN', 'NOT IN',
38
+ 'BETWEEN', 'EXISTS'
39
+ ];
40
+ if (!allowed.includes(op)) {
41
+ throw new Error(`Invalid operator ${op}`);
42
+ }
43
+ switch (op) {
44
+ case 'IN':
45
+ case 'NOT IN': {
46
+ if (!Array.isArray(value)) {
47
+ throw new Error(`${op} expects array`);
48
+ }
49
+ const placeholders = value.map(v => this.ctx.add(v)).join(', ');
50
+ this.add(`${key} ${op} (${placeholders})`);
51
+ break;
52
+ }
53
+ case 'BETWEEN': {
54
+ if (!Array.isArray(value) || value.length !== 2) {
55
+ throw new Error('BETWEEN expects [min,max]');
56
+ }
57
+ const p1 = this.ctx.add(value[0]);
58
+ const p2 = this.ctx.add(value[1]);
59
+ this.add(`${key} BETWEEN ${p1} AND ${p2}`);
60
+ break;
61
+ }
62
+ case 'EXISTS': {
63
+ if (!(value instanceof QueryBuilder_1.default)) {
64
+ throw new Error('EXISTS expects QueryBuilder');
65
+ }
66
+ const { query, params } = value.build();
67
+ params.forEach(p => this.ctx.add(p));
68
+ this.add(`EXISTS (${query})`);
69
+ break;
70
+ }
71
+ default: {
72
+ const placeholder = this.ctx.add(value);
73
+ this.add(`${key} ${op} ${placeholder}`);
74
+ }
75
+ }
76
+ }
77
+ add(expression, type = 'AND') {
78
+ this.parts.push({ type, expression });
79
+ }
43
80
  raw(expression) {
44
- this.parts.push(expression);
81
+ this.add(expression);
45
82
  return this;
46
83
  }
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
84
  andGroup(cb) {
53
85
  const nested = new ConditionBuilder(this.ctx);
54
86
  cb(nested);
55
87
  const built = nested.build();
56
- if (built) {
57
- this.parts.push(`(${built.replace(/^WHERE\s/, '')})`);
58
- }
88
+ if (!built)
89
+ return this;
90
+ this.add(`(${built.replace(/^WHERE\s/, '')})`, 'AND');
59
91
  return this;
60
92
  }
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
93
  orGroup(cb) {
67
94
  const nested = new ConditionBuilder(this.ctx);
68
95
  cb(nested);
69
96
  const built = nested.build();
70
- if (built) {
71
- const clean = built.replace(/^WHERE\s/, '');
72
- this.parts.push(`OR (${clean})`);
73
- }
97
+ if (!built)
98
+ return this;
99
+ this.add(`(${built.replace(/^WHERE\s/, '')})`, 'OR');
74
100
  return this;
75
101
  }
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
- */
102
+ clone() {
103
+ const cloned = new ConditionBuilder(this.ctx);
104
+ cloned.parts = this.parts.map(p => ({
105
+ ...p
106
+ }));
107
+ return cloned;
108
+ }
81
109
  build(prefix = 'WHERE') {
82
110
  if (!this.parts.length)
83
111
  return '';
84
- const normalized = [];
112
+ let sql = '';
85
113
  this.parts.forEach((part, index) => {
86
114
  if (index === 0) {
87
- normalized.push(part);
88
- return;
89
- }
90
- if (part.startsWith('OR ')) {
91
- normalized.push(part);
115
+ sql += part.expression;
92
116
  }
93
117
  else {
94
- normalized.push(`AND ${part}`);
118
+ sql += ` ${part.type} ${part.expression}`;
95
119
  }
96
120
  });
97
- return `${prefix} ${normalized.join(' ')}`;
121
+ return `${prefix} ${sql}`;
98
122
  }
99
123
  }
100
124
  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
  }
@@ -1,44 +1,23 @@
1
1
  import ParamContext from '../core/ParamContext';
2
+ type Operator = '=' | '>' | '<' | '>=' | '<=' | '!=' | '<>' | 'LIKE' | 'ILIKE' | 'IN' | 'NOT IN' | 'BETWEEN' | 'EXISTS';
3
+ type ConditionValue = any | {
4
+ op: Operator;
5
+ value: any;
6
+ };
2
7
  /**
3
8
  * A builder for constructing SQL WHERE and HAVING clauses.
4
9
  */
5
10
  export default class ConditionBuilder {
6
11
  private ctx;
7
12
  private parts;
8
- /**
9
- * Creates an instance of ConditionBuilder.
10
- * @param ctx - The ParamContext to manage query parameters.
11
- */
12
13
  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
- */
14
+ where(obj: Record<string, ConditionValue>): this;
15
+ private handleOperator;
16
+ private add;
25
17
  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
18
  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
19
  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
- */
20
+ clone(): ConditionBuilder;
43
21
  build(prefix?: string): string;
44
22
  }
23
+ export {};
@@ -1,97 +1,118 @@
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
+ Object.entries(obj).forEach(([key, condition]) => {
12
+ if (condition === null) {
13
+ this.add(`${key} IS NULL`);
23
14
  return;
24
15
  }
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}`);
16
+ if (typeof condition === 'object'
17
+ && condition !== null
18
+ && 'op' in condition) {
19
+ const { op, value } = condition;
20
+ this.handleOperator(key, op, value);
29
21
  return;
30
22
  }
31
- const placeholder = this.ctx.add(value);
32
- this.parts.push(`${key} = ${placeholder}`);
23
+ const placeholder = this.ctx.add(condition);
24
+ this.add(`${key} = ${placeholder}`);
33
25
  });
34
26
  return this;
35
27
  }
36
- /**
37
- * Adds a raw SQL expression to the conditions.
38
- * @param expression - The raw SQL expression.
39
- * @returns The current ConditionBuilder instance.
40
- */
28
+ handleOperator(key, op, value) {
29
+ const allowed = [
30
+ '=', '>', '<', '>=', '<=', '!=', '<>',
31
+ 'LIKE', 'ILIKE',
32
+ 'IN', 'NOT IN',
33
+ 'BETWEEN', 'EXISTS'
34
+ ];
35
+ if (!allowed.includes(op)) {
36
+ throw new Error(`Invalid operator ${op}`);
37
+ }
38
+ switch (op) {
39
+ case 'IN':
40
+ case 'NOT IN': {
41
+ if (!Array.isArray(value)) {
42
+ throw new Error(`${op} expects array`);
43
+ }
44
+ const placeholders = value.map(v => this.ctx.add(v)).join(', ');
45
+ this.add(`${key} ${op} (${placeholders})`);
46
+ break;
47
+ }
48
+ case 'BETWEEN': {
49
+ if (!Array.isArray(value) || value.length !== 2) {
50
+ throw new Error('BETWEEN expects [min,max]');
51
+ }
52
+ const p1 = this.ctx.add(value[0]);
53
+ const p2 = this.ctx.add(value[1]);
54
+ this.add(`${key} BETWEEN ${p1} AND ${p2}`);
55
+ break;
56
+ }
57
+ case 'EXISTS': {
58
+ if (!(value instanceof QueryBuilder)) {
59
+ throw new Error('EXISTS expects QueryBuilder');
60
+ }
61
+ const { query, params } = value.build();
62
+ params.forEach(p => this.ctx.add(p));
63
+ this.add(`EXISTS (${query})`);
64
+ break;
65
+ }
66
+ default: {
67
+ const placeholder = this.ctx.add(value);
68
+ this.add(`${key} ${op} ${placeholder}`);
69
+ }
70
+ }
71
+ }
72
+ add(expression, type = 'AND') {
73
+ this.parts.push({ type, expression });
74
+ }
41
75
  raw(expression) {
42
- this.parts.push(expression);
76
+ this.add(expression);
43
77
  return this;
44
78
  }
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
79
  andGroup(cb) {
51
80
  const nested = new ConditionBuilder(this.ctx);
52
81
  cb(nested);
53
82
  const built = nested.build();
54
- if (built) {
55
- this.parts.push(`(${built.replace(/^WHERE\s/, '')})`);
56
- }
83
+ if (!built)
84
+ return this;
85
+ this.add(`(${built.replace(/^WHERE\s/, '')})`, 'AND');
57
86
  return this;
58
87
  }
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
88
  orGroup(cb) {
65
89
  const nested = new ConditionBuilder(this.ctx);
66
90
  cb(nested);
67
91
  const built = nested.build();
68
- if (built) {
69
- const clean = built.replace(/^WHERE\s/, '');
70
- this.parts.push(`OR (${clean})`);
71
- }
92
+ if (!built)
93
+ return this;
94
+ this.add(`(${built.replace(/^WHERE\s/, '')})`, 'OR');
72
95
  return this;
73
96
  }
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
- */
97
+ clone() {
98
+ const cloned = new ConditionBuilder(this.ctx);
99
+ cloned.parts = this.parts.map(p => ({
100
+ ...p
101
+ }));
102
+ return cloned;
103
+ }
79
104
  build(prefix = 'WHERE') {
80
105
  if (!this.parts.length)
81
106
  return '';
82
- const normalized = [];
107
+ let sql = '';
83
108
  this.parts.forEach((part, index) => {
84
109
  if (index === 0) {
85
- normalized.push(part);
86
- return;
87
- }
88
- if (part.startsWith('OR ')) {
89
- normalized.push(part);
110
+ sql += part.expression;
90
111
  }
91
112
  else {
92
- normalized.push(`AND ${part}`);
113
+ sql += ` ${part.type} ${part.expression}`;
93
114
  }
94
115
  });
95
- return `${prefix} ${normalized.join(' ')}`;
116
+ return `${prefix} ${sql}`;
96
117
  }
97
118
  }
@@ -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.1",
3
+ "version": "1.0.3",
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",