@hypequery/clickhouse 1.6.1 → 2.0.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 (141) hide show
  1. package/README-CLI.md +43 -88
  2. package/README.md +84 -253
  3. package/dist/cli/bin.js +16 -8
  4. package/dist/cli/generate-types.js +3 -81
  5. package/dist/cli/type-parsing.js +124 -0
  6. package/dist/core/adapters/clickhouse-adapter.d.ts.map +1 -1
  7. package/dist/core/adapters/clickhouse-adapter.js +3 -2
  8. package/dist/core/cache/cache-manager.d.ts.map +1 -1
  9. package/dist/core/cache/cache-manager.js +5 -3
  10. package/dist/core/connection.d.ts +6 -6
  11. package/dist/core/connection.js +9 -9
  12. package/dist/core/cross-filter.js +1 -1
  13. package/dist/core/dialects/clickhouse-dialect.d.ts +2 -2
  14. package/dist/core/dialects/clickhouse-dialect.d.ts.map +1 -1
  15. package/dist/core/dialects/clickhouse-dialect.js +39 -22
  16. package/dist/core/dialects/sql-dialect.d.ts +2 -2
  17. package/dist/core/dialects/sql-dialect.d.ts.map +1 -1
  18. package/dist/core/env/auto-client.d.ts.map +1 -1
  19. package/dist/core/env/auto-client.js +1 -1
  20. package/dist/core/features/aggregations.d.ts +7 -90
  21. package/dist/core/features/aggregations.d.ts.map +1 -1
  22. package/dist/core/features/aggregations.js +19 -7
  23. package/dist/core/features/analytics.d.ts +5 -870
  24. package/dist/core/features/analytics.d.ts.map +1 -1
  25. package/dist/core/features/analytics.js +15 -13
  26. package/dist/core/features/cross-filtering.d.ts +1 -1
  27. package/dist/core/features/cross-filtering.d.ts.map +1 -1
  28. package/dist/core/features/cross-filtering.js +28 -73
  29. package/dist/core/features/executor.d.ts +1 -1
  30. package/dist/core/features/executor.d.ts.map +1 -1
  31. package/dist/core/features/executor.js +9 -11
  32. package/dist/core/features/filtering.d.ts +5 -91
  33. package/dist/core/features/filtering.d.ts.map +1 -1
  34. package/dist/core/features/filtering.js +63 -77
  35. package/dist/core/features/joins.d.ts +2 -19
  36. package/dist/core/features/joins.d.ts.map +1 -1
  37. package/dist/core/features/joins.js +16 -5
  38. package/dist/core/features/query-modifiers.d.ts +10 -109
  39. package/dist/core/features/query-modifiers.d.ts.map +1 -1
  40. package/dist/core/features/query-modifiers.js +64 -18
  41. package/dist/core/formatters/sql-formatter.d.ts +16 -5
  42. package/dist/core/formatters/sql-formatter.d.ts.map +1 -1
  43. package/dist/core/formatters/sql-formatter.js +197 -93
  44. package/dist/core/join-relationships.d.ts +22 -5
  45. package/dist/core/join-relationships.d.ts.map +1 -1
  46. package/dist/core/join-relationships.js +1 -1
  47. package/dist/core/query-builder.d.ts +63 -12
  48. package/dist/core/query-builder.d.ts.map +1 -1
  49. package/dist/core/query-builder.js +210 -153
  50. package/dist/core/query-node.d.ts +7 -0
  51. package/dist/core/query-node.d.ts.map +1 -0
  52. package/dist/core/query-node.js +80 -0
  53. package/dist/core/tests/integration/setup.d.ts +1 -0
  54. package/dist/core/tests/integration/setup.d.ts.map +1 -1
  55. package/dist/core/tests/integration/setup.js +4 -2
  56. package/dist/core/types/select-types.d.ts +3 -0
  57. package/dist/core/types/select-types.d.ts.map +1 -1
  58. package/dist/core/utils/connection-endpoint.d.ts +3 -0
  59. package/dist/core/utils/connection-endpoint.d.ts.map +1 -0
  60. package/dist/core/utils/connection-endpoint.js +9 -0
  61. package/dist/core/utils/filter-application.d.ts +15 -0
  62. package/dist/core/utils/filter-application.d.ts.map +1 -0
  63. package/dist/core/utils/filter-application.js +32 -0
  64. package/dist/core/utils/query-config-compat.d.ts +48 -0
  65. package/dist/core/utils/query-config-compat.d.ts.map +1 -0
  66. package/dist/core/utils/query-config-compat.js +137 -0
  67. package/dist/core/utils/relation-application.d.ts +9 -0
  68. package/dist/core/utils/relation-application.d.ts.map +1 -0
  69. package/dist/core/utils/relation-application.js +19 -0
  70. package/dist/core/utils/relation-validation.d.ts +6 -0
  71. package/dist/core/utils/relation-validation.d.ts.map +1 -0
  72. package/dist/core/utils/relation-validation.js +29 -0
  73. package/dist/core/utils/sql-expressions.d.ts +14 -0
  74. package/dist/core/utils/sql-expressions.d.ts.map +1 -1
  75. package/dist/core/utils/sql-expressions.js +40 -0
  76. package/dist/core/utils/tuple-filter-validation.d.ts +3 -0
  77. package/dist/core/utils/tuple-filter-validation.d.ts.map +1 -0
  78. package/dist/core/utils/tuple-filter-validation.js +16 -0
  79. package/dist/index.d.ts +2 -11
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +3 -7
  82. package/dist/types/base.d.ts +88 -22
  83. package/dist/types/base.d.ts.map +1 -1
  84. package/dist/types/clickhouse-types.d.ts +5 -2
  85. package/dist/types/clickhouse-types.d.ts.map +1 -1
  86. package/dist/types/filters.d.ts +9 -5
  87. package/dist/types/filters.d.ts.map +1 -1
  88. package/dist/types/index.d.ts +1 -0
  89. package/dist/types/index.d.ts.map +1 -1
  90. package/dist/types/index.js +1 -0
  91. package/dist/types/type-helpers.d.ts +7 -0
  92. package/dist/types/type-helpers.d.ts.map +1 -0
  93. package/package.json +5 -5
  94. package/dist/migrations/config/index.d.ts +0 -3
  95. package/dist/migrations/config/index.d.ts.map +0 -1
  96. package/dist/migrations/config/index.js +0 -1
  97. package/dist/migrations/config/types.d.ts +0 -45
  98. package/dist/migrations/config/types.d.ts.map +0 -1
  99. package/dist/migrations/config/types.js +0 -28
  100. package/dist/migrations/diff/diff.d.ts +0 -11
  101. package/dist/migrations/diff/diff.d.ts.map +0 -1
  102. package/dist/migrations/diff/diff.js +0 -240
  103. package/dist/migrations/diff/index.d.ts +0 -3
  104. package/dist/migrations/diff/index.d.ts.map +0 -1
  105. package/dist/migrations/diff/index.js +0 -1
  106. package/dist/migrations/diff/types.d.ts +0 -74
  107. package/dist/migrations/diff/types.d.ts.map +0 -1
  108. package/dist/migrations/schema/column.d.ts +0 -71
  109. package/dist/migrations/schema/column.d.ts.map +0 -1
  110. package/dist/migrations/schema/column.js +0 -123
  111. package/dist/migrations/schema/define.d.ts +0 -24
  112. package/dist/migrations/schema/define.d.ts.map +0 -1
  113. package/dist/migrations/schema/define.js +0 -47
  114. package/dist/migrations/schema/index.d.ts +0 -4
  115. package/dist/migrations/schema/index.d.ts.map +0 -1
  116. package/dist/migrations/schema/index.js +0 -2
  117. package/dist/migrations/schema/types.d.ts +0 -74
  118. package/dist/migrations/schema/types.d.ts.map +0 -1
  119. package/dist/migrations/schema/types.js +0 -1
  120. package/dist/migrations/snapshot/index.d.ts +0 -3
  121. package/dist/migrations/snapshot/index.d.ts.map +0 -1
  122. package/dist/migrations/snapshot/index.js +0 -1
  123. package/dist/migrations/snapshot/serialize.d.ts +0 -21
  124. package/dist/migrations/snapshot/serialize.d.ts.map +0 -1
  125. package/dist/migrations/snapshot/serialize.js +0 -127
  126. package/dist/migrations/snapshot/types.d.ts +0 -47
  127. package/dist/migrations/snapshot/types.d.ts.map +0 -1
  128. package/dist/migrations/snapshot/types.js +0 -1
  129. package/dist/migrations/sql/index.d.ts +0 -4
  130. package/dist/migrations/sql/index.d.ts.map +0 -1
  131. package/dist/migrations/sql/index.js +0 -2
  132. package/dist/migrations/sql/render.d.ts +0 -11
  133. package/dist/migrations/sql/render.d.ts.map +0 -1
  134. package/dist/migrations/sql/render.js +0 -334
  135. package/dist/migrations/sql/types.d.ts +0 -48
  136. package/dist/migrations/sql/types.d.ts.map +0 -1
  137. package/dist/migrations/sql/types.js +0 -1
  138. package/dist/migrations/sql/write.d.ts +0 -9
  139. package/dist/migrations/sql/write.d.ts.map +0 -1
  140. package/dist/migrations/sql/write.js +0 -31
  141. /package/dist/{migrations/diff/types.js → types/type-helpers.js} +0 -0
@@ -1,116 +1,17 @@
1
1
  import type { BuilderState, SchemaDefinition } from '../types/builder-state.js';
2
2
  import { QueryBuilder } from '../query-builder.js';
3
- import { OrderDirection } from '../../types/index.js';
3
+ import { OrderDirection, type SelectQueryNode } from '../../types/index.js';
4
4
  export declare class QueryModifiersFeature<Schema extends SchemaDefinition<Schema>, State extends BuilderState<Schema, string, any, keyof Schema, Partial<Record<string, keyof Schema>>>> {
5
5
  private builder;
6
6
  constructor(builder: QueryBuilder<Schema, State>);
7
- addGroupBy(columns: string | string[]): {
8
- groupBy: string[];
9
- select?: (string | keyof State["output"])[] | undefined;
10
- where?: import("../../types/base.js").WhereCondition[];
11
- having?: string[];
12
- limit?: number;
13
- offset?: number;
14
- distinct?: boolean;
15
- orderBy?: {
16
- column: keyof State["output"] | import("../../types/schema.js").TableColumn<Schema>;
17
- direction: OrderDirection;
18
- }[] | undefined;
19
- joins?: import("../../types/base.js").JoinClause[];
20
- parameters?: any[];
21
- ctes?: string[];
22
- unionQueries?: string[];
23
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
24
- };
25
- addLimit(count: number): {
26
- limit: number;
27
- select?: (string | keyof State["output"])[] | undefined;
28
- where?: import("../../types/base.js").WhereCondition[];
29
- groupBy?: string[];
30
- having?: string[];
31
- offset?: number;
32
- distinct?: boolean;
33
- orderBy?: {
34
- column: keyof State["output"] | import("../../types/schema.js").TableColumn<Schema>;
35
- direction: OrderDirection;
36
- }[] | undefined;
37
- joins?: import("../../types/base.js").JoinClause[];
38
- parameters?: any[];
39
- ctes?: string[];
40
- unionQueries?: string[];
41
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
42
- };
43
- addOffset(count: number): {
44
- offset: number;
45
- select?: (string | keyof State["output"])[] | undefined;
46
- where?: import("../../types/base.js").WhereCondition[];
47
- groupBy?: string[];
48
- having?: string[];
49
- limit?: number;
50
- distinct?: boolean;
51
- orderBy?: {
52
- column: keyof State["output"] | import("../../types/schema.js").TableColumn<Schema>;
53
- direction: OrderDirection;
54
- }[] | undefined;
55
- joins?: import("../../types/base.js").JoinClause[];
56
- parameters?: any[];
57
- ctes?: string[];
58
- unionQueries?: string[];
59
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
60
- };
61
- addOrderBy(column: string, direction?: OrderDirection): {
62
- orderBy: {
63
- column: keyof State["output"] | import("../../types/schema.js").TableColumn<Schema>;
64
- direction: OrderDirection;
65
- }[];
66
- select?: (string | keyof State["output"])[] | undefined;
67
- where?: import("../../types/base.js").WhereCondition[];
68
- groupBy?: string[];
69
- having?: string[];
70
- limit?: number;
71
- offset?: number;
72
- distinct?: boolean;
73
- joins?: import("../../types/base.js").JoinClause[];
74
- parameters?: any[];
75
- ctes?: string[];
76
- unionQueries?: string[];
77
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
78
- };
79
- addHaving(condition: string, parameters?: any[]): {
80
- having: string[];
81
- parameters: any[] | undefined;
82
- select?: (string | keyof State["output"])[] | undefined;
83
- where?: import("../../types/base.js").WhereCondition[];
84
- groupBy?: string[];
85
- limit?: number;
86
- offset?: number;
87
- distinct?: boolean;
88
- orderBy?: {
89
- column: keyof State["output"] | import("../../types/schema.js").TableColumn<Schema>;
90
- direction: OrderDirection;
91
- }[] | undefined;
92
- joins?: import("../../types/base.js").JoinClause[];
93
- ctes?: string[];
94
- unionQueries?: string[];
95
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
96
- };
97
- setDistinct(): {
98
- distinct: boolean;
99
- select?: (string | keyof State["output"])[] | undefined;
100
- where?: import("../../types/base.js").WhereCondition[];
101
- groupBy?: string[];
102
- having?: string[];
103
- limit?: number;
104
- offset?: number;
105
- orderBy?: {
106
- column: keyof State["output"] | import("../../types/schema.js").TableColumn<Schema>;
107
- direction: OrderDirection;
108
- }[] | undefined;
109
- joins?: import("../../types/base.js").JoinClause[];
110
- parameters?: any[];
111
- ctes?: string[];
112
- unionQueries?: string[];
113
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
114
- };
7
+ addGroupBy(columns: string | string[]): SelectQueryNode<State['output'], Schema>;
8
+ addArrayJoin(type: 'ARRAY' | 'LEFT ARRAY', expression: string): SelectQueryNode<State['output'], Schema>;
9
+ addLimit(count: number): SelectQueryNode<State['output'], Schema>;
10
+ addLimitBy(limit: number, by: string | string[]): SelectQueryNode<State['output'], Schema>;
11
+ addOffset(count: number): SelectQueryNode<State['output'], Schema>;
12
+ addOrderBy(column: string, direction?: OrderDirection): SelectQueryNode<State['output'], Schema>;
13
+ addHaving(condition: string, parameters?: any[]): SelectQueryNode<State['output'], Schema>;
14
+ setDistinct(): SelectQueryNode<State['output'], Schema>;
15
+ setWithTotals(): SelectQueryNode<State['output'], Schema>;
115
16
  }
116
17
  //# sourceMappingURL=query-modifiers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query-modifiers.d.ts","sourceRoot":"","sources":["../../../src/core/features/query-modifiers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,qBAAa,qBAAqB,CAChC,MAAM,SAAS,gBAAgB,CAAC,MAAM,CAAC,EACvC,KAAK,SAAS,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC;IAExF,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAExD,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;;;;;;;;;;;;;;;;;;IAQrC,QAAQ,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;IAQtB,SAAS,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;IAQvB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,cAAsB;;;;;;;;;;;;;;;;;;IAQ5D,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,GAAG,EAAE;;;;;;;;;;;;;;;;;;IAY/C,WAAW;;;;;;;;;;;;;;;;;;CAOZ"}
1
+ {"version":3,"file":"query-modifiers.d.ts","sourceRoot":"","sources":["../../../src/core/features/query-modifiers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5E,qBAAa,qBAAqB,CAChC,MAAM,SAAS,gBAAgB,CAAC,MAAM,CAAC,EACvC,KAAK,SAAS,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC;IAExF,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAExD,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAgBhF,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAexG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAQjE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAY1F,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAQlE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,cAAsB,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAQvG,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,GAAG,EAAE,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAiB1F,WAAW,IAAI,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAQvD,aAAa,IAAI,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAO1D"}
@@ -4,48 +4,94 @@ export class QueryModifiersFeature {
4
4
  this.builder = builder;
5
5
  }
6
6
  addGroupBy(columns) {
7
- const config = this.builder.getConfig();
7
+ const query = this.builder.getQueryNode();
8
+ const existingExpressions = new Set((query.groupBy || []).map(item => item.expression));
9
+ const groupByItems = (Array.isArray(columns) ? columns.map(String) : [String(columns)])
10
+ .filter(expression => {
11
+ if (existingExpressions.has(expression))
12
+ return false;
13
+ existingExpressions.add(expression);
14
+ return true;
15
+ })
16
+ .map(expression => ({ kind: 'group-by-item', expression }));
8
17
  return {
9
- ...config,
10
- groupBy: Array.isArray(columns) ? columns.map(String) : [String(columns)]
18
+ ...query,
19
+ groupBy: [...(query.groupBy || []), ...groupByItems]
20
+ };
21
+ }
22
+ addArrayJoin(type, expression) {
23
+ const query = this.builder.getQueryNode();
24
+ return {
25
+ ...query,
26
+ arrayJoins: [
27
+ ...(query.arrayJoins || []),
28
+ {
29
+ kind: 'array-join',
30
+ type,
31
+ expression,
32
+ },
33
+ ],
11
34
  };
12
35
  }
13
36
  addLimit(count) {
14
- const config = this.builder.getConfig();
37
+ const query = this.builder.getQueryNode();
15
38
  return {
16
- ...config,
39
+ ...query,
17
40
  limit: count
18
41
  };
19
42
  }
43
+ addLimitBy(limit, by) {
44
+ const query = this.builder.getQueryNode();
45
+ return {
46
+ ...query,
47
+ limitBy: {
48
+ kind: 'limit-by',
49
+ limit,
50
+ by: Array.isArray(by) ? by.map(String) : [String(by)],
51
+ },
52
+ };
53
+ }
20
54
  addOffset(count) {
21
- const config = this.builder.getConfig();
55
+ const query = this.builder.getQueryNode();
22
56
  return {
23
- ...config,
57
+ ...query,
24
58
  offset: count
25
59
  };
26
60
  }
27
61
  addOrderBy(column, direction = 'ASC') {
28
- const config = this.builder.getConfig();
62
+ const query = this.builder.getQueryNode();
29
63
  return {
30
- ...config,
31
- orderBy: [...(config.orderBy || []), { column, direction }]
64
+ ...query,
65
+ orderBy: [...(query.orderBy || []), { kind: 'order-by-item', column, direction }]
32
66
  };
33
67
  }
34
68
  addHaving(condition, parameters) {
35
- const config = this.builder.getConfig();
36
- const having = [...(config.having || []), condition];
37
- const newParams = parameters ? [...(config.parameters || []), ...parameters] : config.parameters;
69
+ const query = this.builder.getQueryNode();
70
+ const having = [
71
+ ...(query.having || []),
72
+ {
73
+ kind: 'having',
74
+ expression: condition,
75
+ parameters: parameters?.map(value => ({ kind: 'value', value })),
76
+ }
77
+ ];
38
78
  return {
39
- ...config,
40
- having,
41
- parameters: newParams
79
+ ...query,
80
+ having
42
81
  };
43
82
  }
44
83
  setDistinct() {
45
- const config = this.builder.getConfig();
84
+ const query = this.builder.getQueryNode();
46
85
  return {
47
- ...config,
86
+ ...query,
48
87
  distinct: true
49
88
  };
50
89
  }
90
+ setWithTotals() {
91
+ const query = this.builder.getQueryNode();
92
+ return {
93
+ ...query,
94
+ withTotals: true,
95
+ };
96
+ }
51
97
  }
@@ -1,9 +1,20 @@
1
- import { QueryConfig } from '../../types/index.js';
1
+ import { type CompiledQuery, type ExprNode, type SelectQueryNode, type SourceNode } from '../../types/index.js';
2
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;
3
+ formatSelect(query: SelectQueryNode<any, any>): string;
4
+ formatGroupBy(query: SelectQueryNode<any, any>): string;
5
+ formatArrayJoins(query: SelectQueryNode<any, any>): string;
6
+ formatPrewhere(query: SelectQueryNode<any, any>): string;
7
+ formatWhere(query: SelectQueryNode<any, any>): string;
8
+ formatFrom(source?: SourceNode): string;
9
+ compileExpr(expr?: ExprNode, nested?: boolean): CompiledQuery;
10
+ private compileCondition;
11
+ compileHaving(query: SelectQueryNode<any, any>): CompiledQuery;
6
12
  private getSqlOperator;
7
- formatJoins(config: QueryConfig<any, any>): string;
13
+ formatJoins(query: SelectQueryNode<any, any>): string;
14
+ formatCtes(query: SelectQueryNode<any, any>): string;
15
+ formatOrderBy(query: SelectQueryNode<any, any>): string;
16
+ formatLimitBy(query: SelectQueryNode<any, any>): string;
17
+ private combineCompiled;
18
+ private combineCompiledWithSeparator;
8
19
  }
9
20
  //# 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,sBAAsB,CAAC;AAEnE,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;IAoGlD,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,EAAkB,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAkB,MAAM,sBAAsB,CAAC;AAEhJ,qBAAa,YAAY;IACvB,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMtD,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMvD,gBAAgB,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAO1D,cAAc,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAIxD,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAIrD,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM;IAUvC,WAAW,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,aAAa;IAmD3D,OAAO,CAAC,gBAAgB;IAuFxB,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,aAAa;IAY9D,OAAO,CAAC,cAAc;IActB,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAcrD,UAAU,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAKpD,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAOvD,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAKvD,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,4BAA4B;CAMrC"}
@@ -1,109 +1,181 @@
1
1
  export class SQLFormatter {
2
- formatSelect(config) {
3
- const distinctClause = config.distinct ? 'DISTINCT ' : '';
4
- if (!config.select?.length)
2
+ formatSelect(query) {
3
+ const distinctClause = query.distinct ? 'DISTINCT ' : '';
4
+ if (!query.select?.length)
5
5
  return distinctClause + '*';
6
- return distinctClause + config.select.join(', ');
6
+ return distinctClause + query.select.map(item => item.selection).join(', ');
7
7
  }
8
- formatGroupBy(config) {
9
- const groupBy = config.groupBy;
8
+ formatGroupBy(query) {
9
+ const groupBy = query.groupBy;
10
10
  if (!groupBy?.length)
11
11
  return '';
12
- if (Array.isArray(groupBy)) {
13
- return groupBy.join(', ');
14
- }
15
- return String(groupBy);
12
+ return groupBy.map(item => item.expression).join(', ');
16
13
  }
17
- formatWhere(config) {
18
- if (!config.where?.length)
14
+ formatArrayJoins(query) {
15
+ if (!query.arrayJoins?.length)
19
16
  return '';
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 expression predicates
24
- if (condition.type === 'expression') {
25
- const prefix = index === 0 || afterGroupStart ? '' : ` ${condition.conjunction} `;
26
- afterGroupStart = false;
27
- return `${prefix}${condition.expression}`.trim();
28
- }
29
- // Handle special group markers
30
- if (condition.type === 'group-start') {
31
- const prefix = index === 0 ? '' : ` ${condition.conjunction} `;
32
- afterGroupStart = true; // Mark that the next condition follows a group-start
33
- return `${prefix}(`.trim();
34
- }
35
- if (condition.type === 'group-end') {
36
- afterGroupStart = false; // Reset the flag after group-end
37
- return ')';
38
- }
39
- // Normal conditions
40
- const { column, operator, value, conjunction } = condition;
41
- // Don't add conjunction if it's the first condition or right after a group-start
42
- const prefix = index === 0 || afterGroupStart ? '' : ` ${conjunction} `;
43
- // Reset the afterGroupStart flag
44
- afterGroupStart = false;
45
- // Handle IN operators
46
- if (operator === 'in' || operator === 'notIn') {
47
- if (!Array.isArray(value)) {
48
- throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
17
+ return query.arrayJoins
18
+ .map(item => `${item.type} JOIN ${item.expression}`)
19
+ .join(' ');
20
+ }
21
+ formatPrewhere(query) {
22
+ return this.compileExpr(query.prewhere).query;
23
+ }
24
+ formatWhere(query) {
25
+ return this.compileExpr(query.where).query;
26
+ }
27
+ formatFrom(source) {
28
+ if (!source)
29
+ return '';
30
+ switch (source.kind) {
31
+ case 'table':
32
+ return `${source.name}${source.final ? ' FINAL' : ''}`;
33
+ default:
34
+ throw new Error(`Unsupported source kind: ${String(source.kind)}`);
35
+ }
36
+ }
37
+ compileExpr(expr, nested = false) {
38
+ if (!expr)
39
+ return { query: '', parameters: [] };
40
+ switch (expr.kind) {
41
+ case 'raw':
42
+ return {
43
+ query: expr.expression,
44
+ parameters: expr.parameters.map(parameter => parameter.value),
45
+ };
46
+ case 'group':
47
+ if (!expr.expression) {
48
+ return { query: '', parameters: [] };
49
49
  }
50
- if (value.length === 0) {
51
- return `${prefix}1 = 0`;
50
+ const compiledGroup = this.compileExpr(expr.expression);
51
+ if (!compiledGroup.query) {
52
+ return { query: '', parameters: [] };
52
53
  }
53
- const placeholders = value.map(() => '?').join(', ');
54
- return `${prefix}${column} ${operator === 'in' ? 'IN' : 'NOT IN'} (${placeholders})`.trim();
54
+ return {
55
+ query: `(${compiledGroup.query})`,
56
+ parameters: compiledGroup.parameters,
57
+ };
58
+ case 'sequence':
59
+ return this.combineCompiled(expr.items
60
+ .map((item, index) => {
61
+ const rendered = this.compileExpr(item.expression, true);
62
+ return {
63
+ query: index === 0 ? rendered.query : ` ${item.conjunction} ${rendered.query}`,
64
+ parameters: rendered.parameters,
65
+ };
66
+ })
67
+ .filter(part => part.query.length > 0));
68
+ case 'logical': {
69
+ const rendered = expr.conditions
70
+ .map(condition => this.compileExpr(condition, true))
71
+ .filter(part => part.query.length > 0);
72
+ if (rendered.length === 0)
73
+ return { query: '', parameters: [] };
74
+ const combined = this.combineCompiledWithSeparator(rendered, ` ${expr.operator} `);
75
+ return {
76
+ query: nested ? `(${combined.query})` : combined.query,
77
+ parameters: combined.parameters,
78
+ };
55
79
  }
56
- // Handle GLOBAL IN operators
57
- else if (operator === 'globalIn' || operator === 'globalNotIn') {
58
- if (!Array.isArray(value)) {
59
- throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
60
- }
61
- if (value.length === 0) {
62
- return `${prefix}1 = 0`;
63
- }
64
- const placeholders = value.map(() => '?').join(', ');
65
- return `${prefix}${column} ${operator === 'globalIn' ? 'GLOBAL IN' : 'GLOBAL NOT IN'} (${placeholders})`.trim();
80
+ case 'condition':
81
+ return this.compileCondition(expr);
82
+ default:
83
+ throw new Error(`Unsupported expression kind: ${String(expr.kind)}`);
84
+ }
85
+ }
86
+ compileCondition({ column, operator, value }) {
87
+ if (operator === 'isNull' || operator === 'isNotNull') {
88
+ return {
89
+ query: `${column} IS ${operator === 'isNull' ? '' : 'NOT '}NULL`.trim(),
90
+ parameters: [],
91
+ };
92
+ }
93
+ if (operator === 'in' || operator === 'notIn') {
94
+ if (!Array.isArray(value)) {
95
+ throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
66
96
  }
67
- // Handle subquery IN operators
68
- else if (operator === 'inSubquery' || operator === 'globalInSubquery') {
69
- if (typeof value !== 'string') {
70
- throw new Error(`Expected a string (subquery) for ${operator} operator, but got ${typeof value}`);
71
- }
72
- return `${prefix}${column} ${operator === 'inSubquery' ? 'IN' : 'GLOBAL IN'} (${value})`.trim();
97
+ if (value.length === 0) {
98
+ return { query: operator === 'in' ? '1 = 0' : '1 = 1', parameters: [] };
73
99
  }
74
- // Handle table reference IN operators
75
- else if (operator === 'inTable' || operator === 'globalInTable') {
76
- if (typeof value !== 'string') {
77
- throw new Error(`Expected a string (table name) for ${operator} operator, but got ${typeof value}`);
78
- }
79
- return `${prefix}${column} ${operator === 'inTable' ? 'IN' : 'GLOBAL IN'} ${value}`.trim();
100
+ return {
101
+ query: `${column} ${operator === 'in' ? 'IN' : 'NOT IN'} (${value.map(() => '?').join(', ')})`,
102
+ parameters: value.map(item => item.value),
103
+ };
104
+ }
105
+ if (operator === 'globalIn' || operator === 'globalNotIn') {
106
+ if (!Array.isArray(value)) {
107
+ throw new Error(`Expected an array for ${operator} operator, but got ${typeof value}`);
80
108
  }
81
- // Handle tuple IN operators
82
- else if (operator === 'inTuple' || operator === 'globalInTuple') {
83
- if (!Array.isArray(value)) {
84
- throw new Error(`Expected an array of tuples for ${operator} operator, but got ${typeof value}`);
85
- }
86
- if (value.length === 0) {
87
- return `${prefix}1 = 0`;
88
- }
89
- const placeholders = value.map(() => '(?, ?)').join(', ');
90
- return `${prefix}${column} ${operator === 'inTuple' ? 'IN' : 'GLOBAL IN'} (${placeholders})`.trim();
109
+ if (value.length === 0) {
110
+ return { query: operator === 'globalIn' ? '1 = 0' : '1 = 1', parameters: [] };
111
+ }
112
+ return {
113
+ query: `${column} ${operator === 'globalIn' ? 'GLOBAL IN' : 'GLOBAL NOT IN'} (${value.map(() => '?').join(', ')})`,
114
+ parameters: value.map(item => item.value),
115
+ };
116
+ }
117
+ if (operator === 'inSubquery' || operator === 'globalInSubquery') {
118
+ if (typeof value !== 'string') {
119
+ throw new Error(`Expected a string (subquery) for ${operator} operator, but got ${typeof value}`);
120
+ }
121
+ return {
122
+ query: `${column} ${operator === 'inSubquery' ? 'IN' : 'GLOBAL IN'} (${value})`,
123
+ parameters: [],
124
+ };
125
+ }
126
+ if (operator === 'inTable' || operator === 'globalInTable') {
127
+ if (typeof value !== 'string') {
128
+ throw new Error(`Expected a string (table name) for ${operator} operator, but got ${typeof value}`);
91
129
  }
92
- else if (operator === 'between') {
93
- return `${prefix}${column} BETWEEN ? AND ?`.trim();
130
+ return {
131
+ query: `${column} ${operator === 'inTable' ? 'IN' : 'GLOBAL IN'} ${value}`,
132
+ parameters: [],
133
+ };
134
+ }
135
+ if (operator === 'inTuple' || operator === 'globalInTuple') {
136
+ if (!Array.isArray(value)) {
137
+ throw new Error(`Expected an array of tuples for ${operator} operator, but got ${typeof value}`);
94
138
  }
95
- else if (operator === 'like') {
96
- return `${prefix}${column} LIKE ?`.trim();
139
+ if (value.length === 0) {
140
+ return { query: '1 = 0', parameters: [] };
97
141
  }
98
- else {
99
- return `${prefix}${column} ${this.getSqlOperator(operator)} ?`.trim();
142
+ const tupleWidth = value[0].length;
143
+ if (tupleWidth === 0) {
144
+ throw new Error(`Expected non-empty tuples for ${operator} operator`);
100
145
  }
101
- });
102
- // Join fragments and then remove extra spaces around parentheses
103
- let result = fragments.join(' ');
104
- // Replace "( " with "(" and " )" with ")"
105
- result = result.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')');
106
- return result;
146
+ const tuplePlaceholder = `(${Array.from({ length: tupleWidth }, () => '?').join(', ')})`;
147
+ return {
148
+ query: `${column} ${operator === 'inTuple' ? 'IN' : 'GLOBAL IN'} (${value.map(() => tuplePlaceholder).join(', ')})`,
149
+ parameters: value.flatMap(tuple => tuple.map(item => item.value)),
150
+ };
151
+ }
152
+ if (operator === 'between') {
153
+ const range = value;
154
+ return {
155
+ query: `${column} BETWEEN ? AND ?`,
156
+ parameters: [range[0].value, range[1].value],
157
+ };
158
+ }
159
+ if (operator === 'like') {
160
+ const parameter = value;
161
+ return {
162
+ query: `${column} LIKE ?`,
163
+ parameters: [parameter.value],
164
+ };
165
+ }
166
+ const parameter = value;
167
+ return {
168
+ query: `${column} ${this.getSqlOperator(operator)} ?`,
169
+ parameters: [parameter.value],
170
+ };
171
+ }
172
+ compileHaving(query) {
173
+ if (!query.having?.length)
174
+ return { query: '', parameters: [] };
175
+ return this.combineCompiledWithSeparator(query.having.map(item => ({
176
+ query: item.expression,
177
+ parameters: item.parameters?.map(parameter => parameter.value) || [],
178
+ })), ' AND ');
107
179
  }
108
180
  getSqlOperator(operator) {
109
181
  switch (operator) {
@@ -118,14 +190,46 @@ export class SQLFormatter {
118
190
  throw new Error(`Unsupported operator: ${operator}`);
119
191
  }
120
192
  }
121
- formatJoins(config) {
122
- if (!config.joins?.length)
193
+ formatJoins(query) {
194
+ if (!query.joins?.length)
123
195
  return '';
124
- return config.joins.map(join => {
196
+ return query.joins.map(join => {
125
197
  const tableClause = join.alias
126
198
  ? `${join.table} AS ${join.alias}`
127
199
  : join.table;
128
- return `${join.type} JOIN ${tableClause} ON ${join.leftColumn} = ${join.rightColumn}`;
200
+ const leftColumn = join.leftSource && !join.leftColumn.includes('.')
201
+ ? `${join.leftSource}.${join.leftColumn}`
202
+ : join.leftColumn;
203
+ return `${join.type} JOIN ${tableClause} ON ${leftColumn} = ${join.rightColumn}`;
129
204
  }).join(' ');
130
205
  }
206
+ formatCtes(query) {
207
+ if (!query.ctes?.length)
208
+ return '';
209
+ return query.ctes.map(item => item.expression).join(', ');
210
+ }
211
+ formatOrderBy(query) {
212
+ if (!query.orderBy?.length)
213
+ return '';
214
+ return query.orderBy
215
+ .map(({ column, direction }) => `${String(column)} ${direction}`.trim())
216
+ .join(', ');
217
+ }
218
+ formatLimitBy(query) {
219
+ if (!query.limitBy)
220
+ return '';
221
+ return `${query.limitBy.limit} BY ${query.limitBy.by.join(', ')}`;
222
+ }
223
+ combineCompiled(parts) {
224
+ return {
225
+ query: parts.map(part => part.query).join(''),
226
+ parameters: parts.flatMap(part => part.parameters),
227
+ };
228
+ }
229
+ combineCompiledWithSeparator(parts, separator) {
230
+ return {
231
+ query: parts.map(part => part.query).join(separator),
232
+ parameters: parts.flatMap(part => part.parameters),
233
+ };
234
+ }
131
235
  }