@hypequery/clickhouse 2.0.2 → 2.1.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.
- package/README.md +1 -1
- package/dist/cli/generate-types.d.ts +35 -1
- package/dist/cli/generate-types.js +32 -11
- package/dist/cli/index.d.ts +7 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/type-parsing.js +22 -4
- package/dist/core/dialects/clickhouse-dialect.d.ts.map +1 -1
- package/dist/core/dialects/clickhouse-dialect.js +2 -1
- package/dist/core/tests/integration/setup.d.ts.map +1 -1
- package/dist/core/tests/integration/setup.js +1 -3
- package/dist/core/utils/sql-expressions.d.ts.map +1 -1
- package/dist/core/utils/sql-expressions.js +6 -4
- package/dist/core/utils/sql-literals.d.ts +3 -0
- package/dist/core/utils/sql-literals.d.ts.map +1 -0
- package/dist/core/utils/sql-literals.js +39 -0
- package/dist/datasets.d.ts +20 -8
- package/dist/datasets.d.ts.map +1 -1
- package/dist/datasets.js +25 -11
- package/package.json +13 -3
- package/dist/core/tests/integration/test-data.json +0 -190
- package/dist/migrations/config/index.d.ts +0 -3
- package/dist/migrations/config/index.d.ts.map +0 -1
- package/dist/migrations/config/index.js +0 -1
- package/dist/migrations/config/types.d.ts +0 -45
- package/dist/migrations/config/types.d.ts.map +0 -1
- package/dist/migrations/config/types.js +0 -28
- package/dist/migrations/diff/diff.d.ts +0 -11
- package/dist/migrations/diff/diff.d.ts.map +0 -1
- package/dist/migrations/diff/diff.js +0 -240
- package/dist/migrations/diff/index.d.ts +0 -3
- package/dist/migrations/diff/index.d.ts.map +0 -1
- package/dist/migrations/diff/index.js +0 -1
- package/dist/migrations/diff/types.d.ts +0 -74
- package/dist/migrations/diff/types.d.ts.map +0 -1
- package/dist/migrations/diff/types.js +0 -1
- package/dist/migrations/introspect/index.d.ts +0 -3
- package/dist/migrations/introspect/index.d.ts.map +0 -1
- package/dist/migrations/introspect/index.js +0 -1
- package/dist/migrations/introspect/pull-schema.d.ts +0 -23
- package/dist/migrations/introspect/pull-schema.d.ts.map +0 -1
- package/dist/migrations/introspect/pull-schema.js +0 -135
- package/dist/migrations/plan/index.d.ts +0 -3
- package/dist/migrations/plan/index.d.ts.map +0 -1
- package/dist/migrations/plan/index.js +0 -1
- package/dist/migrations/plan/plan.d.ts +0 -12
- package/dist/migrations/plan/plan.d.ts.map +0 -1
- package/dist/migrations/plan/plan.js +0 -416
- package/dist/migrations/plan/types.d.ts +0 -93
- package/dist/migrations/plan/types.d.ts.map +0 -1
- package/dist/migrations/plan/types.js +0 -1
- package/dist/migrations/schema/column.d.ts +0 -71
- package/dist/migrations/schema/column.d.ts.map +0 -1
- package/dist/migrations/schema/column.js +0 -123
- package/dist/migrations/schema/define.d.ts +0 -24
- package/dist/migrations/schema/define.d.ts.map +0 -1
- package/dist/migrations/schema/define.js +0 -47
- package/dist/migrations/schema/index.d.ts +0 -4
- package/dist/migrations/schema/index.d.ts.map +0 -1
- package/dist/migrations/schema/index.js +0 -2
- package/dist/migrations/schema/types.d.ts +0 -74
- package/dist/migrations/schema/types.d.ts.map +0 -1
- package/dist/migrations/schema/types.js +0 -1
- package/dist/migrations/snapshot/index.d.ts +0 -3
- package/dist/migrations/snapshot/index.d.ts.map +0 -1
- package/dist/migrations/snapshot/index.js +0 -1
- package/dist/migrations/snapshot/serialize.d.ts +0 -21
- package/dist/migrations/snapshot/serialize.d.ts.map +0 -1
- package/dist/migrations/snapshot/serialize.js +0 -127
- package/dist/migrations/snapshot/types.d.ts +0 -47
- package/dist/migrations/snapshot/types.d.ts.map +0 -1
- package/dist/migrations/snapshot/types.js +0 -1
- package/dist/migrations/sql/index.d.ts +0 -4
- package/dist/migrations/sql/index.d.ts.map +0 -1
- package/dist/migrations/sql/index.js +0 -2
- package/dist/migrations/sql/render.d.ts +0 -10
- package/dist/migrations/sql/render.d.ts.map +0 -1
- package/dist/migrations/sql/render.js +0 -347
- package/dist/migrations/sql/types.d.ts +0 -53
- package/dist/migrations/sql/types.d.ts.map +0 -1
- package/dist/migrations/sql/types.js +0 -1
- package/dist/migrations/sql/write.d.ts +0 -10
- package/dist/migrations/sql/write.d.ts.map +0 -1
- package/dist/migrations/sql/write.js +0 -35
- package/dist/semantic-backend.d.ts +0 -7
- package/dist/semantic-backend.d.ts.map +0 -1
- package/dist/semantic-backend.js +0 -208
package/README.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ClickHouse client surface needed by the type generator.
|
|
3
|
+
*/
|
|
4
|
+
export interface TypeGenerationClickHouseClient {
|
|
5
|
+
query(options: { query: string; format: 'JSONEachRow' }): Promise<{
|
|
6
|
+
json(): Promise<Array<Record<string, string>>>;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Options for generating TypeScript type definitions from ClickHouse.
|
|
12
|
+
*/
|
|
13
|
+
export interface GenerateTypesOptions {
|
|
14
|
+
includeTables?: string[];
|
|
15
|
+
excludeTables?: string[];
|
|
16
|
+
client?: TypeGenerationClickHouseClient;
|
|
17
|
+
generatedBy?: string;
|
|
18
|
+
includeUsageExample?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Converts a ClickHouse type string to a TypeScript type string.
|
|
23
|
+
*/
|
|
24
|
+
export declare function clickhouseToTsType(type: string): string;
|
|
25
|
+
|
|
1
26
|
/**
|
|
2
27
|
* Generates TypeScript type definitions from the ClickHouse database schema
|
|
3
28
|
* @param outputPath - The file path where the type definitions will be written
|
|
29
|
+
* @param options - Options for type generation
|
|
30
|
+
*/
|
|
31
|
+
export declare function generateTypes(outputPath: string, options?: GenerateTypesOptions): Promise<void>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generates TypeScript type definition contents from a ClickHouse client.
|
|
4
35
|
*/
|
|
5
|
-
export declare function
|
|
36
|
+
export declare function generateTypeDefinitions(
|
|
37
|
+
client: TypeGenerationClickHouseClient,
|
|
38
|
+
options?: GenerateTypesOptions,
|
|
39
|
+
): Promise<string>;
|
|
@@ -19,6 +19,9 @@ dotenv.config();
|
|
|
19
19
|
* @typedef {Object} GenerateTypesOptions
|
|
20
20
|
* @property {string[]} [includeTables] - List of tables to include
|
|
21
21
|
* @property {string[]} [excludeTables] - List of tables to exclude
|
|
22
|
+
* @property {object} [client] - ClickHouse client to use instead of ClickHouseConnection
|
|
23
|
+
* @property {string} [generatedBy] - Package name to include in the generated file header
|
|
24
|
+
* @property {boolean} [includeUsageExample] - Whether to append the historical usage example
|
|
22
25
|
*/
|
|
23
26
|
|
|
24
27
|
/**
|
|
@@ -28,8 +31,30 @@ dotenv.config();
|
|
|
28
31
|
* @returns {Promise<void>}
|
|
29
32
|
*/
|
|
30
33
|
export async function generateTypes(outputPath, options = {}) {
|
|
31
|
-
const client = ClickHouseConnection.getClient();
|
|
32
|
-
const
|
|
34
|
+
const client = options.client || ClickHouseConnection.getClient();
|
|
35
|
+
const typeDefinitions = await generateTypeDefinitions(client, options);
|
|
36
|
+
|
|
37
|
+
// Ensure the output directory exists
|
|
38
|
+
const outputDir = path.dirname(path.resolve(outputPath));
|
|
39
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
40
|
+
|
|
41
|
+
// Write the file
|
|
42
|
+
await fs.writeFile(path.resolve(outputPath), typeDefinitions);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generates TypeScript type definition contents from a ClickHouse client.
|
|
47
|
+
* @param {object} client - ClickHouse client with a query method
|
|
48
|
+
* @param {GenerateTypesOptions} [options] - Options for type generation
|
|
49
|
+
* @returns {Promise<string>}
|
|
50
|
+
*/
|
|
51
|
+
export async function generateTypeDefinitions(client, options = {}) {
|
|
52
|
+
const {
|
|
53
|
+
includeTables = [],
|
|
54
|
+
excludeTables = [],
|
|
55
|
+
generatedBy = '@hypequery/clickhouse',
|
|
56
|
+
includeUsageExample = true,
|
|
57
|
+
} = options;
|
|
33
58
|
|
|
34
59
|
// Get all tables
|
|
35
60
|
const tablesQuery = await client.query({
|
|
@@ -52,7 +77,7 @@ export async function generateTypes(outputPath, options = {}) {
|
|
|
52
77
|
console.warn('Warning: No tables match the filter criteria. Check your include/exclude options.');
|
|
53
78
|
}
|
|
54
79
|
|
|
55
|
-
let typeDefinitions = `// Generated by
|
|
80
|
+
let typeDefinitions = `// Generated by ${generatedBy}
|
|
56
81
|
// This file defines TypeScript types based on your ClickHouse database schema
|
|
57
82
|
|
|
58
83
|
/**
|
|
@@ -95,8 +120,8 @@ export interface IntrospectedSchema {`;
|
|
|
95
120
|
typeDefinitions += '\n}\n\n';
|
|
96
121
|
}
|
|
97
122
|
|
|
98
|
-
|
|
99
|
-
|
|
123
|
+
if (includeUsageExample) {
|
|
124
|
+
typeDefinitions += `
|
|
100
125
|
/**
|
|
101
126
|
* Usage example:
|
|
102
127
|
*
|
|
@@ -114,13 +139,9 @@ export interface IntrospectedSchema {`;
|
|
|
114
139
|
* .execute();
|
|
115
140
|
*/
|
|
116
141
|
`;
|
|
142
|
+
}
|
|
117
143
|
|
|
118
|
-
|
|
119
|
-
const outputDir = path.dirname(path.resolve(outputPath));
|
|
120
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
121
|
-
|
|
122
|
-
// Write the file
|
|
123
|
-
await fs.writeFile(path.resolve(outputPath), typeDefinitions);
|
|
144
|
+
return typeDefinitions;
|
|
124
145
|
}
|
|
125
146
|
|
|
126
147
|
/**
|
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// CLI module exports
|
|
2
|
-
export { generateTypes } from './generate-types.js';
|
|
2
|
+
export { clickhouseToTsType, generateTypeDefinitions, generateTypes } from './generate-types.js';
|
package/dist/cli/type-parsing.js
CHANGED
|
@@ -33,10 +33,20 @@ function splitTopLevelArgs(value) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function unwrapType(type, wrapperName) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
// Trim whitespace and handle case-insensitive matching
|
|
37
|
+
const trimmedType = type.trim();
|
|
38
|
+
const lowerType = trimmedType.toLowerCase();
|
|
39
|
+
const lowerWrapper = wrapperName.toLowerCase();
|
|
40
|
+
const prefix = `${lowerWrapper}(`;
|
|
41
|
+
|
|
42
|
+
if (lowerType.startsWith(prefix) && trimmedType.endsWith(')')) {
|
|
43
|
+
// Extract inner type, preserving original case
|
|
44
|
+
const innerStart = trimmedType.indexOf('(') + 1;
|
|
45
|
+
const innerEnd = trimmedType.lastIndexOf(')');
|
|
46
|
+
return trimmedType.slice(innerStart, innerEnd).trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return null;
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
function getPrimitiveTsType(type) {
|
|
@@ -45,6 +55,8 @@ function getPrimitiveTsType(type) {
|
|
|
45
55
|
switch (lowerType) {
|
|
46
56
|
case 'string':
|
|
47
57
|
case 'uuid':
|
|
58
|
+
case 'ipv4':
|
|
59
|
+
case 'ipv6':
|
|
48
60
|
return 'string';
|
|
49
61
|
case 'int8':
|
|
50
62
|
case 'int16':
|
|
@@ -72,9 +84,15 @@ function getPrimitiveTsType(type) {
|
|
|
72
84
|
case 'bool':
|
|
73
85
|
case 'boolean':
|
|
74
86
|
return 'boolean';
|
|
87
|
+
case 'json':
|
|
88
|
+
return 'unknown';
|
|
75
89
|
default:
|
|
76
90
|
if (type.startsWith('FixedString(')) return 'string';
|
|
77
91
|
if (type.startsWith('Decimal(')) return 'number';
|
|
92
|
+
if (type.startsWith('Decimal32(')) return 'number';
|
|
93
|
+
if (type.startsWith('Decimal64(')) return 'number';
|
|
94
|
+
if (type.startsWith('Decimal128(')) return 'number';
|
|
95
|
+
if (type.startsWith('Decimal256(')) return 'number';
|
|
78
96
|
if (type.startsWith('DateTime64(')) return 'string';
|
|
79
97
|
if (type.startsWith('DateTime(')) return 'string';
|
|
80
98
|
if (type.startsWith('Enum8(')) return 'string';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clickhouse-dialect.d.ts","sourceRoot":"","sources":["../../../src/core/dialects/clickhouse-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"clickhouse-dialect.d.ts","sourceRoot":"","sources":["../../../src/core/dialects/clickhouse-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAExE,qBAAa,iBAAkB,YAAW,UAAU;IAClD,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,OAAO,CAAC,SAAS,CAAsB;IAEvC,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,mBAAmB,GAAG,aAAa;IA6D3F,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAQ5E,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;CAK1D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SQLFormatter } from '../formatters/sql-formatter.js';
|
|
2
|
+
import { formatIntervalLiteral } from '../utils/sql-literals.js';
|
|
2
3
|
export class ClickHouseDialect {
|
|
3
4
|
name = 'clickhouse';
|
|
4
5
|
formatter = new SQLFormatter();
|
|
@@ -52,7 +53,7 @@ export class ClickHouseDialect {
|
|
|
52
53
|
}
|
|
53
54
|
formatTimeInterval(column, interval, method) {
|
|
54
55
|
if (method === 'toStartOfInterval') {
|
|
55
|
-
return `${method}(${column}, INTERVAL ${interval})`;
|
|
56
|
+
return `${method}(${column}, INTERVAL ${formatIntervalLiteral(interval)})`;
|
|
56
57
|
}
|
|
57
58
|
return `${method}(${column})`;
|
|
58
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/setup.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,yBAAyB,EACzB,sBAAsB,EACtB,SAAS,
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/setup.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,yBAAyB,EACzB,sBAAsB,EACtB,SAAS,EAQV,MAAM,kDAAkD,CAAC;AA4B1D,eAAO,MAAM,wBAAwB;;;;;;EAyBpC,CAAC;AAGF,eAAO,MAAM,2BAA2B,yGAevC,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,OAAO,CAE/E,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,IAAI,CAO7D,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAC5B,oBAAgB,EAChB,sBAAoB,KACnB,OAAO,CAAC,IAAI,CAOd,CAAC;AAGF,eAAO,MAAM,uBAAuB,QAAa,OAAO,CAAC,IAAI,CAU5D,CAAC;AAGF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,OAAO,CAAC;QACnB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,SAAS,EAAE,CAAC;AAKxE,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAuBtD,CAAC"}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { ClickHouseConnection } from '../../connection.js';
|
|
2
2
|
import { logger as hypeQueryLogger } from '../../utils/logger.js';
|
|
3
|
-
import { CLICKHOUSE_CONTAINER_NAME, TEST_CONNECTION_CONFIG, TEST_DATA, detectComposeCommand, ensureDockerDaemon, isContainerRunning as sharedIsContainerRunning, seedClickHouseDatabase, startClickHouseContainer as sharedStartClickHouseContainer, stopClickHouseContainer as sharedStopClickHouseContainer, waitForClickHouse as sharedWaitForClickHouse,
|
|
4
|
-
// @ts-expect-error: shared test harness is plain JS
|
|
5
|
-
} from '../../../../../../testing/clickhouse/harness.mjs';
|
|
3
|
+
import { CLICKHOUSE_CONTAINER_NAME, TEST_CONNECTION_CONFIG, TEST_DATA, detectComposeCommand, ensureDockerDaemon, isContainerRunning as sharedIsContainerRunning, seedClickHouseDatabase, startClickHouseContainer as sharedStartClickHouseContainer, stopClickHouseContainer as sharedStopClickHouseContainer, waitForClickHouse as sharedWaitForClickHouse, } from '../../../../../../testing/clickhouse/harness.mjs';
|
|
6
4
|
// Disable the hypequery logger to prevent "logs after tests" errors
|
|
7
5
|
// This must be done early in the setup, before any queries run
|
|
8
6
|
hypeQueryLogger.configure({ enabled: false });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-expressions.d.ts","sourceRoot":"","sources":["../../../src/core/utils/sql-expressions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sql-expressions.d.ts","sourceRoot":"","sources":["../../../src/core/utils/sql-expressions.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatIntervalLiteral, quoteStringLiteral } from './sql-literals.js';
|
|
1
2
|
/**
|
|
2
3
|
* Creates a raw SQL expression
|
|
3
4
|
* @param sql The SQL expression string
|
|
@@ -34,17 +35,18 @@ export function toDateTime(field, alias) {
|
|
|
34
35
|
}
|
|
35
36
|
export function formatDateTime(field, format, options = {}) {
|
|
36
37
|
const { timezone, alias } = options;
|
|
37
|
-
let sql = `formatDateTime(${field},
|
|
38
|
+
let sql = `formatDateTime(${field}, ${quoteStringLiteral(format)}`;
|
|
38
39
|
if (timezone) {
|
|
39
|
-
sql += `,
|
|
40
|
+
sql += `, ${quoteStringLiteral(timezone)}`;
|
|
40
41
|
}
|
|
41
42
|
sql += ')';
|
|
42
43
|
return alias ? rawAs(sql, alias) : raw(sql);
|
|
43
44
|
}
|
|
44
45
|
export function toStartOfInterval(field, interval, alias) {
|
|
46
|
+
const sql = `toStartOfInterval(${field}, INTERVAL ${formatIntervalLiteral(interval)})`;
|
|
45
47
|
return alias
|
|
46
|
-
? rawAs(
|
|
47
|
-
: raw(
|
|
48
|
+
? rawAs(sql, alias)
|
|
49
|
+
: raw(sql);
|
|
48
50
|
}
|
|
49
51
|
function toStartOfUnit(functionName, field, alias) {
|
|
50
52
|
return alias
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-literals.d.ts","sourceRoot":"","sources":["../../../src/core/utils/sql-literals.ts"],"names":[],"mappings":"AA2BA,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAc9D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const INTERVAL_UNITS = {
|
|
2
|
+
second: 'SECOND',
|
|
3
|
+
seconds: 'SECOND',
|
|
4
|
+
minute: 'MINUTE',
|
|
5
|
+
minutes: 'MINUTE',
|
|
6
|
+
hour: 'HOUR',
|
|
7
|
+
hours: 'HOUR',
|
|
8
|
+
day: 'DAY',
|
|
9
|
+
days: 'DAY',
|
|
10
|
+
week: 'WEEK',
|
|
11
|
+
weeks: 'WEEK',
|
|
12
|
+
month: 'MONTH',
|
|
13
|
+
months: 'MONTH',
|
|
14
|
+
quarter: 'QUARTER',
|
|
15
|
+
quarters: 'QUARTER',
|
|
16
|
+
year: 'YEAR',
|
|
17
|
+
years: 'YEAR',
|
|
18
|
+
millisecond: 'MILLISECOND',
|
|
19
|
+
milliseconds: 'MILLISECOND',
|
|
20
|
+
microsecond: 'MICROSECOND',
|
|
21
|
+
microseconds: 'MICROSECOND',
|
|
22
|
+
nanosecond: 'NANOSECOND',
|
|
23
|
+
nanoseconds: 'NANOSECOND',
|
|
24
|
+
};
|
|
25
|
+
const INTERVAL_PATTERN = /^(\d+)\s+([a-z]+)$/i;
|
|
26
|
+
export function formatIntervalLiteral(interval) {
|
|
27
|
+
const normalized = interval.trim();
|
|
28
|
+
const match = normalized.match(INTERVAL_PATTERN);
|
|
29
|
+
const unit = match ? INTERVAL_UNITS[match[2].toLowerCase()] : undefined;
|
|
30
|
+
if (!match || !unit) {
|
|
31
|
+
throw new Error(`Invalid time interval: "${interval}". Expected "<number> <unit>" where unit is ` +
|
|
32
|
+
'one of second, minute, hour, day, week, month, quarter, year, millisecond, microsecond, nanosecond ' +
|
|
33
|
+
'(e.g. "1 day", "15 minute").');
|
|
34
|
+
}
|
|
35
|
+
return `${match[1]} ${unit}`;
|
|
36
|
+
}
|
|
37
|
+
export function quoteStringLiteral(value) {
|
|
38
|
+
return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "''")}'`;
|
|
39
|
+
}
|
package/dist/datasets.d.ts
CHANGED
|
@@ -9,18 +9,30 @@
|
|
|
9
9
|
* - @hypequery/datasets: Owns semantic planning, validation, and execution protocol
|
|
10
10
|
* - @hypequery/clickhouse: Implements SQL translation and execution for ClickHouse
|
|
11
11
|
*
|
|
12
|
-
* Usage:
|
|
12
|
+
* Usage (recommended): pass the query builder you already use for
|
|
13
|
+
* hand-written queries, so semantic and ad hoc queries share one connection.
|
|
13
14
|
* ```ts
|
|
14
15
|
* import { createDatasetClient } from '@hypequery/datasets';
|
|
15
|
-
* import {
|
|
16
|
+
* import { createQueryBuilder } from '@hypequery/clickhouse';
|
|
17
|
+
*
|
|
18
|
+
* const db = createQueryBuilder({
|
|
19
|
+
* url: process.env.CLICKHOUSE_URL,
|
|
20
|
+
* username: process.env.CLICKHOUSE_USER,
|
|
21
|
+
* password: process.env.CLICKHOUSE_PASSWORD,
|
|
22
|
+
* database: process.env.CLICKHOUSE_DATABASE,
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* const analytics = createDatasetClient({ queryBuilder: db });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Advanced: `createBackend` exposes the database-agnostic SemanticBackend
|
|
29
|
+
* protocol directly. Reach for it when you want a standalone backend instance
|
|
30
|
+
* rather than sharing a query builder.
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { createBackend } from '@hypequery/clickhouse/datasets';
|
|
16
33
|
*
|
|
17
34
|
* const analytics = createDatasetClient({
|
|
18
|
-
* backend: createBackend({
|
|
19
|
-
* url: process.env.CLICKHOUSE_URL,
|
|
20
|
-
* username: process.env.CLICKHOUSE_USER,
|
|
21
|
-
* password: process.env.CLICKHOUSE_PASSWORD,
|
|
22
|
-
* database: process.env.CLICKHOUSE_DATABASE,
|
|
23
|
-
* })
|
|
35
|
+
* backend: createBackend({ url, username, password, database }),
|
|
24
36
|
* });
|
|
25
37
|
* ```
|
|
26
38
|
*/
|
package/dist/datasets.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datasets.d.ts","sourceRoot":"","sources":["../src/datasets.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"datasets.d.ts","sourceRoot":"","sources":["../src/datasets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAuW3D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,SAAS,gBAAgB,CAAC,MAAM,CAAC,EACnE,MAAM,EAAE,mBAAmB,GAC1B,eAAe,CAqDjB"}
|
package/dist/datasets.js
CHANGED
|
@@ -9,18 +9,30 @@
|
|
|
9
9
|
* - @hypequery/datasets: Owns semantic planning, validation, and execution protocol
|
|
10
10
|
* - @hypequery/clickhouse: Implements SQL translation and execution for ClickHouse
|
|
11
11
|
*
|
|
12
|
-
* Usage:
|
|
12
|
+
* Usage (recommended): pass the query builder you already use for
|
|
13
|
+
* hand-written queries, so semantic and ad hoc queries share one connection.
|
|
13
14
|
* ```ts
|
|
14
15
|
* import { createDatasetClient } from '@hypequery/datasets';
|
|
15
|
-
* import {
|
|
16
|
+
* import { createQueryBuilder } from '@hypequery/clickhouse';
|
|
17
|
+
*
|
|
18
|
+
* const db = createQueryBuilder({
|
|
19
|
+
* url: process.env.CLICKHOUSE_URL,
|
|
20
|
+
* username: process.env.CLICKHOUSE_USER,
|
|
21
|
+
* password: process.env.CLICKHOUSE_PASSWORD,
|
|
22
|
+
* database: process.env.CLICKHOUSE_DATABASE,
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* const analytics = createDatasetClient({ queryBuilder: db });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Advanced: `createBackend` exposes the database-agnostic SemanticBackend
|
|
29
|
+
* protocol directly. Reach for it when you want a standalone backend instance
|
|
30
|
+
* rather than sharing a query builder.
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { createBackend } from '@hypequery/clickhouse/datasets';
|
|
16
33
|
*
|
|
17
34
|
* const analytics = createDatasetClient({
|
|
18
|
-
* backend: createBackend({
|
|
19
|
-
* url: process.env.CLICKHOUSE_URL,
|
|
20
|
-
* username: process.env.CLICKHOUSE_USER,
|
|
21
|
-
* password: process.env.CLICKHOUSE_PASSWORD,
|
|
22
|
-
* database: process.env.CLICKHOUSE_DATABASE,
|
|
23
|
-
* })
|
|
35
|
+
* backend: createBackend({ url, username, password, database }),
|
|
24
36
|
* });
|
|
25
37
|
* ```
|
|
26
38
|
*/
|
|
@@ -282,7 +294,7 @@ function buildAggregateQuery(queryBuilder, plan) {
|
|
|
282
294
|
}
|
|
283
295
|
// Apply tenant filter (auto-injected)
|
|
284
296
|
if (plan.tenant) {
|
|
285
|
-
qb = qb.where(plan.tenant.field,
|
|
297
|
+
qb = qb.where(plan.tenant.field, plan.tenant.operator, plan.tenant.value);
|
|
286
298
|
}
|
|
287
299
|
// Apply user filters
|
|
288
300
|
qb = applyFilters(qb, plan.filters);
|
|
@@ -357,14 +369,16 @@ export function createBackend(config) {
|
|
|
357
369
|
meta: {
|
|
358
370
|
sql,
|
|
359
371
|
timingMs: Date.now() - start,
|
|
360
|
-
tenant: plan.tenant?.value,
|
|
372
|
+
tenant: plan.tenant?.operator === 'eq' ? plan.tenant.value : undefined,
|
|
361
373
|
},
|
|
362
374
|
};
|
|
363
375
|
}
|
|
364
376
|
// Derived metrics: CTE query with formulas
|
|
365
377
|
const { sql, parameters } = buildDerivedSQL(queryBuilder, plan);
|
|
366
378
|
const data = await queryBuilder.rawQuery(sql, parameters);
|
|
367
|
-
const tenant = plan.input.kind === 'aggregate'
|
|
379
|
+
const tenant = plan.input.kind === 'aggregate' && plan.input.tenant?.operator === 'eq'
|
|
380
|
+
? plan.input.tenant.value
|
|
381
|
+
: undefined;
|
|
368
382
|
return {
|
|
369
383
|
data,
|
|
370
384
|
meta: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/clickhouse",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "ClickHouse typescript query builder",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
"import": "./dist/index.js",
|
|
15
15
|
"require": "./dist/index.js"
|
|
16
16
|
},
|
|
17
|
+
"./datasets": {
|
|
18
|
+
"types": "./dist/datasets.d.ts",
|
|
19
|
+
"import": "./dist/datasets.js",
|
|
20
|
+
"require": "./dist/datasets.js"
|
|
21
|
+
},
|
|
17
22
|
"./cli": {
|
|
18
23
|
"types": "./dist/cli/index.d.ts",
|
|
19
24
|
"import": "./dist/cli/index.js",
|
|
@@ -33,11 +38,15 @@
|
|
|
33
38
|
"dotenv": "^16.0.0"
|
|
34
39
|
},
|
|
35
40
|
"peerDependencies": {
|
|
36
|
-
"@clickhouse/client-web": "^0.2.0 || ^1.0.0"
|
|
41
|
+
"@clickhouse/client-web": "^0.2.0 || ^1.0.0",
|
|
42
|
+
"@hypequery/datasets": "^0.1.0 || ^0.2.0"
|
|
37
43
|
},
|
|
38
44
|
"peerDependenciesMeta": {
|
|
39
45
|
"@clickhouse/client-web": {
|
|
40
46
|
"optional": true
|
|
47
|
+
},
|
|
48
|
+
"@hypequery/datasets": {
|
|
49
|
+
"optional": true
|
|
41
50
|
}
|
|
42
51
|
},
|
|
43
52
|
"devDependencies": {
|
|
@@ -51,7 +60,8 @@
|
|
|
51
60
|
"typedoc-plugin-markdown": "^4.6.0",
|
|
52
61
|
"typescript": "^5.7.3",
|
|
53
62
|
"@vitest/coverage-v8": "^2.1.6",
|
|
54
|
-
"vitest": "^2.1.6"
|
|
63
|
+
"vitest": "^2.1.6",
|
|
64
|
+
"@hypequery/datasets": "0.2.0"
|
|
55
65
|
},
|
|
56
66
|
"ts-node": {
|
|
57
67
|
"esm": true,
|