@hypequery/clickhouse 0.2.1 → 0.2.2-beta.0

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 (69) hide show
  1. package/README-CLI.md +1 -1
  2. package/dist/cli/bin.js +129 -37
  3. package/dist/cli/generate-types.js +104 -15
  4. package/dist/core/connection.d.ts +112 -0
  5. package/dist/core/connection.d.ts.map +1 -1
  6. package/dist/core/connection.js +148 -25
  7. package/dist/core/cross-filter.d.ts +69 -0
  8. package/dist/core/cross-filter.d.ts.map +1 -1
  9. package/dist/core/cross-filter.js +1 -83
  10. package/dist/core/features/aggregations.d.ts +102 -0
  11. package/dist/core/features/analytics.d.ts +66 -0
  12. package/dist/core/features/analytics.d.ts.map +1 -1
  13. package/dist/core/features/cross-filtering.d.ts +31 -0
  14. package/dist/core/features/cross-filtering.d.ts.map +1 -0
  15. package/dist/core/features/cross-filtering.js +123 -0
  16. package/dist/core/features/executor.d.ts +19 -0
  17. package/dist/core/features/executor.js +3 -3
  18. package/dist/core/features/filtering.d.ts +95 -0
  19. package/dist/core/features/filtering.d.ts.map +1 -1
  20. package/dist/core/features/filtering.js +89 -3
  21. package/dist/core/features/joins.d.ts +29 -0
  22. package/dist/core/features/pagination.d.ts +23 -0
  23. package/dist/core/features/query-modifiers.d.ts +119 -0
  24. package/dist/core/formatters/sql-formatter.d.ts +9 -0
  25. package/dist/core/formatters/sql-formatter.d.ts.map +1 -1
  26. package/dist/core/formatters/sql-formatter.js +61 -5
  27. package/dist/core/join-relationships.d.ts +50 -0
  28. package/dist/core/join-relationships.d.ts.map +1 -1
  29. package/dist/core/query-builder.d.ts +258 -0
  30. package/dist/core/query-builder.d.ts.map +1 -1
  31. package/dist/core/query-builder.js +88 -27
  32. package/dist/core/tests/index.d.ts +2 -0
  33. package/dist/core/tests/integration/pagination-test-tbc.d.ts +2 -0
  34. package/dist/core/tests/integration/pagination-test-tbc.d.ts.map +1 -0
  35. package/dist/core/tests/integration/pagination-test-tbc.js +189 -0
  36. package/dist/core/tests/integration/setup.d.ts +40 -0
  37. package/dist/core/tests/integration/setup.d.ts.map +1 -1
  38. package/dist/core/tests/integration/setup.js +279 -238
  39. package/dist/core/tests/integration/test-config.d.ts +15 -0
  40. package/dist/core/tests/integration/test-config.d.ts.map +1 -0
  41. package/dist/core/tests/integration/test-config.js +15 -0
  42. package/dist/core/tests/integration/test-initializer.d.ts +7 -0
  43. package/dist/core/tests/integration/test-initializer.d.ts.map +1 -0
  44. package/dist/core/tests/integration/test-initializer.js +32 -0
  45. package/dist/core/tests/test-utils.d.ts +29 -0
  46. package/dist/core/tests/test-utils.d.ts.map +1 -1
  47. package/dist/core/tests/test-utils.js +6 -2
  48. package/dist/core/utils/logger.d.ts +37 -0
  49. package/dist/core/utils/logger.js +6 -6
  50. package/dist/core/utils/sql-expressions.d.ts +63 -0
  51. package/dist/core/utils/sql-expressions.d.ts.map +1 -1
  52. package/dist/core/utils/sql-expressions.js +9 -5
  53. package/dist/core/utils.d.ts +3 -0
  54. package/dist/core/validators/filter-validator.d.ts +8 -0
  55. package/dist/core/validators/filter-validator.js +1 -1
  56. package/dist/core/validators/value-validator.d.ts +6 -0
  57. package/dist/formatters/index.d.ts +1 -0
  58. package/dist/index.d.ts +12 -27
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +14 -2
  61. package/dist/types/base.d.ts +77 -0
  62. package/dist/types/base.d.ts.map +1 -1
  63. package/dist/types/clickhouse-types.d.ts +13 -0
  64. package/dist/types/clickhouse-types.d.ts.map +1 -1
  65. package/dist/types/filters.d.ts +53 -0
  66. package/dist/types/filters.d.ts.map +1 -1
  67. package/dist/types/index.d.ts +3 -0
  68. package/package.json +36 -13
  69. package/README.md +0 -276
@@ -0,0 +1,123 @@
1
+ //@ts-
2
+ /**
3
+ * Type guard to check if an object is a FilterConditionInput
4
+ */
5
+ function isFilterCondition(obj) {
6
+ return obj && 'column' in obj && 'operator' in obj && 'value' in obj;
7
+ }
8
+ /**
9
+ * Type guard to check if an object is a FilterGroup
10
+ */
11
+ function isFilterGroup(obj) {
12
+ return obj && 'conditions' in obj && 'operator' in obj;
13
+ }
14
+ /**
15
+ * Feature for handling cross-filter operations on queries
16
+ */
17
+ export class CrossFilteringFeature {
18
+ constructor(builder) {
19
+ this.builder = builder;
20
+ }
21
+ /**
22
+ * Applies a set of cross filters to the query
23
+ * @param crossFilter - An instance of CrossFilter containing shared filter conditions
24
+ * @returns Updated query config
25
+ */
26
+ applyCrossFilters(crossFilter) {
27
+ const filterGroup = crossFilter.getConditions();
28
+ if (filterGroup.conditions.length === 0) {
29
+ return this.builder.getConfig();
30
+ }
31
+ // Apply conditions based on filter group operator
32
+ if (filterGroup.operator === 'AND') {
33
+ // For AND groups, apply each condition directly
34
+ this.applyAndConditions(filterGroup.conditions);
35
+ }
36
+ else {
37
+ // For OR groups, special handling to ensure proper parentheses
38
+ // We use whereGroup instead of orWhereGroup here since this is a top-level group
39
+ this.builder.whereGroup(builder => {
40
+ this.applyOrConditions(filterGroup.conditions, builder);
41
+ });
42
+ }
43
+ return this.builder.getConfig();
44
+ }
45
+ /**
46
+ * Apply AND conditions - each condition is applied with WHERE
47
+ */
48
+ applyAndConditions(conditions) {
49
+ conditions.forEach(condition => {
50
+ if (isFilterCondition(condition)) {
51
+ // Simple condition - apply with WHERE
52
+ this.builder.where(condition.column, condition.operator, condition.value);
53
+ }
54
+ else if (isFilterGroup(condition)) {
55
+ // Nested group
56
+ if (condition.operator === 'AND') {
57
+ // AND subgroup - apply all conditions
58
+ this.builder.whereGroup(builder => {
59
+ const feature = new CrossFilteringFeature(builder);
60
+ feature.applyAndConditions(condition.conditions);
61
+ });
62
+ }
63
+ else {
64
+ // OR subgroup within AND - needs special parentheses handling
65
+ this.builder.whereGroup(builder => {
66
+ const feature = new CrossFilteringFeature(builder);
67
+ feature.applyOrConditions(condition.conditions, builder);
68
+ });
69
+ }
70
+ }
71
+ });
72
+ }
73
+ /**
74
+ * Apply direct OR conditions without adding extra conjunctions
75
+ * @param conditions The conditions to apply
76
+ * @param builder The builder to apply conditions to, defaults to this.builder
77
+ */
78
+ applyOrConditions(conditions, builder = this.builder) {
79
+ if (conditions.length === 0)
80
+ return;
81
+ // Handle first condition
82
+ const firstCondition = conditions[0];
83
+ if (isFilterCondition(firstCondition)) {
84
+ builder.where(firstCondition.column, firstCondition.operator, firstCondition.value);
85
+ }
86
+ else if (isFilterGroup(firstCondition)) {
87
+ // Handle nested group
88
+ if (firstCondition.operator === 'AND') {
89
+ builder.whereGroup(innerBuilder => {
90
+ const feature = new CrossFilteringFeature(innerBuilder);
91
+ feature.applyAndConditions(firstCondition.conditions);
92
+ });
93
+ }
94
+ else {
95
+ builder.whereGroup(innerBuilder => {
96
+ const feature = new CrossFilteringFeature(innerBuilder);
97
+ feature.applyOrConditions(firstCondition.conditions, innerBuilder);
98
+ });
99
+ }
100
+ }
101
+ // Handle remaining conditions
102
+ for (let i = 1; i < conditions.length; i++) {
103
+ const condition = conditions[i];
104
+ if (isFilterCondition(condition)) {
105
+ builder.orWhere(condition.column, condition.operator, condition.value);
106
+ }
107
+ else if (isFilterGroup(condition)) {
108
+ if (condition.operator === 'AND') {
109
+ builder.orWhereGroup(innerBuilder => {
110
+ const feature = new CrossFilteringFeature(innerBuilder);
111
+ feature.applyAndConditions(condition.conditions);
112
+ });
113
+ }
114
+ else {
115
+ builder.orWhereGroup(innerBuilder => {
116
+ const feature = new CrossFilteringFeature(innerBuilder);
117
+ feature.applyOrConditions(condition.conditions, innerBuilder);
118
+ });
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,19 @@
1
+ import { QueryBuilder } from '../query-builder';
2
+ import { ColumnType } from '../../types';
3
+ export declare class ExecutorFeature<Schema extends {
4
+ [tableName: string]: {
5
+ [columnName: string]: ColumnType;
6
+ };
7
+ }, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
8
+ private builder;
9
+ constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
10
+ toSQLWithParams(): {
11
+ sql: string;
12
+ parameters: any[];
13
+ };
14
+ toSQL(): string;
15
+ execute(): Promise<T[]>;
16
+ stream(): Promise<ReadableStream<T[]>>;
17
+ private toSQLWithoutParameters;
18
+ }
19
+ //# sourceMappingURL=executor.d.ts.map
@@ -1,6 +1,6 @@
1
- import { ClickHouseConnection } from '../connection';
2
- import { substituteParameters } from '../utils';
3
- import { logger } from '../utils/logger';
1
+ import { ClickHouseConnection } from '../connection.js';
2
+ import { substituteParameters } from '../utils.js';
3
+ import { logger } from '../utils/logger.js';
4
4
  export class ExecutorFeature {
5
5
  constructor(builder) {
6
6
  this.builder = builder;
@@ -0,0 +1,95 @@
1
+ import { QueryBuilder } from '../query-builder';
2
+ import { ColumnType, FilterOperator, TableColumn } from '../../types';
3
+ export declare class FilteringFeature<Schema extends {
4
+ [tableName: string]: {
5
+ [columnName: string]: ColumnType;
6
+ };
7
+ }, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
8
+ private builder;
9
+ constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
10
+ addCondition<K extends keyof OriginalT | TableColumn<Schema>>(conjunction: 'AND' | 'OR', column: K | K[], operator: FilterOperator, value: any): {
11
+ where: import("../../types").WhereCondition[];
12
+ parameters: any[];
13
+ select?: (string | keyof T)[] | undefined;
14
+ groupBy?: string[];
15
+ having?: string[];
16
+ limit?: number;
17
+ offset?: number;
18
+ distinct?: boolean;
19
+ orderBy?: {
20
+ column: TableColumn<Schema> | keyof T;
21
+ direction: import("../../types").OrderDirection;
22
+ }[] | undefined;
23
+ joins?: import("../../types").JoinClause[];
24
+ ctes?: string[];
25
+ unionQueries?: string[];
26
+ settings?: string;
27
+ };
28
+ /**
29
+ * Adds a group-start marker to start a parenthesized group of conditions with AND conjunction
30
+ * @returns The updated query config
31
+ */
32
+ startWhereGroup(): {
33
+ where: import("../../types").WhereCondition[];
34
+ select?: (string | keyof T)[] | undefined;
35
+ groupBy?: string[];
36
+ having?: string[];
37
+ limit?: number;
38
+ offset?: number;
39
+ distinct?: boolean;
40
+ orderBy?: {
41
+ column: TableColumn<Schema> | keyof T;
42
+ direction: import("../../types").OrderDirection;
43
+ }[] | undefined;
44
+ joins?: import("../../types").JoinClause[];
45
+ parameters?: any[];
46
+ ctes?: string[];
47
+ unionQueries?: string[];
48
+ settings?: string;
49
+ };
50
+ /**
51
+ * Adds a group-start marker to start a parenthesized group of conditions with OR conjunction
52
+ * @returns The updated query config
53
+ */
54
+ startOrWhereGroup(): {
55
+ where: import("../../types").WhereCondition[];
56
+ select?: (string | keyof T)[] | undefined;
57
+ groupBy?: string[];
58
+ having?: string[];
59
+ limit?: number;
60
+ offset?: number;
61
+ distinct?: boolean;
62
+ orderBy?: {
63
+ column: TableColumn<Schema> | keyof T;
64
+ direction: import("../../types").OrderDirection;
65
+ }[] | undefined;
66
+ joins?: import("../../types").JoinClause[];
67
+ parameters?: any[];
68
+ ctes?: string[];
69
+ unionQueries?: string[];
70
+ settings?: string;
71
+ };
72
+ /**
73
+ * Adds a group-end marker to end a parenthesized group of conditions
74
+ * @returns The updated query config
75
+ */
76
+ endWhereGroup(): {
77
+ where: import("../../types").WhereCondition[];
78
+ select?: (string | keyof T)[] | undefined;
79
+ groupBy?: string[];
80
+ having?: string[];
81
+ limit?: number;
82
+ offset?: number;
83
+ distinct?: boolean;
84
+ orderBy?: {
85
+ column: TableColumn<Schema> | keyof T;
86
+ direction: import("../../types").OrderDirection;
87
+ }[] | undefined;
88
+ joins?: import("../../types").JoinClause[];
89
+ parameters?: any[];
90
+ ctes?: string[];
91
+ unionQueries?: string[];
92
+ settings?: string;
93
+ };
94
+ }
95
+ //# sourceMappingURL=filtering.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"filtering.d.ts","sourceRoot":"","sources":["../../../src/core/features/filtering.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtE,qBAAa,gBAAgB,CAC3B,MAAM,SAAS;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;CAAE,EAC5E,CAAC,EACD,SAAS,SAAS,OAAO,GAAG,KAAK,EACjC,YAAY,GAAG,EAAE,EACjB,SAAS,GAAG,CAAC;IAED,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC;IAExF,YAAY,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,EAC1D,WAAW,EAAE,KAAK,GAAG,IAAI,EACzB,MAAM,EAAE,CAAC,EACT,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,GAAG;;;;;;;;;;;;;;;;;;CA6Bb"}
1
+ {"version":3,"file":"filtering.d.ts","sourceRoot":"","sources":["../../../src/core/features/filtering.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtE,qBAAa,gBAAgB,CAC3B,MAAM,SAAS;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;CAAE,EAC5E,CAAC,EACD,SAAS,SAAS,OAAO,GAAG,KAAK,EACjC,YAAY,GAAG,EAAE,EACjB,SAAS,GAAG,CAAC;IAED,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC;IAExF,YAAY,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,EAC1D,WAAW,EAAE,KAAK,GAAG,IAAI,EACzB,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,EACf,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,GAAG;;;;;;;;;;;;;;;;;;IA4DZ;;;OAGG;IACH,eAAe;;;;;;;;;;;;;;;;;;IAkBf;;;OAGG;IACH,iBAAiB;;;;;;;;;;;;;;;;;;IAkBjB;;;OAGG;IACH,aAAa;;;;;;;;;;;;;;;;;;CAiBd"}
@@ -6,15 +6,44 @@ export class FilteringFeature {
6
6
  const config = this.builder.getConfig();
7
7
  const where = config.where || [];
8
8
  const parameters = config.parameters || [];
9
+ // Handle tuple columns
10
+ const columnString = Array.isArray(column)
11
+ ? `(${column.map(String).join(', ')})`
12
+ : String(column);
9
13
  where.push({
10
- column: String(column),
14
+ column: columnString,
11
15
  operator,
12
16
  value,
13
- conjunction
17
+ conjunction,
18
+ type: 'condition'
14
19
  });
15
- if (operator === 'in' || operator === 'notIn') {
20
+ // Handle different parameter types based on operator
21
+ if (operator === 'in' || operator === 'notIn' || operator === 'globalIn' || operator === 'globalNotIn') {
22
+ if (!Array.isArray(value)) {
23
+ throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
24
+ }
16
25
  parameters.push(...value);
17
26
  }
27
+ else if (operator === 'inTuple' || operator === 'globalInTuple') {
28
+ if (!Array.isArray(value)) {
29
+ throw new Error(`Expected an array of tuples for ${operator} operator, but got ${typeof value}`);
30
+ }
31
+ value.forEach((tuple) => {
32
+ parameters.push(...tuple);
33
+ });
34
+ }
35
+ else if (operator === 'inSubquery' || operator === 'globalInSubquery') {
36
+ if (typeof value !== 'string') {
37
+ throw new Error(`Expected a string (subquery) for ${operator} operator, but got ${typeof value}`);
38
+ }
39
+ // No parameters
40
+ }
41
+ else if (operator === 'inTable' || operator === 'globalInTable') {
42
+ if (typeof value !== 'string') {
43
+ throw new Error(`Expected a string (table name) for ${operator} operator, but got ${typeof value}`);
44
+ }
45
+ // No parameters
46
+ }
18
47
  else if (operator === 'between') {
19
48
  parameters.push(value[0], value[1]);
20
49
  }
@@ -27,4 +56,61 @@ export class FilteringFeature {
27
56
  parameters
28
57
  };
29
58
  }
59
+ /**
60
+ * Adds a group-start marker to start a parenthesized group of conditions with AND conjunction
61
+ * @returns The updated query config
62
+ */
63
+ startWhereGroup() {
64
+ const config = this.builder.getConfig();
65
+ const where = config.where || [];
66
+ where.push({
67
+ column: '', // Not used for group markers
68
+ operator: 'eq', // Not used for group markers
69
+ value: null, // Not used for group markers
70
+ conjunction: 'AND',
71
+ type: 'group-start'
72
+ });
73
+ return {
74
+ ...config,
75
+ where
76
+ };
77
+ }
78
+ /**
79
+ * Adds a group-start marker to start a parenthesized group of conditions with OR conjunction
80
+ * @returns The updated query config
81
+ */
82
+ startOrWhereGroup() {
83
+ const config = this.builder.getConfig();
84
+ const where = config.where || [];
85
+ where.push({
86
+ column: '', // Not used for group markers
87
+ operator: 'eq', // Not used for group markers
88
+ value: null, // Not used for group markers
89
+ conjunction: 'OR',
90
+ type: 'group-start'
91
+ });
92
+ return {
93
+ ...config,
94
+ where
95
+ };
96
+ }
97
+ /**
98
+ * Adds a group-end marker to end a parenthesized group of conditions
99
+ * @returns The updated query config
100
+ */
101
+ endWhereGroup() {
102
+ const config = this.builder.getConfig();
103
+ const where = config.where || [];
104
+ where.push({
105
+ column: '', // Not used for group markers
106
+ operator: 'eq', // Not used for group markers
107
+ value: null, // Not used for group markers
108
+ conjunction: 'AND', // Not relevant for end markers
109
+ type: 'group-end'
110
+ });
111
+ return {
112
+ ...config,
113
+ where
114
+ };
115
+ }
30
116
  }
@@ -0,0 +1,29 @@
1
+ import { QueryBuilder } from '../query-builder';
2
+ import { ColumnType, JoinType } from '../../types';
3
+ export declare class JoinFeature<Schema extends {
4
+ [tableName: string]: {
5
+ [columnName: string]: ColumnType;
6
+ };
7
+ }, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
8
+ private builder;
9
+ constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
10
+ addJoin<TableName extends keyof Schema>(type: JoinType, table: TableName, leftColumn: keyof OriginalT, rightColumn: `${TableName & string}.${keyof Schema[TableName] & string}`, alias?: string): {
11
+ joins: import("../../types").JoinClause[];
12
+ select?: (string | keyof T)[] | undefined;
13
+ where?: import("../../types").WhereCondition[];
14
+ groupBy?: string[];
15
+ having?: string[];
16
+ limit?: number;
17
+ offset?: number;
18
+ distinct?: boolean;
19
+ orderBy?: {
20
+ column: keyof T | import("../../types").TableColumn<Schema>;
21
+ direction: import("../../types").OrderDirection;
22
+ }[] | undefined;
23
+ parameters?: any[];
24
+ ctes?: string[];
25
+ unionQueries?: string[];
26
+ settings?: string;
27
+ };
28
+ }
29
+ //# sourceMappingURL=joins.d.ts.map
@@ -0,0 +1,23 @@
1
+ import { QueryBuilder } from '../query-builder';
2
+ import { ColumnType, PaginationOptions, PaginatedResult } from '../../types';
3
+ export declare class PaginationFeature<Schema extends {
4
+ [tableName: string]: {
5
+ [columnName: string]: ColumnType;
6
+ };
7
+ }, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
8
+ private builder;
9
+ private static cursorStacks;
10
+ private stackKey;
11
+ constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
12
+ private get cursorStack();
13
+ private set cursorStack(value);
14
+ private get currentPosition();
15
+ private set currentPosition(value);
16
+ private encodeCursor;
17
+ private decodeCursor;
18
+ paginate(options: PaginationOptions<T>): Promise<PaginatedResult<T>>;
19
+ private generateCursor;
20
+ firstPage(pageSize: number): Promise<PaginatedResult<T>>;
21
+ iteratePages(pageSize: number): AsyncGenerator<PaginatedResult<T>>;
22
+ }
23
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1,119 @@
1
+ import { QueryBuilder } from '../query-builder';
2
+ import { ColumnType, OrderDirection, TableColumn } from '../../types';
3
+ export declare class QueryModifiersFeature<Schema extends {
4
+ [tableName: string]: {
5
+ [columnName: string]: ColumnType;
6
+ };
7
+ }, T, HasSelect extends boolean = false, Aggregations = {}, OriginalT = T> {
8
+ private builder;
9
+ constructor(builder: QueryBuilder<Schema, T, HasSelect, Aggregations, OriginalT>);
10
+ addGroupBy(columns: (keyof T | TableColumn<Schema>) | Array<keyof T | TableColumn<Schema>>): {
11
+ groupBy: string[];
12
+ select?: (string | keyof T)[] | undefined;
13
+ where?: import("../../types").WhereCondition[];
14
+ having?: string[];
15
+ limit?: number;
16
+ offset?: number;
17
+ distinct?: boolean;
18
+ orderBy?: {
19
+ column: TableColumn<Schema> | keyof T;
20
+ direction: OrderDirection;
21
+ }[] | undefined;
22
+ joins?: import("../../types").JoinClause[];
23
+ parameters?: any[];
24
+ ctes?: string[];
25
+ unionQueries?: string[];
26
+ settings?: string;
27
+ };
28
+ addLimit(count: number): {
29
+ limit: number;
30
+ select?: (string | keyof T)[] | undefined;
31
+ where?: import("../../types").WhereCondition[];
32
+ groupBy?: string[];
33
+ having?: string[];
34
+ offset?: number;
35
+ distinct?: boolean;
36
+ orderBy?: {
37
+ column: TableColumn<Schema> | keyof T;
38
+ direction: OrderDirection;
39
+ }[] | undefined;
40
+ joins?: import("../../types").JoinClause[];
41
+ parameters?: any[];
42
+ ctes?: string[];
43
+ unionQueries?: string[];
44
+ settings?: string;
45
+ };
46
+ addOffset(count: number): {
47
+ offset: number;
48
+ select?: (string | keyof T)[] | undefined;
49
+ where?: import("../../types").WhereCondition[];
50
+ groupBy?: string[];
51
+ having?: string[];
52
+ limit?: number;
53
+ distinct?: boolean;
54
+ orderBy?: {
55
+ column: TableColumn<Schema> | keyof T;
56
+ direction: OrderDirection;
57
+ }[] | undefined;
58
+ joins?: import("../../types").JoinClause[];
59
+ parameters?: any[];
60
+ ctes?: string[];
61
+ unionQueries?: string[];
62
+ settings?: string;
63
+ };
64
+ addOrderBy<K extends keyof T | TableColumn<Schema>>(column: K, direction?: OrderDirection): {
65
+ orderBy: {
66
+ column: TableColumn<Schema> | keyof T;
67
+ direction: OrderDirection;
68
+ }[];
69
+ select?: (string | keyof T)[] | undefined;
70
+ where?: import("../../types").WhereCondition[];
71
+ groupBy?: string[];
72
+ having?: string[];
73
+ limit?: number;
74
+ offset?: number;
75
+ distinct?: boolean;
76
+ joins?: import("../../types").JoinClause[];
77
+ parameters?: any[];
78
+ ctes?: string[];
79
+ unionQueries?: string[];
80
+ settings?: string;
81
+ };
82
+ addHaving(condition: string, parameters?: any[]): {
83
+ having: string[];
84
+ parameters: any[] | undefined;
85
+ select?: (string | keyof T)[] | undefined;
86
+ where?: import("../../types").WhereCondition[];
87
+ groupBy?: string[];
88
+ limit?: number;
89
+ offset?: number;
90
+ distinct?: boolean;
91
+ orderBy?: {
92
+ column: TableColumn<Schema> | keyof T;
93
+ direction: OrderDirection;
94
+ }[] | undefined;
95
+ joins?: import("../../types").JoinClause[];
96
+ ctes?: string[];
97
+ unionQueries?: string[];
98
+ settings?: string;
99
+ };
100
+ setDistinct(): {
101
+ distinct: boolean;
102
+ select?: (string | keyof T)[] | undefined;
103
+ where?: import("../../types").WhereCondition[];
104
+ groupBy?: string[];
105
+ having?: string[];
106
+ limit?: number;
107
+ offset?: number;
108
+ orderBy?: {
109
+ column: TableColumn<Schema> | keyof T;
110
+ direction: OrderDirection;
111
+ }[] | undefined;
112
+ joins?: import("../../types").JoinClause[];
113
+ parameters?: any[];
114
+ ctes?: string[];
115
+ unionQueries?: string[];
116
+ settings?: string;
117
+ };
118
+ }
119
+ //# sourceMappingURL=query-modifiers.d.ts.map
@@ -0,0 +1,9 @@
1
+ import { QueryConfig } from '../../types';
2
+ export declare class SQLFormatter {
3
+ formatSelect(config: QueryConfig<any, any>): string;
4
+ formatGroupBy(config: QueryConfig<any, any>): string;
5
+ formatWhere(config: QueryConfig<any, any>): string;
6
+ private getSqlOperator;
7
+ formatJoins(config: QueryConfig<any, any>): string;
8
+ }
9
+ //# sourceMappingURL=sql-formatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sql-formatter.d.ts","sourceRoot":"","sources":["../../../src/core/formatters/sql-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAE1D,qBAAa,YAAY;IACvB,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMnD,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IASpD,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IA4BlD,OAAO,CAAC,cAAc;IActB,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;CAUnD"}
1
+ {"version":3,"file":"sql-formatter.d.ts","sourceRoot":"","sources":["../../../src/core/formatters/sql-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAE1D,qBAAa,YAAY;IACvB,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMnD,aAAa,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IASpD,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IA6FlD,OAAO,CAAC,cAAc;IActB,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;CAUnD"}
@@ -17,10 +17,26 @@ export class SQLFormatter {
17
17
  formatWhere(config) {
18
18
  if (!config.where?.length)
19
19
  return '';
20
- return config.where
21
- .map((condition, index) => {
20
+ let afterGroupStart = false; // Track whether we're immediately after a group-start
21
+ // First pass - generate the SQL fragments for each condition
22
+ const fragments = config.where.map((condition, index) => {
23
+ // Handle special group markers
24
+ if (condition.type === 'group-start') {
25
+ const prefix = index === 0 ? '' : ` ${condition.conjunction} `;
26
+ afterGroupStart = true; // Mark that the next condition follows a group-start
27
+ return `${prefix}(`.trim();
28
+ }
29
+ if (condition.type === 'group-end') {
30
+ afterGroupStart = false; // Reset the flag after group-end
31
+ return ')';
32
+ }
33
+ // Normal conditions
22
34
  const { column, operator, value, conjunction } = condition;
23
- const prefix = index === 0 ? '' : ` ${conjunction} `;
35
+ // Don't add conjunction if it's the first condition or right after a group-start
36
+ const prefix = index === 0 || afterGroupStart ? '' : ` ${conjunction} `;
37
+ // Reset the afterGroupStart flag
38
+ afterGroupStart = false;
39
+ // Handle IN operators
24
40
  if (operator === 'in' || operator === 'notIn') {
25
41
  if (!Array.isArray(value)) {
26
42
  throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
@@ -31,6 +47,42 @@ export class SQLFormatter {
31
47
  const placeholders = value.map(() => '?').join(', ');
32
48
  return `${prefix}${column} ${operator === 'in' ? 'IN' : 'NOT IN'} (${placeholders})`.trim();
33
49
  }
50
+ // Handle GLOBAL IN operators
51
+ else if (operator === 'globalIn' || operator === 'globalNotIn') {
52
+ if (!Array.isArray(value)) {
53
+ throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
54
+ }
55
+ if (value.length === 0) {
56
+ return `${prefix}1 = 0`;
57
+ }
58
+ const placeholders = value.map(() => '?').join(', ');
59
+ return `${prefix}${column} ${operator === 'globalIn' ? 'GLOBAL IN' : 'GLOBAL NOT IN'} (${placeholders})`.trim();
60
+ }
61
+ // Handle subquery IN operators
62
+ else if (operator === 'inSubquery' || operator === 'globalInSubquery') {
63
+ if (typeof value !== 'string') {
64
+ throw new Error(`Expected a string (subquery) for ${operator} operator, but got ${typeof value}`);
65
+ }
66
+ return `${prefix}${column} ${operator === 'inSubquery' ? 'IN' : 'GLOBAL IN'} (${value})`.trim();
67
+ }
68
+ // Handle table reference IN operators
69
+ else if (operator === 'inTable' || operator === 'globalInTable') {
70
+ if (typeof value !== 'string') {
71
+ throw new Error(`Expected a string (table name) for ${operator} operator, but got ${typeof value}`);
72
+ }
73
+ return `${prefix}${column} ${operator === 'inTable' ? 'IN' : 'GLOBAL IN'} ${value}`.trim();
74
+ }
75
+ // Handle tuple IN operators
76
+ else if (operator === 'inTuple' || operator === 'globalInTuple') {
77
+ if (!Array.isArray(value)) {
78
+ throw new Error(`Expected an array of tuples for ${operator} operator, but got ${typeof value}`);
79
+ }
80
+ if (value.length === 0) {
81
+ return `${prefix}1 = 0`;
82
+ }
83
+ const placeholders = value.map(() => '(?, ?)').join(', ');
84
+ return `${prefix}${column} ${operator === 'inTuple' ? 'IN' : 'GLOBAL IN'} (${placeholders})`.trim();
85
+ }
34
86
  else if (operator === 'between') {
35
87
  return `${prefix}${column} BETWEEN ? AND ?`.trim();
36
88
  }
@@ -40,8 +92,12 @@ export class SQLFormatter {
40
92
  else {
41
93
  return `${prefix}${column} ${this.getSqlOperator(operator)} ?`.trim();
42
94
  }
43
- })
44
- .join(' ');
95
+ });
96
+ // Join fragments and then remove extra spaces around parentheses
97
+ let result = fragments.join(' ');
98
+ // Replace "( " with "(" and " )" with ")"
99
+ result = result.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')');
100
+ return result;
45
101
  }
46
102
  getSqlOperator(operator) {
47
103
  switch (operator) {