@housekit/orm 0.1.47 → 0.1.49
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 +120 -5
- package/dist/builders/delete.js +112 -0
- package/dist/builders/insert.d.ts +0 -91
- package/dist/builders/insert.js +393 -0
- package/dist/builders/prepared.d.ts +1 -2
- package/dist/builders/prepared.js +30 -0
- package/dist/builders/select.d.ts +0 -161
- package/dist/builders/select.js +562 -0
- package/dist/builders/select.types.js +1 -0
- package/dist/builders/update.js +136 -0
- package/dist/client.d.ts +0 -6
- package/dist/client.js +140 -0
- package/dist/codegen/zod.js +107 -0
- package/dist/column.d.ts +1 -25
- package/dist/column.js +133 -0
- package/dist/compiler.d.ts +0 -7
- package/dist/compiler.js +513 -0
- package/dist/core.js +6 -0
- package/dist/data-types.d.ts +0 -61
- package/dist/data-types.js +127 -0
- package/dist/dictionary.d.ts +0 -149
- package/dist/dictionary.js +158 -0
- package/dist/engines.d.ts +0 -385
- package/dist/engines.js +292 -0
- package/dist/expressions.d.ts +0 -10
- package/dist/expressions.js +268 -0
- package/dist/external.d.ts +0 -112
- package/dist/external.js +224 -0
- package/dist/index.d.ts +0 -51
- package/dist/index.js +139 -6853
- package/dist/logger.js +36 -0
- package/dist/materialized-views.d.ts +0 -188
- package/dist/materialized-views.js +380 -0
- package/dist/metadata.js +59 -0
- package/dist/modules/aggregates.d.ts +0 -164
- package/dist/modules/aggregates.js +121 -0
- package/dist/modules/array.d.ts +0 -98
- package/dist/modules/array.js +71 -0
- package/dist/modules/conditional.d.ts +0 -84
- package/dist/modules/conditional.js +138 -0
- package/dist/modules/conversion.d.ts +0 -147
- package/dist/modules/conversion.js +109 -0
- package/dist/modules/geo.d.ts +0 -164
- package/dist/modules/geo.js +112 -0
- package/dist/modules/hash.js +4 -0
- package/dist/modules/index.js +12 -0
- package/dist/modules/json.d.ts +0 -106
- package/dist/modules/json.js +76 -0
- package/dist/modules/math.d.ts +0 -16
- package/dist/modules/math.js +16 -0
- package/dist/modules/string.d.ts +0 -136
- package/dist/modules/string.js +89 -0
- package/dist/modules/time.d.ts +0 -123
- package/dist/modules/time.js +91 -0
- package/dist/modules/types.d.ts +0 -133
- package/dist/modules/types.js +114 -0
- package/dist/modules/window.js +140 -0
- package/dist/relational.d.ts +0 -82
- package/dist/relational.js +290 -0
- package/dist/relations.js +21 -0
- package/dist/schema-builder.d.ts +0 -90
- package/dist/schema-builder.js +140 -0
- package/dist/table.d.ts +0 -42
- package/dist/table.js +406 -0
- package/dist/utils/background-batcher.js +75 -0
- package/dist/utils/batch-transform.js +51 -0
- package/dist/utils/binary-reader.d.ts +0 -6
- package/dist/utils/binary-reader.js +334 -0
- package/dist/utils/binary-serializer.d.ts +0 -125
- package/dist/utils/binary-serializer.js +637 -0
- package/dist/utils/binary-worker-code.js +1 -0
- package/dist/utils/binary-worker-pool.d.ts +0 -34
- package/dist/utils/binary-worker-pool.js +206 -0
- package/dist/utils/binary-worker.d.ts +0 -11
- package/dist/utils/binary-worker.js +63 -0
- package/dist/utils/insert-processing.d.ts +0 -2
- package/dist/utils/insert-processing.js +163 -0
- package/dist/utils/lru-cache.js +30 -0
- package/package.json +68 -3
package/dist/client.d.ts
CHANGED
|
@@ -7,21 +7,15 @@ import { type TableDefinition, type TableRuntime, type CleanInsert } from './cor
|
|
|
7
7
|
import { type HousekitLogger } from './logger';
|
|
8
8
|
import { type RelationalAPI } from './relational';
|
|
9
9
|
interface ConnectionPoolConfig {
|
|
10
|
-
/** Maximum concurrent sockets (default: 100) */
|
|
11
10
|
maxSockets?: number;
|
|
12
|
-
/** Keep connections alive (default: true) */
|
|
13
11
|
keepAlive?: boolean;
|
|
14
|
-
/** Keep-alive initial delay in ms (default: 1000) */
|
|
15
12
|
keepAliveInitialDelay?: number;
|
|
16
|
-
/** Socket timeout in ms (default: 30000) */
|
|
17
13
|
timeout?: number;
|
|
18
14
|
}
|
|
19
15
|
export type HousekitClientConfig = ClickHouseClientConfigOptions & {
|
|
20
16
|
schema?: Record<string, TableDefinition<any>>;
|
|
21
17
|
logger?: HousekitLogger;
|
|
22
|
-
/** Connection pool configuration */
|
|
23
18
|
pool?: ConnectionPoolConfig;
|
|
24
|
-
/** Skip validation for maximum insert performance */
|
|
25
19
|
skipValidation?: boolean;
|
|
26
20
|
};
|
|
27
21
|
export type ClientConfigWithSchema = HousekitClientConfig;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createClient as createChClient } from '@clickhouse/client';
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
import http from 'http';
|
|
4
|
+
import https from 'https';
|
|
5
|
+
import { ClickHouseQueryBuilder } from './builders/select';
|
|
6
|
+
import { ClickHouseInsertBuilder } from './builders/insert';
|
|
7
|
+
import { ClickHouseDeleteBuilder } from './builders/delete';
|
|
8
|
+
import { ClickHouseUpdateBuilder } from './builders/update';
|
|
9
|
+
import { buildInsertPlan, processRowsStream } from './utils/insert-processing';
|
|
10
|
+
import { wrapClientWithLogger } from './logger';
|
|
11
|
+
import { buildRelationalAPI } from './relational';
|
|
12
|
+
const agentPool = new Map();
|
|
13
|
+
function getOrCreateAgent(url, config = {}) {
|
|
14
|
+
const isHttps = url.startsWith('https');
|
|
15
|
+
const key = `${url}-${config.maxSockets ?? 100}`;
|
|
16
|
+
let agent = agentPool.get(key);
|
|
17
|
+
if (agent)
|
|
18
|
+
return agent;
|
|
19
|
+
const agentConfig = {
|
|
20
|
+
keepAlive: config.keepAlive ?? true,
|
|
21
|
+
keepAliveMsecs: config.keepAliveInitialDelay ?? 1000,
|
|
22
|
+
maxSockets: config.maxSockets ?? 100,
|
|
23
|
+
maxFreeSockets: Math.floor((config.maxSockets ?? 100) / 2),
|
|
24
|
+
timeout: config.timeout ?? 30000,
|
|
25
|
+
};
|
|
26
|
+
agent = isHttps
|
|
27
|
+
? new https.Agent(agentConfig)
|
|
28
|
+
: new http.Agent(agentConfig);
|
|
29
|
+
agentPool.set(key, agent);
|
|
30
|
+
return agent;
|
|
31
|
+
}
|
|
32
|
+
export function createHousekitClient(config) {
|
|
33
|
+
const { schema, logger, pool, skipValidation, ...configRest } = config;
|
|
34
|
+
const normalizedConfig = { ...configRest };
|
|
35
|
+
if ('host' in normalizedConfig && !('url' in normalizedConfig)) {
|
|
36
|
+
const host = normalizedConfig.host;
|
|
37
|
+
normalizedConfig.url = host.startsWith('http://') || host.startsWith('https://')
|
|
38
|
+
? host
|
|
39
|
+
: `http://${host}`;
|
|
40
|
+
delete normalizedConfig.host;
|
|
41
|
+
}
|
|
42
|
+
const urlStr = normalizedConfig.url?.toString() ?? '';
|
|
43
|
+
const keepAliveAgent = getOrCreateAgent(urlStr, pool);
|
|
44
|
+
const clientConfig = {
|
|
45
|
+
...normalizedConfig,
|
|
46
|
+
clickhouse_settings: {
|
|
47
|
+
enable_http_compression: 1,
|
|
48
|
+
...normalizedConfig.clickhouse_settings
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
clientConfig.http_agent = keepAliveAgent;
|
|
52
|
+
const baseChClient = createChClient(clientConfig);
|
|
53
|
+
const client = wrapClientWithLogger(baseChClient, logger);
|
|
54
|
+
const tableExists = async (tableName) => {
|
|
55
|
+
const result = await client.query({
|
|
56
|
+
query: `EXISTS TABLE \`${tableName}\``,
|
|
57
|
+
format: 'JSONEachRow'
|
|
58
|
+
});
|
|
59
|
+
const rows = await result.json();
|
|
60
|
+
const exists = rows[0]?.result === 1 || rows[0]?.exists === 1;
|
|
61
|
+
return Boolean(exists);
|
|
62
|
+
};
|
|
63
|
+
const ensureTable = async (table) => {
|
|
64
|
+
const exists = await tableExists(table.$table);
|
|
65
|
+
if (!exists) {
|
|
66
|
+
await client.query({ query: table.toSQL() });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const dropTable = async (tableName, options) => {
|
|
70
|
+
const clause = options?.ifExists === false ? '' : ' IF EXISTS';
|
|
71
|
+
await client.query({ query: `DROP TABLE${clause} \`${tableName}\`` });
|
|
72
|
+
};
|
|
73
|
+
const binaryInsertConfig = {
|
|
74
|
+
url: normalizedConfig.url?.toString() || 'http://localhost:8123',
|
|
75
|
+
username: normalizedConfig.username || 'default',
|
|
76
|
+
password: normalizedConfig.password || '',
|
|
77
|
+
database: normalizedConfig.database || 'default',
|
|
78
|
+
skipValidation,
|
|
79
|
+
};
|
|
80
|
+
const baseClient = {
|
|
81
|
+
rawClient: client,
|
|
82
|
+
select: (fieldsOrTable) => {
|
|
83
|
+
const builder = new ClickHouseQueryBuilder(client);
|
|
84
|
+
if (!fieldsOrTable)
|
|
85
|
+
return builder.select();
|
|
86
|
+
if (typeof fieldsOrTable === 'object' && fieldsOrTable !== null && '$table' in fieldsOrTable) {
|
|
87
|
+
return builder.select().from(fieldsOrTable);
|
|
88
|
+
}
|
|
89
|
+
return builder.select(fieldsOrTable);
|
|
90
|
+
},
|
|
91
|
+
with: (name, query) => {
|
|
92
|
+
const builder = new ClickHouseQueryBuilder(client);
|
|
93
|
+
return builder.with(name, query);
|
|
94
|
+
},
|
|
95
|
+
insert: (table) => new ClickHouseInsertBuilder(client, table, binaryInsertConfig),
|
|
96
|
+
insertMany: async (table, values, opts) => {
|
|
97
|
+
const blockSize = Math.max(opts?.maxBlockSize || 10000, 1);
|
|
98
|
+
const plan = buildInsertPlan(table);
|
|
99
|
+
const mode = plan.useCompact ? 'compact' : 'json';
|
|
100
|
+
const processedRows = processRowsStream(values, plan, mode);
|
|
101
|
+
const stream = Readable.from(processedRows, { objectMode: true, highWaterMark: blockSize });
|
|
102
|
+
const clickhouse_settings = opts?.asyncInsertWait !== undefined
|
|
103
|
+
? { async_insert: 1, wait_for_async_insert: opts.asyncInsertWait ? 1 : 0 }
|
|
104
|
+
: undefined;
|
|
105
|
+
await client.insert({
|
|
106
|
+
table: table.$table,
|
|
107
|
+
values: stream,
|
|
108
|
+
format: mode === 'compact' ? 'JSONCompactEachRow' : 'JSONEachRow',
|
|
109
|
+
columns: mode === 'compact' ? plan.columnNames : undefined,
|
|
110
|
+
...(clickhouse_settings ? { clickhouse_settings } : {})
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
update: (table) => new ClickHouseUpdateBuilder(client, table),
|
|
114
|
+
delete: (table) => new ClickHouseDeleteBuilder(client, table),
|
|
115
|
+
command: async (params) => {
|
|
116
|
+
return client.query(params);
|
|
117
|
+
},
|
|
118
|
+
raw: async (query, params) => {
|
|
119
|
+
const resultSet = await client.query({
|
|
120
|
+
query,
|
|
121
|
+
query_params: params,
|
|
122
|
+
format: 'JSONEachRow'
|
|
123
|
+
});
|
|
124
|
+
return resultSet.json();
|
|
125
|
+
},
|
|
126
|
+
tableExists,
|
|
127
|
+
ensureTable,
|
|
128
|
+
dropTable,
|
|
129
|
+
close: () => client.close(),
|
|
130
|
+
schema: schema,
|
|
131
|
+
_config: config,
|
|
132
|
+
query: undefined
|
|
133
|
+
};
|
|
134
|
+
const relationalAPI = buildRelationalAPI(client, schema);
|
|
135
|
+
if (relationalAPI) {
|
|
136
|
+
baseClient.query = relationalAPI;
|
|
137
|
+
}
|
|
138
|
+
return baseClient;
|
|
139
|
+
}
|
|
140
|
+
export const createClientFromConfigObject = createHousekitClient;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ClickHouseColumn } from '../column';
|
|
3
|
+
function mapClickHouseTypeToZod(column) {
|
|
4
|
+
const type = column.type.toLowerCase();
|
|
5
|
+
let zodType;
|
|
6
|
+
if (type.startsWith('nullable(')) {
|
|
7
|
+
const innerType = column.type.slice('nullable('.length, -1);
|
|
8
|
+
const innerColumn = new ClickHouseColumn(column.name, innerType);
|
|
9
|
+
zodType = mapClickHouseTypeToZod(innerColumn).nullable();
|
|
10
|
+
}
|
|
11
|
+
else if (type.startsWith('array(')) {
|
|
12
|
+
const innerType = column.type.slice('array('.length, -1);
|
|
13
|
+
const innerColumn = new ClickHouseColumn(column.name, innerType);
|
|
14
|
+
zodType = z.array(mapClickHouseTypeToZod(innerColumn));
|
|
15
|
+
}
|
|
16
|
+
else if (type.startsWith('fixedstring')) {
|
|
17
|
+
zodType = z.string();
|
|
18
|
+
}
|
|
19
|
+
else if (type.startsWith('enum')) {
|
|
20
|
+
if (column.meta?.enumValues) {
|
|
21
|
+
zodType = z.enum(column.meta.enumValues);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
zodType = z.string();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else if (type.startsWith('datetime64')) {
|
|
28
|
+
zodType = z.date();
|
|
29
|
+
}
|
|
30
|
+
else if (type.startsWith('decimal')) {
|
|
31
|
+
zodType = z.string().refine((val) => !isNaN(parseFloat(val)), {
|
|
32
|
+
message: "Must be a valid decimal number string",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
switch (type) {
|
|
37
|
+
case 'uuid':
|
|
38
|
+
case 'string':
|
|
39
|
+
case 'json':
|
|
40
|
+
case 'ipv4':
|
|
41
|
+
case 'ipv6':
|
|
42
|
+
zodType = z.string();
|
|
43
|
+
break;
|
|
44
|
+
case 'int8':
|
|
45
|
+
case 'uint8':
|
|
46
|
+
case 'int16':
|
|
47
|
+
case 'uint16':
|
|
48
|
+
case 'int32':
|
|
49
|
+
case 'uint32':
|
|
50
|
+
case 'float32':
|
|
51
|
+
case 'float64':
|
|
52
|
+
zodType = z.number();
|
|
53
|
+
break;
|
|
54
|
+
case 'int64':
|
|
55
|
+
case 'uint64':
|
|
56
|
+
case 'int128':
|
|
57
|
+
case 'uint128':
|
|
58
|
+
case 'int256':
|
|
59
|
+
case 'uint256':
|
|
60
|
+
zodType = z.bigint();
|
|
61
|
+
break;
|
|
62
|
+
case 'boolean':
|
|
63
|
+
case 'bool':
|
|
64
|
+
zodType = z.boolean();
|
|
65
|
+
break;
|
|
66
|
+
case 'date':
|
|
67
|
+
case 'datetime':
|
|
68
|
+
zodType = z.date();
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
zodType = z.string();
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (column.isNull && !type.startsWith('nullable(')) {
|
|
76
|
+
zodType = zodType.nullable();
|
|
77
|
+
}
|
|
78
|
+
return zodType;
|
|
79
|
+
}
|
|
80
|
+
export function generateSelectSchema(table) {
|
|
81
|
+
const shape = {};
|
|
82
|
+
for (const key in table.$columns) {
|
|
83
|
+
if (Object.prototype.hasOwnProperty.call(table.$columns, key)) {
|
|
84
|
+
const column = table.$columns[key];
|
|
85
|
+
shape[key] = mapClickHouseTypeToZod(column);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return z.object(shape);
|
|
89
|
+
}
|
|
90
|
+
export function generateInsertSchema(table) {
|
|
91
|
+
const shape = {};
|
|
92
|
+
for (const key in table.$columns) {
|
|
93
|
+
if (Object.prototype.hasOwnProperty.call(table.$columns, key)) {
|
|
94
|
+
const column = table.$columns[key];
|
|
95
|
+
let zodType = mapClickHouseTypeToZod(column);
|
|
96
|
+
const isOptionalBySchema = !column.isNull && (column.meta?.default !== undefined || column.meta?.defaultFn !== undefined || column.meta?.defaultExpr !== undefined || column.meta?.autoGenerate !== undefined);
|
|
97
|
+
const isOptionalByNullable = column.isNull;
|
|
98
|
+
if (isOptionalBySchema || isOptionalByNullable) {
|
|
99
|
+
zodType = zodType.optional();
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
}
|
|
103
|
+
shape[key] = zodType;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return z.object(shape);
|
|
107
|
+
}
|
package/dist/column.d.ts
CHANGED
|
@@ -26,8 +26,7 @@ export declare class ClickHouseColumn<TType = any, TNotNull extends boolean = tr
|
|
|
26
26
|
isNull: boolean;
|
|
27
27
|
meta?: ColumnMeta | undefined;
|
|
28
28
|
tableName?: string;
|
|
29
|
-
constructor(name: string, type: string, isNull?: boolean,
|
|
30
|
-
meta?: ColumnMeta | undefined);
|
|
29
|
+
constructor(name: string, type: string, isNull?: boolean, meta?: ColumnMeta | undefined);
|
|
31
30
|
clone<NTNotNull extends boolean = TNotNull, NAutoGenerated extends boolean = TAutoGenerated>(overrides?: {
|
|
32
31
|
name?: string;
|
|
33
32
|
type?: string;
|
|
@@ -40,37 +39,14 @@ export declare class ClickHouseColumn<TType = any, TNotNull extends boolean = tr
|
|
|
40
39
|
version?: 1 | 3 | 4 | 5 | 6 | 7;
|
|
41
40
|
}): ClickHouseColumn<TType, TNotNull, true>;
|
|
42
41
|
primaryKey(): ClickHouseColumn<TType, true, TAutoGenerated>;
|
|
43
|
-
/**
|
|
44
|
-
* Define a default value for the column.
|
|
45
|
-
* Can be a static value or a SQL expression like 'now()'.
|
|
46
|
-
*/
|
|
47
42
|
default(value: any): ClickHouseColumn<TType, TNotNull, true>;
|
|
48
43
|
references(getColumn: () => ClickHouseColumn, options?: {
|
|
49
44
|
onDelete?: 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
50
45
|
onUpdate?: 'cascade' | 'set null' | 'restrict' | 'no action';
|
|
51
46
|
}): ClickHouseColumn<TType, TNotNull, TAutoGenerated>;
|
|
52
|
-
/**
|
|
53
|
-
* Add a comment to the column in the database.
|
|
54
|
-
*/
|
|
55
47
|
comment(text: string): ClickHouseColumn<TType, TNotNull, TAutoGenerated>;
|
|
56
48
|
alias(expression: string): ClickHouseColumn<TType, TNotNull, TAutoGenerated>;
|
|
57
|
-
/**
|
|
58
|
-
* Apply a compression codec to this column.
|
|
59
|
-
* Can be chained to apply multiple codecs (e.g., Delta then ZSTD).
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* ```typescript
|
|
63
|
-
* // Optimize for time-series
|
|
64
|
-
* timestamp.codec('DoubleDelta').codec('ZSTD', 3)
|
|
65
|
-
* // Optimize for metrics
|
|
66
|
-
* value.codec('Gorilla')
|
|
67
|
-
* ```
|
|
68
|
-
*/
|
|
69
49
|
codec(type: 'ZSTD' | 'LZ4' | 'Delta' | 'DoubleDelta' | 'Gorilla' | 'None', level?: number): ClickHouseColumn<TType, TNotNull, TAutoGenerated>;
|
|
70
|
-
/**
|
|
71
|
-
* Calculates the column value on the client side before insertion.
|
|
72
|
-
* Useful for UUIDs, hashes, or computations based on other fields.
|
|
73
|
-
*/
|
|
74
50
|
$defaultFn(fn: (row: Record<string, any>) => any): ClickHouseColumn<TType, TNotNull, true>;
|
|
75
51
|
toSQL(): string;
|
|
76
52
|
}
|
package/dist/column.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export class ClickHouseColumn {
|
|
2
|
+
name;
|
|
3
|
+
type;
|
|
4
|
+
isNull;
|
|
5
|
+
meta;
|
|
6
|
+
tableName;
|
|
7
|
+
constructor(name, type, isNull = false, meta) {
|
|
8
|
+
this.name = name;
|
|
9
|
+
this.type = type;
|
|
10
|
+
this.isNull = isNull;
|
|
11
|
+
this.meta = meta;
|
|
12
|
+
}
|
|
13
|
+
clone(overrides = {}) {
|
|
14
|
+
const clonedMeta = overrides.meta ?? (this.meta ? { ...this.meta } : undefined);
|
|
15
|
+
const col = new ClickHouseColumn(overrides.name ?? this.name, overrides.type ?? this.type, overrides.isNull ?? this.isNull, clonedMeta);
|
|
16
|
+
col.tableName = this.tableName;
|
|
17
|
+
return col;
|
|
18
|
+
}
|
|
19
|
+
notNull() {
|
|
20
|
+
return this.clone({ isNull: false });
|
|
21
|
+
}
|
|
22
|
+
nullable() {
|
|
23
|
+
return this.clone({ isNull: true });
|
|
24
|
+
}
|
|
25
|
+
autoGenerate(options) {
|
|
26
|
+
if (this.type !== 'UUID') {
|
|
27
|
+
throw new Error('autoGenerate() can only be used with UUID columns');
|
|
28
|
+
}
|
|
29
|
+
const version = options?.version || 4;
|
|
30
|
+
const meta = { ...(this.meta ?? {}), autoGenerate: { type: 'uuid', version } };
|
|
31
|
+
return this.clone({ meta });
|
|
32
|
+
}
|
|
33
|
+
primaryKey() {
|
|
34
|
+
const meta = { ...(this.meta ?? {}), isPrimaryKey: true };
|
|
35
|
+
return this.clone({ isNull: false, meta });
|
|
36
|
+
}
|
|
37
|
+
default(value) {
|
|
38
|
+
const meta = { ...(this.meta ?? {}) };
|
|
39
|
+
if (typeof value === 'string') {
|
|
40
|
+
const trimmed = value.trim();
|
|
41
|
+
const isExpression = trimmed.includes('(') && trimmed.includes(')') ||
|
|
42
|
+
trimmed.match(/^[a-zA-Z_][a-zA-Z0-9_]*\s*\(/);
|
|
43
|
+
if (isExpression) {
|
|
44
|
+
meta.defaultExpr = trimmed;
|
|
45
|
+
delete meta.default;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
meta.default = value;
|
|
49
|
+
delete meta.defaultExpr;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
meta.default = value;
|
|
54
|
+
delete meta.defaultExpr;
|
|
55
|
+
}
|
|
56
|
+
return this.clone({ meta });
|
|
57
|
+
}
|
|
58
|
+
references(getColumn, options) {
|
|
59
|
+
const column = getColumn();
|
|
60
|
+
const meta = {
|
|
61
|
+
...(this.meta ?? {}),
|
|
62
|
+
references: {
|
|
63
|
+
table: '',
|
|
64
|
+
column: column.name,
|
|
65
|
+
onDelete: options?.onDelete,
|
|
66
|
+
onUpdate: options?.onUpdate,
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
return this.clone({ meta });
|
|
70
|
+
}
|
|
71
|
+
comment(text) {
|
|
72
|
+
const meta = { ...(this.meta ?? {}), comment: text };
|
|
73
|
+
return this.clone({ meta });
|
|
74
|
+
}
|
|
75
|
+
alias(expression) {
|
|
76
|
+
const meta = { ...(this.meta ?? {}), alias: expression };
|
|
77
|
+
return this.clone({ meta });
|
|
78
|
+
}
|
|
79
|
+
codec(type, level) {
|
|
80
|
+
const codecStr = level ? `${type}(${level})` : type;
|
|
81
|
+
const meta = {
|
|
82
|
+
...(this.meta ?? {}),
|
|
83
|
+
codec: this.meta?.codec ? `${this.meta.codec}, ${codecStr}` : codecStr
|
|
84
|
+
};
|
|
85
|
+
return this.clone({ meta });
|
|
86
|
+
}
|
|
87
|
+
$defaultFn(fn) {
|
|
88
|
+
const meta = { ...(this.meta ?? {}), defaultFn: fn };
|
|
89
|
+
return this.clone({ meta });
|
|
90
|
+
}
|
|
91
|
+
toSQL() {
|
|
92
|
+
const isComposite = this.type.startsWith('Array(') || this.type.startsWith('Map(') || this.type.startsWith('Tuple(');
|
|
93
|
+
const baseType = (this.isNull && !isComposite) ? `Nullable(${this.type})` : this.type;
|
|
94
|
+
const clauses = [baseType];
|
|
95
|
+
if (this.meta?.codec) {
|
|
96
|
+
clauses.push(`CODEC(${this.meta.codec})`);
|
|
97
|
+
}
|
|
98
|
+
if (this.meta?.defaultExpr) {
|
|
99
|
+
clauses.push(`DEFAULT ${this.meta.defaultExpr}`);
|
|
100
|
+
}
|
|
101
|
+
else if (this.meta?.default !== undefined) {
|
|
102
|
+
const defaultValue = this.meta.default;
|
|
103
|
+
let sqlValue;
|
|
104
|
+
if (typeof defaultValue === 'string') {
|
|
105
|
+
sqlValue = `'${defaultValue.replace(/'/g, "''")}'`;
|
|
106
|
+
}
|
|
107
|
+
else if (typeof defaultValue === 'number') {
|
|
108
|
+
sqlValue = String(defaultValue);
|
|
109
|
+
}
|
|
110
|
+
else if (typeof defaultValue === 'boolean') {
|
|
111
|
+
sqlValue = defaultValue ? '1' : '0';
|
|
112
|
+
}
|
|
113
|
+
else if (defaultValue === null) {
|
|
114
|
+
sqlValue = 'NULL';
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
sqlValue = `'${String(defaultValue).replace(/'/g, "''")}'`;
|
|
118
|
+
}
|
|
119
|
+
clauses.push(`DEFAULT ${sqlValue}`);
|
|
120
|
+
}
|
|
121
|
+
if (this.meta?.materialized) {
|
|
122
|
+
clauses.push(`MATERIALIZED ${this.meta.materialized}`);
|
|
123
|
+
}
|
|
124
|
+
if (this.meta?.alias) {
|
|
125
|
+
clauses.push(`ALIAS ${this.meta.alias}`);
|
|
126
|
+
}
|
|
127
|
+
if (this.meta?.comment) {
|
|
128
|
+
const escapedComment = this.meta.comment.replace(/'/g, "''");
|
|
129
|
+
clauses.push(`COMMENT '${escapedComment}'`);
|
|
130
|
+
}
|
|
131
|
+
return clauses.join(' ');
|
|
132
|
+
}
|
|
133
|
+
}
|
package/dist/compiler.d.ts
CHANGED
|
@@ -4,17 +4,10 @@ export declare class QueryCompiler {
|
|
|
4
4
|
private paramCounter;
|
|
5
5
|
reset(): void;
|
|
6
6
|
private getNextParamName;
|
|
7
|
-
/**
|
|
8
|
-
* Compile with caching support.
|
|
9
|
-
* Returns a PreparedQuery ready for execution and the values to bind.
|
|
10
|
-
*/
|
|
11
7
|
compileWithCache(state: QueryBuilderState, client: any): {
|
|
12
8
|
cachedQuery: PreparedQuery<any>;
|
|
13
9
|
values: any[];
|
|
14
10
|
};
|
|
15
|
-
/**
|
|
16
|
-
* Legacy/Internal method for getting SQL + Params directly (used by toSQL)
|
|
17
|
-
*/
|
|
18
11
|
compileSelect(state: QueryBuilderState): {
|
|
19
12
|
sql: string;
|
|
20
13
|
params: Record<string, unknown>;
|