@tmlmobilidade/databases 20260323.400.54

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 (57) hide show
  1. package/dist/clients/go-clickhouse.d.ts +30 -0
  2. package/dist/clients/go-clickhouse.js +110 -0
  3. package/dist/clients/go-mongo.d.ts +30 -0
  4. package/dist/clients/go-mongo.js +117 -0
  5. package/dist/clients/index.d.ts +5 -0
  6. package/dist/clients/index.js +5 -0
  7. package/dist/clients/pcgidb-ticketing.d.ts +30 -0
  8. package/dist/clients/pcgidb-ticketing.js +117 -0
  9. package/dist/clients/pcgidb-validations.d.ts +29 -0
  10. package/dist/clients/pcgidb-validations.js +116 -0
  11. package/dist/clients/rawdb.d.ts +29 -0
  12. package/dist/clients/rawdb.js +116 -0
  13. package/dist/index.d.ts +4 -0
  14. package/dist/index.js +4 -0
  15. package/dist/interfaces/index.d.ts +2 -0
  16. package/dist/interfaces/index.js +2 -0
  17. package/dist/interfaces/simplified-apex/index.d.ts +4 -0
  18. package/dist/interfaces/simplified-apex/index.js +4 -0
  19. package/dist/interfaces/simplified-apex/simplified-apex-locations.d.ts +38 -0
  20. package/dist/interfaces/simplified-apex/simplified-apex-locations.js +57 -0
  21. package/dist/interfaces/simplified-apex/simplified-apex-on-board-refunds.d.ts +48 -0
  22. package/dist/interfaces/simplified-apex/simplified-apex-on-board-refunds.js +67 -0
  23. package/dist/interfaces/simplified-apex/simplified-apex-on-board-sales.d.ts +49 -0
  24. package/dist/interfaces/simplified-apex/simplified-apex-on-board-sales.js +68 -0
  25. package/dist/interfaces/simplified-apex/simplified-apex-validations.d.ts +47 -0
  26. package/dist/interfaces/simplified-apex/simplified-apex-validations.js +67 -0
  27. package/dist/interfaces/simplified-vehicle-events/index.d.ts +1 -0
  28. package/dist/interfaces/simplified-vehicle-events/index.js +1 -0
  29. package/dist/interfaces/simplified-vehicle-events/simplified-vehicle-events.d.ts +39 -0
  30. package/dist/interfaces/simplified-vehicle-events/simplified-vehicle-events.js +61 -0
  31. package/dist/templates/clickhouse.d.ts +107 -0
  32. package/dist/templates/clickhouse.js +201 -0
  33. package/dist/types/clickhouse/column.d.ts +36 -0
  34. package/dist/types/clickhouse/column.js +2 -0
  35. package/dist/types/clickhouse/data-types.d.ts +5 -0
  36. package/dist/types/clickhouse/data-types.js +1 -0
  37. package/dist/types/clickhouse/index.d.ts +3 -0
  38. package/dist/types/clickhouse/index.js +3 -0
  39. package/dist/types/clickhouse/table-engines.d.ts +7 -0
  40. package/dist/types/clickhouse/table-engines.js +1 -0
  41. package/dist/types/index.d.ts +1 -0
  42. package/dist/types/index.js +1 -0
  43. package/dist/utils/get-clickhouse-param-type.d.ts +7 -0
  44. package/dist/utils/get-clickhouse-param-type.js +16 -0
  45. package/dist/utils/index.d.ts +3 -0
  46. package/dist/utils/index.js +3 -0
  47. package/dist/utils/prepare-named-query-params.d.ts +16 -0
  48. package/dist/utils/prepare-named-query-params.js +47 -0
  49. package/dist/utils/prepare-positional-query-params.d.ts +13 -0
  50. package/dist/utils/prepare-positional-query-params.js +36 -0
  51. package/dist/utils/query-from-file.d.ts +17 -0
  52. package/dist/utils/query-from-file.js +42 -0
  53. package/dist/utils/query-from-string.d.ts +14 -0
  54. package/dist/utils/query-from-string.js +31 -0
  55. package/dist/utils/validate-sql-param.d.ts +12 -0
  56. package/dist/utils/validate-sql-param.js +23 -0
  57. package/package.json +53 -0
@@ -0,0 +1,61 @@
1
+ /* * */
2
+ import { GOClickHouseClient } from '../../clients/go-clickhouse.js';
3
+ import { ClickHouseInterfaceTemplate } from '../../templates/clickhouse.js';
4
+ import { asyncSingletonProxy } from '@tmlmobilidade/utils';
5
+ /* * */
6
+ const tableSchema = [
7
+ // Required Fields
8
+ { name: '_id', type: 'String' },
9
+ { name: 'agency_id', type: 'String' },
10
+ { name: 'created_at', type: 'UInt64' },
11
+ { name: 'latitude', type: 'Float64' },
12
+ { name: 'longitude', type: 'Float64' },
13
+ { name: 'received_at', type: 'UInt64' },
14
+ { name: 'trip_id', type: 'String' },
15
+ { name: 'vehicle_id', type: 'String' },
16
+ // Optional Fields
17
+ { name: 'bearing', type: 'Nullable(Float64)' },
18
+ { name: 'current_status', type: 'Nullable(String)' },
19
+ { name: 'door', type: 'Nullable(String)' },
20
+ { name: 'driver_id', type: 'Nullable(String)' },
21
+ { name: 'extra_trip_id', type: 'Nullable(String)' },
22
+ { name: 'odometer', type: 'Nullable(Float64)' },
23
+ { name: 'pattern_id', type: 'Nullable(String)' },
24
+ { name: 'stop_id', type: 'Nullable(String)' },
25
+ ];
26
+ /* * */
27
+ class SimplifiedVehicleEventsNewClass extends ClickHouseInterfaceTemplate {
28
+ //
29
+ static _instance = null;
30
+ databaseName = 'operation';
31
+ schema = tableSchema;
32
+ tableName = 'simplified_vehicle_events';
33
+ /**
34
+ * Returns the singleton instance of the subclass.
35
+ */
36
+ static async getInstance() {
37
+ // If no instance exists, create one and store the promise.
38
+ // This ensures that if multiple calls to getInstance() happen concurrently,
39
+ // they will all await the same initialization process.
40
+ if (!this._instance) {
41
+ this._instance = (async () => {
42
+ const instance = new SimplifiedVehicleEventsNewClass();
43
+ // This behaves like the constructor,
44
+ // but allows for async initialization.
45
+ await instance.init();
46
+ return instance;
47
+ })();
48
+ }
49
+ // Await the instance if it's still initializing,
50
+ // or return it immediately if ready.
51
+ return await this._instance;
52
+ }
53
+ connectToClient() {
54
+ return GOClickHouseClient.getClient();
55
+ }
56
+ async postInit() {
57
+ console.log('Post init ClickHouse service for Simplified Vehicle Events...');
58
+ }
59
+ }
60
+ /* * */
61
+ export const simplifiedVehicleEventsNew = asyncSingletonProxy(SimplifiedVehicleEventsNewClass);
@@ -0,0 +1,107 @@
1
+ import { type ClickHouseColumn, type ClickHouseTableEngine } from '../types/index.js';
2
+ import { type ClickHouseClient, type DataFormat } from '@clickhouse/client';
3
+ export declare abstract class ClickHouseInterfaceTemplate<T> {
4
+ abstract readonly databaseName: string;
5
+ readonly engine: ClickHouseTableEngine;
6
+ readonly orderBy: string;
7
+ abstract readonly schema: ClickHouseColumn<T>[];
8
+ abstract readonly tableName: string;
9
+ private client;
10
+ /**
11
+ * Disallow direct instantiation of the service.
12
+ * Use getClient() instead to ensure singleton behavior.
13
+ */
14
+ protected constructor();
15
+ /**
16
+ * Executes a COUNT query on the ClickHouse table using the service's client.
17
+ * @param select The columns to select in the query (e.g., `"*"`, `"column1, column2"`).
18
+ * @param where The WHERE clause to filter the results (e.g., `"id = 1"`).
19
+ * @param params Optional key-value substitutions applied to the WHERE clause (replaces $1, $2, etc.).
20
+ * @returns A promise that resolves to an array of results matching the query.
21
+ */
22
+ count(select: string, where: string, params?: Record<string, number | string>): Promise<number>;
23
+ /**
24
+ * Executes a DELETE query on the ClickHouse table using the service's client.
25
+ * @param where The WHERE clause to filter the results (e.g., `"id = 1"`).
26
+ * @param params Optional key-value substitutions applied to the WHERE clause (replaces $1, $2, etc.).
27
+ * @returns A promise that resolves when the delete operation is complete.
28
+ */
29
+ delete(where: string, params?: Record<string, number | string>): Promise<void>;
30
+ /**
31
+ * Executes a DISTINCT query on the ClickHouse table using the service's client.
32
+ * @param select The columns to select in the query (e.g., `"*"`, `"column1, column2"`).
33
+ * @param where The WHERE clause to filter the results (e.g., `"id = 1"`).
34
+ * @param params Optional key-value substitutions applied to the WHERE clause (replaces $1, $2, etc.).
35
+ * @returns A promise that resolves to an array of distinct values matching the query.
36
+ */
37
+ distinct<T>(field: keyof T, where: string, params?: Record<string, number | string>): Promise<T[keyof T][]>;
38
+ /**
39
+ * Provides access to the ClickHouse client instance,
40
+ * initializing it if it has not already been created.
41
+ * @returns A promise that resolves to the ClickHouse client instance.
42
+ * @warning Use with caution: direct access to the client allows for executing arbitrary queries.
43
+ */
44
+ getClient(): Promise<ClickHouseClient>;
45
+ /**
46
+ * Returns the name of the database used by this service.
47
+ */
48
+ getDatabaseName(): Promise<string>;
49
+ /**
50
+ * Returns the name of the table used by this service.
51
+ */
52
+ getTableName(): Promise<string>;
53
+ /**
54
+ * Inserts data into a specified ClickHouse table using the service's client.
55
+ * @param format The format of the data being inserted (default is 'JSONEachRow').
56
+ * @param values An array of data objects to insert into the table.
57
+ * @returns A promise that resolves when the data is inserted successfully.
58
+ */
59
+ insert<T>(format: DataFormat, values: T[]): Promise<import("@clickhouse/client").InsertResult>;
60
+ /**
61
+ * Executes a simple SELECT query on the ClickHouse table using the service's client.
62
+ * @param select The columns to select in the query (e.g., `"*"`, `"column1, column2"`).
63
+ * @param where The WHERE clause to filter the results (e.g., `"id = 1"`).
64
+ * @param params Optional key-value substitutions applied to the WHERE clause (replaces $1, $2, etc.).
65
+ * @returns A promise that resolves to an array of results matching the query.
66
+ */
67
+ select(select: string, where: string, params?: Record<string, number | string>): Promise<T[]>;
68
+ /**
69
+ * Abstract method to establish a connection to ClickHouse.
70
+ * This method must be implemented by subclasses to define the specific connection logic,
71
+ * such as handling SSH tunneling or configuring connection parameters.
72
+ */
73
+ protected abstract connectToClient(): Promise<ClickHouseClient>;
74
+ /**
75
+ * Initializes the ClickHouse client and ensures that the specified database and table exist.
76
+ * This method should be called before performing any operations on the database or table.
77
+ * It handles the asynchronous setup process and logs any errors that occur during initialization.
78
+ * @throws Will throw an error if the client initialization or database/table setup fails.
79
+ * @returns A promise that resolves when the initialization process is complete.
80
+ */
81
+ protected init(): Promise<void>;
82
+ /**
83
+ * Optional override for custom setup logic:
84
+ * indexes, materialized views, constraints, etc.
85
+ */
86
+ protected postInit(): Promise<void>;
87
+ /**
88
+ * Ensures that the specified database exists in ClickHouse, creating it if it does not already exist.
89
+ * This method performs input validation to prevent SQL injection and logs the outcome of the operation.
90
+ * @throws Will throw an error if the database name is unsafe or if the database creation query fails.
91
+ * @returns A promise that resolves when the database is ensured to exist.
92
+ */
93
+ private ensureDatabase;
94
+ /**
95
+ * Ensures that the specified table exists in ClickHouse, creating it if it does not already exist.
96
+ * This method performs input validation to prevent SQL injection and logs the outcome of the operation.
97
+ * It constructs a CREATE TABLE query based on the provided schema and engine type, and executes it using the client.
98
+ * @throws Will throw an error if any of the inputs are unsafe or if the table creation query fails.
99
+ * @returns A promise that resolves when the table is ensured to exist.
100
+ */
101
+ private ensureTable;
102
+ /**
103
+ * Constructs the appropriate engine string based on the provided engine type.
104
+ * @throws Will throw an error if an unsupported engine type is provided.
105
+ */
106
+ private getEngineString;
107
+ }
@@ -0,0 +1,201 @@
1
+ /* * */
2
+ import { preparePositionalQueryParams } from '../utils/prepare-positional-query-params.js';
3
+ import { queryFromString } from '../utils/query-from-string.js';
4
+ import { validateSqlParam } from '../utils/validate-sql-param.js';
5
+ import { Logger } from '@tmlmobilidade/logger';
6
+ /* * */
7
+ export class ClickHouseInterfaceTemplate {
8
+ engine = 'ReplicatedMergeTree';
9
+ orderBy = '_id';
10
+ client;
11
+ /**
12
+ * Disallow direct instantiation of the service.
13
+ * Use getClient() instead to ensure singleton behavior.
14
+ */
15
+ constructor() { }
16
+ /**
17
+ * Executes a COUNT query on the ClickHouse table using the service's client.
18
+ * @param select The columns to select in the query (e.g., `"*"`, `"column1, column2"`).
19
+ * @param where The WHERE clause to filter the results (e.g., `"id = 1"`).
20
+ * @param params Optional key-value substitutions applied to the WHERE clause (replaces $1, $2, etc.).
21
+ * @returns A promise that resolves to an array of results matching the query.
22
+ */
23
+ async count(select, where, params) {
24
+ const result = await queryFromString(this.client, `SELECT COUNT(${select}) AS count FROM "${this.databaseName}"."${this.tableName}" WHERE ${where}`, params);
25
+ return result.length > 0 ? result[0].count : 0;
26
+ }
27
+ /**
28
+ * Executes a DELETE query on the ClickHouse table using the service's client.
29
+ * @param where The WHERE clause to filter the results (e.g., `"id = 1"`).
30
+ * @param params Optional key-value substitutions applied to the WHERE clause (replaces $1, $2, etc.).
31
+ * @returns A promise that resolves when the delete operation is complete.
32
+ */
33
+ async delete(where, params) {
34
+ const preparedQuery = preparePositionalQueryParams(`DELETE FROM "${this.databaseName}"."${this.tableName}" WHERE ${where}`, params);
35
+ await this.client.command({
36
+ query: preparedQuery.query,
37
+ query_params: preparedQuery.query_params,
38
+ });
39
+ }
40
+ /**
41
+ * Executes a DISTINCT query on the ClickHouse table using the service's client.
42
+ * @param select The columns to select in the query (e.g., `"*"`, `"column1, column2"`).
43
+ * @param where The WHERE clause to filter the results (e.g., `"id = 1"`).
44
+ * @param params Optional key-value substitutions applied to the WHERE clause (replaces $1, $2, etc.).
45
+ * @returns A promise that resolves to an array of distinct values matching the query.
46
+ */
47
+ async distinct(field, where, params) {
48
+ const result = await queryFromString(this.client, `SELECT ${String(field)} FROM "${this.databaseName}"."${this.tableName}" WHERE ${where}`, params);
49
+ return result.map(doc => doc[field]);
50
+ }
51
+ /**
52
+ * Provides access to the ClickHouse client instance,
53
+ * initializing it if it has not already been created.
54
+ * @returns A promise that resolves to the ClickHouse client instance.
55
+ * @warning Use with caution: direct access to the client allows for executing arbitrary queries.
56
+ */
57
+ async getClient() {
58
+ if (!this.client)
59
+ await this.init();
60
+ return this.client;
61
+ }
62
+ /**
63
+ * Returns the name of the database used by this service.
64
+ */
65
+ async getDatabaseName() {
66
+ return this.databaseName;
67
+ }
68
+ /**
69
+ * Returns the name of the table used by this service.
70
+ */
71
+ async getTableName() {
72
+ return this.tableName;
73
+ }
74
+ /**
75
+ * Inserts data into a specified ClickHouse table using the service's client.
76
+ * @param format The format of the data being inserted (default is 'JSONEachRow').
77
+ * @param values An array of data objects to insert into the table.
78
+ * @returns A promise that resolves when the data is inserted successfully.
79
+ */
80
+ async insert(format = 'JSONEachRow', values) {
81
+ return this.client.insert({
82
+ format: format,
83
+ table: `"${this.databaseName}"."${this.tableName}"`,
84
+ values: values,
85
+ });
86
+ }
87
+ /**
88
+ * Executes a simple SELECT query on the ClickHouse table using the service's client.
89
+ * @param select The columns to select in the query (e.g., `"*"`, `"column1, column2"`).
90
+ * @param where The WHERE clause to filter the results (e.g., `"id = 1"`).
91
+ * @param params Optional key-value substitutions applied to the WHERE clause (replaces $1, $2, etc.).
92
+ * @returns A promise that resolves to an array of results matching the query.
93
+ */
94
+ async select(select, where, params) {
95
+ return await queryFromString(this.client, `SELECT ${select} FROM "${this.databaseName}"."${this.tableName}" WHERE ${where}`, params);
96
+ }
97
+ /**
98
+ * Initializes the ClickHouse client and ensures that the specified database and table exist.
99
+ * This method should be called before performing any operations on the database or table.
100
+ * It handles the asynchronous setup process and logs any errors that occur during initialization.
101
+ * @throws Will throw an error if the client initialization or database/table setup fails.
102
+ * @returns A promise that resolves when the initialization process is complete.
103
+ */
104
+ async init() {
105
+ // Skip if already initialized
106
+ if (this.client)
107
+ return;
108
+ // Validate required properties before attempting to connect
109
+ if (!this.databaseName)
110
+ throw new Error('CLICKHOUSE: databaseName is required.');
111
+ if (!this.tableName)
112
+ throw new Error('CLICKHOUSE: tableName is required.');
113
+ if (!this.schema || this.schema.length === 0)
114
+ throw new Error('CLICKHOUSE: schema is required and cannot be empty.');
115
+ // Connect to the ClickHouse client
116
+ this.client = await this.connectToClient();
117
+ // Ensure the database and table exist, and perform any additional setup
118
+ await this.ensureDatabase();
119
+ await this.ensureTable();
120
+ await this.postInit();
121
+ }
122
+ /**
123
+ * Optional override for custom setup logic:
124
+ * indexes, materialized views, constraints, etc.
125
+ */
126
+ async postInit() {
127
+ // no-op by default
128
+ }
129
+ /**
130
+ * Ensures that the specified database exists in ClickHouse, creating it if it does not already exist.
131
+ * This method performs input validation to prevent SQL injection and logs the outcome of the operation.
132
+ * @throws Will throw an error if the database name is unsafe or if the database creation query fails.
133
+ * @returns A promise that resolves when the database is ensured to exist.
134
+ */
135
+ async ensureDatabase() {
136
+ // Validate the inputs are safe identifiers to prevent SQL injection
137
+ if (!validateSqlParam(this.databaseName, false))
138
+ throw new Error(`CLICKHOUSE [${this.databaseName}]: Unsafe database name provided.`);
139
+ // Perform the query to create the database if it does not exist
140
+ try {
141
+ await this.client.command({ query: `CREATE DATABASE IF NOT EXISTS "${this.databaseName}" on CLUSTER default_cluster;` });
142
+ Logger.info(`CLICKHOUSE [${this.databaseName}]: Database created.`);
143
+ }
144
+ catch (error) {
145
+ Logger.error(`CLICKHOUSE [${this.databaseName}]: Error @ createDatabase(): ${error.message}`);
146
+ throw error;
147
+ }
148
+ }
149
+ /**
150
+ * Ensures that the specified table exists in ClickHouse, creating it if it does not already exist.
151
+ * This method performs input validation to prevent SQL injection and logs the outcome of the operation.
152
+ * It constructs a CREATE TABLE query based on the provided schema and engine type, and executes it using the client.
153
+ * @throws Will throw an error if any of the inputs are unsafe or if the table creation query fails.
154
+ * @returns A promise that resolves when the table is ensured to exist.
155
+ */
156
+ async ensureTable() {
157
+ // Validate the inputs are safe identifiers to prevent SQL injection
158
+ if (!validateSqlParam(this.databaseName, false))
159
+ throw new Error(`CLICKHOUSE [${this.databaseName}]: Unsafe database name provided.`);
160
+ if (!validateSqlParam(this.tableName, false))
161
+ throw new Error(`CLICKHOUSE [${this.tableName}]: Unsafe table name provided.`);
162
+ if (!validateSqlParam(this.engine, false))
163
+ throw new Error(`CLICKHOUSE [${this.engine}]: Unsafe engine type provided.`);
164
+ if (!validateSqlParam(this.orderBy, false))
165
+ throw new Error(`CLICKHOUSE [${this.orderBy}]: Unsafe orderBy clause provided.`);
166
+ // Validate the schema columns are safe identifiers
167
+ const unsafeColumns = this.schema.filter(column => !validateSqlParam(column.name, false)).map(column => column.name);
168
+ if (unsafeColumns.length > 0)
169
+ throw new Error(`CLICKHOUSE [${this.tableName}]: Unsafe column names provided: ${unsafeColumns.join(', ')}.`);
170
+ // Ensure the database exists before creating the table
171
+ await this.ensureDatabase();
172
+ // Setup the full CREATE TABLE query
173
+ const createTableQuery = `
174
+ CREATE TABLE IF NOT EXISTS "${this.databaseName}"."${this.tableName}" ON CLUSTER default_cluster (
175
+ ${this.schema.map(column => `${column.name} ${column.type}`).join(', ')}
176
+ ) ENGINE = ${this.getEngineString()}
177
+ ORDER BY ${this.orderBy}
178
+ `;
179
+ // Perform the query to create the table
180
+ try {
181
+ await this.client.command({ query: createTableQuery });
182
+ Logger.info(`CLICKHOUSE [${this.tableName}]: Table created.`);
183
+ }
184
+ catch (error) {
185
+ Logger.error(`CLICKHOUSE [${this.tableName}]: Error @ createTable(): ${error.message}`);
186
+ throw error;
187
+ }
188
+ }
189
+ /**
190
+ * Constructs the appropriate engine string based on the provided engine type.
191
+ * @throws Will throw an error if an unsupported engine type is provided.
192
+ */
193
+ getEngineString() {
194
+ switch (this.engine) {
195
+ case 'ReplicatedMergeTree':
196
+ return `ReplicatedMergeTree('/clickhouse/tables/{shard}/${this.databaseName}/${this.tableName}', '{replica}')`;
197
+ default:
198
+ throw new Error(`CLICKHOUSE [${this.databaseName}/${this.tableName}]: Unsupported engine type: ${this.engine}`);
199
+ }
200
+ }
201
+ }
@@ -0,0 +1,36 @@
1
+ import { type ClickHouseDataType } from './data-types.js';
2
+ /**
3
+ * Definition of a ClickHouse column,
4
+ * including its name, type, and various optional properties.
5
+ */
6
+ export interface ClickHouseColumn<T> {
7
+ /** Alias expression (computed on read) */
8
+ alias?: string;
9
+ /** Column codec for compression */
10
+ codec?: string;
11
+ /** Comment for the column */
12
+ comment?: string;
13
+ /** Default value expression */
14
+ default?: string;
15
+ /** Create a secondary index (skipping index) on this column */
16
+ indexed?: boolean;
17
+ /** Granularity for the index. Default: 4 */
18
+ indexGranularity?: number;
19
+ /** Type of skipping index. Default: 'minmax' */
20
+ indexType?: 'bloom_filter' | 'minmax' | 'ngrambf_v1' | 'set' | 'tokenbf_v1';
21
+ /** Use LowCardinality wrapper for low-cardinality strings */
22
+ lowCardinality?: boolean;
23
+ /** Materialized value expression (computed on insert) */
24
+ materialized?: string;
25
+ name: Extract<keyof T, string>;
26
+ /** Whether the column can be null (wraps type in Nullable) */
27
+ nullable?: boolean;
28
+ /** Include this column in the ORDER BY clause (ClickHouse's primary index) */
29
+ primaryKey?: boolean;
30
+ /** Order of this column in the primary key (lower = first). Default: 0 */
31
+ primaryKeyOrder?: number;
32
+ /** TTL expression for this column */
33
+ ttl?: string;
34
+ /** The ClickHouse data type */
35
+ type: ClickHouseDataType;
36
+ }
@@ -0,0 +1,2 @@
1
+ /* * */
2
+ export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Supported ClickHouse data types that
3
+ * can be used in ClickHouse table schemas.
4
+ */
5
+ export type ClickHouseDataType = 'Bool' | 'Boolean' | 'Date32' | 'Date' | 'DateTime' | 'Decimal' | 'Float32' | 'Float64' | 'Int8' | 'Int16' | 'Int32' | 'Int64' | 'Int128' | 'Int256' | 'String' | 'UInt8' | 'UInt16' | 'UInt32' | 'UInt64' | 'UInt128' | 'UInt256' | 'UUID' | `Array(${string})` | `DateTime64(${number})` | `Decimal(${number}, ${number})` | `Enum8(${string})` | `Enum16(${string})` | `FixedString(${number})` | `LowCardinality(${string})` | `Map(${string}, ${string})` | `Nullable(${string})`;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from './column.js';
2
+ export * from './data-types.js';
3
+ export * from './table-engines.js';
@@ -0,0 +1,3 @@
1
+ export * from './column.js';
2
+ export * from './data-types.js';
3
+ export * from './table-engines.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Definition for allowed ClickHouse table engines.
3
+ * Please avoid using other engines before consulting with the team
4
+ * as ClickHouse has many engines with different features and limitations.
5
+ * For example, only `ReplicatedMergeTree` supports automatic replication.
6
+ */
7
+ export type ClickHouseTableEngine = 'ReplicatedMergeTree';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from './clickhouse/index.js';
@@ -0,0 +1 @@
1
+ export * from './clickhouse/index.js';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Determines the appropriate ClickHouse parameter type based on the value's JavaScript type.
3
+ * This is used to convert untyped query placeholders into typed ClickHouse parameters.
4
+ * @param value
5
+ * @returns
6
+ */
7
+ export declare function getClickHouseParamType(value: number | string): 'Float64' | 'Int64' | 'String';
@@ -0,0 +1,16 @@
1
+ /* * */
2
+ /**
3
+ * Determines the appropriate ClickHouse parameter type based on the value's JavaScript type.
4
+ * This is used to convert untyped query placeholders into typed ClickHouse parameters.
5
+ * @param value
6
+ * @returns
7
+ */
8
+ export function getClickHouseParamType(value) {
9
+ if (typeof value === 'number') {
10
+ if (!Number.isFinite(value)) {
11
+ throw new Error('CLICKHOUSE: Query params do not support non-finite numbers.');
12
+ }
13
+ return Number.isInteger(value) ? 'Int64' : 'Float64';
14
+ }
15
+ return 'String';
16
+ }
@@ -0,0 +1,3 @@
1
+ export * from './prepare-named-query-params.js';
2
+ export * from './prepare-positional-query-params.js';
3
+ export * from './validate-sql-param.js';
@@ -0,0 +1,3 @@
1
+ export * from './prepare-named-query-params.js';
2
+ export * from './prepare-positional-query-params.js';
3
+ export * from './validate-sql-param.js';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Prepares a SQL query with named parameters by validating the parameter keys
3
+ * and ensuring that all provided parameters are used in the query. It also converts untyped
4
+ * placeholders (e.g., {name}) into typed ClickHouse parameters (e.g., {name:Type}) based on
5
+ * the value type. This function is essential for safely constructing SQL queries with dynamic
6
+ * parameters while preventing SQL injection vulnerabilities.
7
+ * @param query The SQL query string containing named parameters in the format {paramName} or {paramName:Type}.
8
+ * @param params An optional object mapping parameter names to their values. The keys must be valid SQL parameter names.
9
+ * @param context An optional string providing context for error messages (e.g., the name of the query or operation).
10
+ * @throws Will throw an error if any parameter key is invalid, if there are missing parameters required by the query, or if there are unused parameters provided.
11
+ * @returns An object containing the normalized query string with typed parameters and a mapping of parameter names to their values.
12
+ */
13
+ export declare function prepareNamedQueryParams(query: string, params?: Record<string, number | string>, context?: string): {
14
+ query: string;
15
+ queryParams: Record<string, number | string>;
16
+ };
@@ -0,0 +1,47 @@
1
+ /* * */
2
+ import { validateSqlParam } from './validate-sql-param.js';
3
+ /**
4
+ * Prepares a SQL query with named parameters by validating the parameter keys
5
+ * and ensuring that all provided parameters are used in the query. It also converts untyped
6
+ * placeholders (e.g., {name}) into typed ClickHouse parameters (e.g., {name:Type}) based on
7
+ * the value type. This function is essential for safely constructing SQL queries with dynamic
8
+ * parameters while preventing SQL injection vulnerabilities.
9
+ * @param query The SQL query string containing named parameters in the format {paramName} or {paramName:Type}.
10
+ * @param params An optional object mapping parameter names to their values. The keys must be valid SQL parameter names.
11
+ * @param context An optional string providing context for error messages (e.g., the name of the query or operation).
12
+ * @throws Will throw an error if any parameter key is invalid, if there are missing parameters required by the query, or if there are unused parameters provided.
13
+ * @returns An object containing the normalized query string with typed parameters and a mapping of parameter names to their values.
14
+ */
15
+ export function prepareNamedQueryParams(query, params, context) {
16
+ const queryParams = {};
17
+ const providedParams = params ?? {};
18
+ const usedKeys = new Set();
19
+ for (const key of Object.keys(providedParams)) {
20
+ validateSqlParam(key);
21
+ }
22
+ // Backward compatibility: convert untyped placeholders ({name}) into typed ClickHouse params.
23
+ const normalizedQuery = query.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_, key) => {
24
+ if (!(key in providedParams)) {
25
+ throw new Error(`CLICKHOUSE "${context ?? 'query'}": Missing query param: ${key}`);
26
+ }
27
+ usedKeys.add(key);
28
+ const value = providedParams[key];
29
+ queryParams[key] = value;
30
+ return `{${key}:${this.getClickHouseParamType(value)}}`;
31
+ });
32
+ // Also include explicitly typed placeholders already present in query (e.g. {id:UInt64}).
33
+ for (const match of normalizedQuery.matchAll(/\{([A-Za-z_][A-Za-z0-9_]*):[^}]+\}/g)) {
34
+ const key = match[1];
35
+ if (!(key in providedParams)) {
36
+ throw new Error(`CLICKHOUSE "${context ?? 'query'}": Missing query param: ${key}`);
37
+ }
38
+ usedKeys.add(key);
39
+ queryParams[key] = providedParams[key];
40
+ }
41
+ for (const key of Object.keys(providedParams)) {
42
+ if (!usedKeys.has(key)) {
43
+ throw new Error(`CLICKHOUSE "${context ?? 'query'}": Unused query param: ${key}`);
44
+ }
45
+ }
46
+ return { query: normalizedQuery, queryParams };
47
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Prepares a SQL query with positional parameters by replacing placeholders
3
+ * in the format of $1, $2, etc., with ClickHouse's named parameter syntax.
4
+ * It also validates that all provided parameters are used in the query and that their keys are valid.
5
+ * @param query The SQL query containing positional placeholders (e.g., $1, $2).
6
+ * @param params An optional object mapping parameter indices (as strings) to their values.
7
+ * @throws Will throw an error if a placeholder is missing a corresponding parameter, if there are unused parameters, or if any parameter keys are invalid.
8
+ * @returns An object containing the transformed query and a mapping of named parameters to their values.
9
+ */
10
+ export declare function preparePositionalQueryParams(query: string, params?: Record<string, number | string>): {
11
+ query: string;
12
+ query_params: Record<string, number | string>;
13
+ };
@@ -0,0 +1,36 @@
1
+ /* * */
2
+ import { getClickHouseParamType } from './get-clickhouse-param-type.js';
3
+ /**
4
+ * Prepares a SQL query with positional parameters by replacing placeholders
5
+ * in the format of $1, $2, etc., with ClickHouse's named parameter syntax.
6
+ * It also validates that all provided parameters are used in the query and that their keys are valid.
7
+ * @param query The SQL query containing positional placeholders (e.g., $1, $2).
8
+ * @param params An optional object mapping parameter indices (as strings) to their values.
9
+ * @throws Will throw an error if a placeholder is missing a corresponding parameter, if there are unused parameters, or if any parameter keys are invalid.
10
+ * @returns An object containing the transformed query and a mapping of named parameters to their values.
11
+ */
12
+ export function preparePositionalQueryParams(query, params) {
13
+ const queryParams = {};
14
+ const providedParams = params ?? {};
15
+ const usedKeys = new Set();
16
+ const normalizedQuery = query.replace(/\$(\d+)/g, (_, index) => {
17
+ const key = String(index);
18
+ if (!(key in providedParams)) {
19
+ throw new Error(`CLICKHOUSE "${query}": Missing query param: $${key}`);
20
+ }
21
+ usedKeys.add(key);
22
+ const queryParamKey = `p${key}`;
23
+ const value = providedParams[key];
24
+ queryParams[queryParamKey] = value;
25
+ return `{${queryParamKey}:${getClickHouseParamType(value)}}`;
26
+ });
27
+ for (const key of Object.keys(providedParams)) {
28
+ if (!/^\d+$/.test(key)) {
29
+ throw new Error(`CLICKHOUSE "${query}": Invalid positional query param key: "${key}"`);
30
+ }
31
+ if (!usedKeys.has(key)) {
32
+ throw new Error(`CLICKHOUSE "${query}": Unused query param: $${key}`);
33
+ }
34
+ }
35
+ return { query: normalizedQuery, query_params: queryParams };
36
+ }
@@ -0,0 +1,17 @@
1
+ import { type ClickHouseClient } from '@clickhouse/client';
2
+ /**
3
+ * Executes a query from a .sql file with optional parameter substitutions.
4
+ * @param client The ClickHouse client to use for executing the query.
5
+ * @param filePath Absolute or relative path to the .sql file.
6
+ * @param params Optional key-value substitutions applied to the query (replaces {key} placeholders).
7
+ * @returns Query result rows typed as `T`.
8
+ * @example
9
+ * // Given a SQL file "get_users.sql" with the content:
10
+ * // SELECT * FROM users WHERE created_at >= {start_date} AND created_at <= {end_date}
11
+ *
12
+ * const users = await clickhouseService.queryFromFile<User>('get_users.sql', {
13
+ * start_date: '2024-01-01',
14
+ * end_date: '2024-12-31',
15
+ * });
16
+ */
17
+ export declare function queryFromFile<T>(client: ClickHouseClient, filePath: string, params?: Record<string, number | string>): Promise<T[]>;