Files
smartclickhouse/ts/smartclickhouse.types.ts

135 lines
4.4 KiB
TypeScript

// ============================================================
// Column Data Types
// ============================================================
export type TClickhouseColumnType =
| 'String'
| 'UInt8' | 'UInt16' | 'UInt32' | 'UInt64'
| 'Int8' | 'Int16' | 'Int32' | 'Int64'
| 'Float32' | 'Float64'
| 'Bool'
| 'Date' | 'Date32'
| 'DateTime' | 'DateTime64'
| 'UUID'
| 'IPv4' | 'IPv6'
| (string & {}); // allow arbitrary ClickHouse types like "DateTime64(3, 'Europe/Berlin')"
// ============================================================
// Engine Configuration
// ============================================================
export type TClickhouseEngine =
| 'MergeTree'
| 'ReplacingMergeTree'
| 'SummingMergeTree'
| 'AggregatingMergeTree'
| 'CollapsingMergeTree'
| 'VersionedCollapsingMergeTree';
export interface IEngineConfig {
engine: TClickhouseEngine;
/** For ReplacingMergeTree: the version column name */
versionColumn?: string;
/** For CollapsingMergeTree: the sign column name */
signColumn?: string;
}
// ============================================================
// Column Definition
// ============================================================
export interface IColumnDefinition {
name: string;
type: TClickhouseColumnType;
defaultExpression?: string;
codec?: string;
}
// ============================================================
// Table Options
// ============================================================
export interface IClickhouseTableOptions<T = any> {
tableName: string;
database?: string;
engine?: IEngineConfig;
orderBy: (keyof T & string) | (keyof T & string)[];
partitionBy?: string;
primaryKey?: (keyof T & string) | (keyof T & string)[];
ttl?: {
column: keyof T & string;
interval: string; // e.g., '30 DAY', '1 MONTH'
};
columns?: IColumnDefinition[];
/** Enable auto-schema evolution (add columns from data). Default: true */
autoSchemaEvolution?: boolean;
/** Data retention in days (shorthand for ttl). If ttl is set, this is ignored. */
retainDataForDays?: number;
}
// ============================================================
// Column Info from system.columns
// ============================================================
export interface IColumnInfo {
database: string;
table: string;
name: string;
type: string;
position: string;
default_kind: string;
default_expression: string;
data_compressed_bytes: string;
data_uncompressed_bytes: string;
marks_bytes: string;
comment: string;
is_in_partition_key: 0 | 1;
is_in_sorting_key: 0 | 1;
is_in_primary_key: 0 | 1;
is_in_sampling_key: 0 | 1;
compression_codec: string;
}
// ============================================================
// Comparison Operators for Query Builder
// ============================================================
export type TComparisonOperator =
| '='
| '!='
| '>'
| '>='
| '<'
| '<='
| 'LIKE'
| 'NOT LIKE'
| 'IN'
| 'NOT IN'
| 'BETWEEN';
// ============================================================
// Value Escaping (SQL Injection Prevention)
// ============================================================
export function escapeClickhouseValue(value: any): string {
if (value === null || value === undefined) return 'NULL';
if (typeof value === 'number') return String(value);
if (typeof value === 'boolean') return value ? '1' : '0';
if (value instanceof Date) return `'${value.toISOString().replace('T', ' ').replace('Z', '')}'`;
if (Array.isArray(value)) {
return `(${value.map(escapeClickhouseValue).join(', ')})`;
}
// String: escape single quotes and backslashes
return `'${String(value).replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
}
// ============================================================
// ClickHouse Type Detection from JS Values
// ============================================================
export function detectClickhouseType(value: any): TClickhouseColumnType | null {
if (value === null || value === undefined) return null;
if (typeof value === 'string') return 'String';
if (typeof value === 'number') return 'Float64';
if (typeof value === 'boolean') return 'UInt8';
if (value instanceof Array) {
if (value.length === 0) return null;
const elementType = detectClickhouseType(value[0]);
if (!elementType) return null;
return `Array(${elementType})` as TClickhouseColumnType;
}
return null;
}