@housekit/orm 0.1.43 → 0.1.45

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.
@@ -254,25 +254,20 @@ export declare class ClickHouseQueryBuilder<TTable extends TableDefinition<any>
254
254
  stream(): AsyncIterableIterator<TResult>;
255
255
  /**
256
256
  * Native binary mode for high-performance data retrieval.
257
- * Reads data using ClickHouse 'RowBinary' format and parses it directly in Node.js.
258
- * Up to 10x faster than standard JSON select for large datasets.
259
257
  *
260
- * Requirements: All selected columns must be strictly typed (ClickHouseColumn instances).
258
+ * @deprecated RowBinary streaming is not supported by @clickhouse/client 1.15+.
259
+ * Use standard select() which uses optimized JSONEachRow format.
261
260
  */
262
261
  native(): Promise<TResult[]>;
263
262
  /**
264
263
  * Vector mode for columnar data retrieval.
265
- * Returns data in Columnar format (TypedArrays) instead of Row-based objects.
266
- * Ideal for analytical processing, charting, or passing to libraries like Apache Arrow.
267
264
  *
268
- * @example
269
- * const { prices } = await db.select({ prices: trades.price }).vector();
270
- * // prices is a Float64Array
265
+ * @deprecated RowBinary streaming is not supported by @clickhouse/client 1.15+.
266
+ * Use standard select() which uses optimized JSONEachRow format.
271
267
  */
272
268
  vector(): Promise<{
273
269
  [K in keyof TResult]: TResult[K] extends number ? (TResult[K] extends number ? Float64Array | Int32Array : Array<TResult[K]>) : Array<TResult[K]>;
274
270
  }>;
275
- private executeBinary;
276
271
  explain(): Promise<any>;
277
272
  explainPipeline(): Promise<any>;
278
273
  /**
package/dist/index.js CHANGED
@@ -15,379 +15,8 @@ var __toESM = (mod, isNodeMode, target) => {
15
15
  });
16
16
  return to;
17
17
  };
18
- var __export = (target, all) => {
19
- for (var name in all)
20
- __defProp(target, name, {
21
- get: all[name],
22
- enumerable: true,
23
- configurable: true,
24
- set: (newValue) => all[name] = () => newValue
25
- });
26
- };
27
18
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
28
19
 
29
- // src/utils/binary-reader.ts
30
- var exports_binary_reader = {};
31
- __export(exports_binary_reader, {
32
- createBinaryDecoder: () => createBinaryDecoder,
33
- BinaryReader: () => BinaryReader
34
- });
35
-
36
- class BinaryReader {
37
- buffer;
38
- offset = 0;
39
- view;
40
- constructor(buffer) {
41
- this.buffer = buffer;
42
- this.view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
43
- }
44
- reset(buffer) {
45
- this.buffer = buffer;
46
- this.offset = 0;
47
- this.view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
48
- }
49
- getOffset() {
50
- return this.offset;
51
- }
52
- isEOF() {
53
- return this.offset >= this.buffer.length;
54
- }
55
- readInt8() {
56
- const val = this.view.getInt8(this.offset);
57
- this.offset += 1;
58
- return val;
59
- }
60
- readUInt8() {
61
- const val = this.view.getUint8(this.offset);
62
- this.offset += 1;
63
- return val;
64
- }
65
- readInt16() {
66
- const val = this.view.getInt16(this.offset, true);
67
- this.offset += 2;
68
- return val;
69
- }
70
- readUInt16() {
71
- const val = this.view.getUint16(this.offset, true);
72
- this.offset += 2;
73
- return val;
74
- }
75
- readInt32() {
76
- const val = this.view.getInt32(this.offset, true);
77
- this.offset += 4;
78
- return val;
79
- }
80
- readUInt32() {
81
- const val = this.view.getUint32(this.offset, true);
82
- this.offset += 4;
83
- return val;
84
- }
85
- readInt64() {
86
- const val = this.view.getBigInt64(this.offset, true);
87
- this.offset += 8;
88
- return val;
89
- }
90
- readUInt64() {
91
- const val = this.view.getBigUint64(this.offset, true);
92
- this.offset += 8;
93
- return val;
94
- }
95
- readInt128() {
96
- const low = this.view.getBigUint64(this.offset, true);
97
- const high = this.view.getBigInt64(this.offset + 8, true);
98
- this.offset += 16;
99
- return high << 64n | low;
100
- }
101
- readUInt128() {
102
- const low = this.view.getBigUint64(this.offset, true);
103
- const high = this.view.getBigUint64(this.offset + 8, true);
104
- this.offset += 16;
105
- return high << 64n | low;
106
- }
107
- readInt256() {
108
- let val = 0n;
109
- for (let i = 0;i < 4; i++) {
110
- const word = this.view.getBigUint64(this.offset + i * 8, true);
111
- val |= word << BigInt(i) * 64n;
112
- }
113
- this.offset += 32;
114
- return val;
115
- }
116
- readUInt256() {
117
- return this.readInt256();
118
- }
119
- readFloat32() {
120
- const val = this.view.getFloat32(this.offset, true);
121
- this.offset += 4;
122
- return val;
123
- }
124
- readFloat64() {
125
- const val = this.view.getFloat64(this.offset, true);
126
- this.offset += 8;
127
- return val;
128
- }
129
- readVarInt() {
130
- let result = 0;
131
- let shift = 0;
132
- while (true) {
133
- const byte = this.buffer[this.offset++];
134
- result |= (byte & 127) << shift;
135
- if ((byte & 128) === 0)
136
- break;
137
- shift += 7;
138
- }
139
- return result;
140
- }
141
- readString() {
142
- const len = this.readVarInt();
143
- if (len === 0)
144
- return "";
145
- const str = this.buffer.toString("utf-8", this.offset, this.offset + len);
146
- this.offset += len;
147
- return str;
148
- }
149
- readFixedString(length) {
150
- const str = this.buffer.toString("utf-8", this.offset, this.offset + length);
151
- this.offset += length;
152
- return str.replace(/\u0000+$/, "");
153
- }
154
- readUUID() {
155
- const bytes = this.buffer.subarray(this.offset, this.offset + 16);
156
- this.offset += 16;
157
- const hex = Buffer.allocUnsafe(16);
158
- for (let i = 0;i < 8; i++) {
159
- hex[i] = bytes[7 - i];
160
- }
161
- for (let i = 0;i < 8; i++) {
162
- hex[8 + i] = bytes[15 - i];
163
- }
164
- const s = hex.toString("hex");
165
- return `${s.slice(0, 8)}-${s.slice(8, 12)}-${s.slice(12, 16)}-${s.slice(16, 20)}-${s.slice(20)}`;
166
- }
167
- readDate() {
168
- const days = this.readUInt16();
169
- const date = new Date(days * 24 * 60 * 60 * 1000);
170
- return date.toISOString().split("T")[0];
171
- }
172
- readDate32() {
173
- const days = this.readInt32();
174
- const date = new Date(days * 24 * 60 * 60 * 1000);
175
- return date.toISOString().split("T")[0];
176
- }
177
- readDateTime() {
178
- const seconds = this.readUInt32();
179
- return new Date(seconds * 1000);
180
- }
181
- readDateTime64(precision = 3) {
182
- const ticks = this.readInt64();
183
- const divisor = BigInt(Math.pow(10, precision));
184
- let ms;
185
- if (precision === 3) {
186
- ms = ticks;
187
- } else if (precision > 3) {
188
- ms = ticks / BigInt(Math.pow(10, precision - 3));
189
- } else {
190
- ms = ticks * BigInt(Math.pow(10, 3 - precision));
191
- }
192
- return new Date(Number(ms));
193
- }
194
- readNullable(reader) {
195
- const isNull2 = this.readUInt8();
196
- if (isNull2 === 1)
197
- return null;
198
- return reader();
199
- }
200
- readArray(itemReader) {
201
- const lenBig = this.readUInt64();
202
- const length = Number(lenBig);
203
- const res = new Array(length);
204
- for (let i = 0;i < length; i++) {
205
- res[i] = itemReader();
206
- }
207
- return res;
208
- }
209
- readMap(keyReader, valueReader) {
210
- const lenBig = this.readUInt64();
211
- const length = Number(lenBig);
212
- const res = {};
213
- for (let i = 0;i < length; i++) {
214
- const key = keyReader();
215
- const value = valueReader();
216
- res[key] = value;
217
- }
218
- return res;
219
- }
220
- readDecimal32(scale) {
221
- const val = this.readInt32();
222
- return val / Math.pow(10, scale);
223
- }
224
- readDecimal64(scale) {
225
- const val = this.readInt64();
226
- return Number(val) / Math.pow(10, scale);
227
- }
228
- readDecimal128(scale) {
229
- const val = this.readInt128();
230
- return Number(val) / Math.pow(10, scale);
231
- }
232
- readBool() {
233
- return this.readUInt8() === 1;
234
- }
235
- readIPv4() {
236
- const val = this.readUInt32();
237
- return [
238
- val & 255,
239
- val >> 8 & 255,
240
- val >> 16 & 255,
241
- val >> 24 & 255
242
- ].join(".");
243
- }
244
- readIPv6() {
245
- const buffer = this.buffer.subarray(this.offset, this.offset + 16);
246
- this.offset += 16;
247
- const parts = [];
248
- for (let i = 0;i < 16; i += 2) {
249
- const group = buffer.readUInt16BE(i).toString(16);
250
- parts.push(group);
251
- }
252
- return parts.join(":").replace(/(^|:)0(:0)*:0(:|$)/, "::");
253
- }
254
- }
255
- function createBinaryDecoder(type) {
256
- const t = type.toLowerCase().trim();
257
- if (t.startsWith("nullable(")) {
258
- const inner = t.match(/^nullable\((.+)\)$/)[1];
259
- const innerDecoder = createBinaryDecoder(inner);
260
- return (r) => r.readNullable(() => innerDecoder(r));
261
- }
262
- if (t.startsWith("array(")) {
263
- const inner = t.match(/^array\((.+)\)$/)[1];
264
- const innerDecoder = createBinaryDecoder(inner);
265
- return (r) => r.readArray(() => innerDecoder(r));
266
- }
267
- if (t.startsWith("map(")) {
268
- const inner = t.match(/^map\((.+)\)$/)[1];
269
- const [keyType, valueType] = parseGenericTypes(inner);
270
- const keyDecoder = createBinaryDecoder(keyType);
271
- const valueDecoder = createBinaryDecoder(valueType);
272
- return (r) => r.readMap(() => keyDecoder(r), () => valueDecoder(r));
273
- }
274
- if (t.startsWith("tuple(")) {
275
- const inner = t.match(/^tuple\((.+)\)$/)[1];
276
- const types = parseGenericTypes(inner);
277
- const decoders = types.map((type2) => createBinaryDecoder(type2));
278
- return (r) => decoders.map((d) => d(r));
279
- }
280
- if (t.startsWith("nested(")) {
281
- const inner = t.match(/^nested\((.+)\)$/)[1];
282
- const fields = parseGenericTypes(inner);
283
- const types = fields.map((f) => {
284
- const parts = f.trim().split(/\s+/);
285
- return parts.length < 2 ? "String" : parts.slice(1).join(" ");
286
- });
287
- const tupleDecoders = types.map((type2) => createBinaryDecoder(type2));
288
- return (r) => r.readArray(() => tupleDecoders.map((d) => d(r)));
289
- }
290
- if (t.startsWith("lowcardinality(")) {
291
- const inner = t.match(/^lowcardinality\((.+)\)$/)[1];
292
- return createBinaryDecoder(inner);
293
- }
294
- if (t.startsWith("fixedstring(")) {
295
- const len = parseInt(t.match(/\d+/)[0], 10);
296
- return (r) => r.readFixedString(len);
297
- }
298
- if (t.startsWith("decimal")) {
299
- const match = t.match(/^decimal(?:32|64|128)?\((\d+),\s*(\d+)\)$/);
300
- if (match) {
301
- const scale = parseInt(match[2], 10);
302
- if (t.includes("decimal128"))
303
- return (r) => r.readDecimal128(scale);
304
- if (t.includes("decimal64"))
305
- return (r) => r.readDecimal64(scale);
306
- return (r) => r.readDecimal32(scale);
307
- }
308
- }
309
- if (t.startsWith("datetime64")) {
310
- const match = t.match(/^datetime64\((\d+)/);
311
- const precision = match ? parseInt(match[1], 10) : 3;
312
- return (r) => r.readDateTime64(precision);
313
- }
314
- if (t.startsWith("enum")) {
315
- if (t.startsWith("enum8"))
316
- return (r) => r.readInt8();
317
- return (r) => r.readInt16();
318
- }
319
- switch (t) {
320
- case "uint8":
321
- return (r) => r.readUInt8();
322
- case "int8":
323
- return (r) => r.readInt8();
324
- case "uint16":
325
- return (r) => r.readUInt16();
326
- case "int16":
327
- return (r) => r.readInt16();
328
- case "uint32":
329
- return (r) => r.readUInt32();
330
- case "int32":
331
- return (r) => r.readInt32();
332
- case "uint64":
333
- return (r) => r.readUInt64();
334
- case "int64":
335
- return (r) => r.readInt64();
336
- case "uint128":
337
- return (r) => r.readUInt128();
338
- case "int128":
339
- return (r) => r.readInt128();
340
- case "uint256":
341
- return (r) => r.readUInt256();
342
- case "int256":
343
- return (r) => r.readInt256();
344
- case "float32":
345
- return (r) => r.readFloat32();
346
- case "float64":
347
- return (r) => r.readFloat64();
348
- case "string":
349
- return (r) => r.readString();
350
- case "uuid":
351
- return (r) => r.readUUID();
352
- case "bool":
353
- return (r) => r.readBool();
354
- case "date":
355
- return (r) => r.readDate();
356
- case "date32":
357
- return (r) => r.readDate32();
358
- case "datetime":
359
- return (r) => r.readDateTime();
360
- case "ipv4":
361
- return (r) => r.readIPv4();
362
- case "ipv6":
363
- return (r) => r.readIPv6();
364
- default:
365
- return (r) => r.readString();
366
- }
367
- }
368
- function parseGenericTypes(typeString) {
369
- const args = [];
370
- let current = "";
371
- let parenDepth = 0;
372
- for (let i = 0;i < typeString.length; i++) {
373
- const char = typeString[i];
374
- if (char === "," && parenDepth === 0) {
375
- args.push(current.trim());
376
- current = "";
377
- } else {
378
- if (char === "(")
379
- parenDepth++;
380
- if (char === ")")
381
- parenDepth--;
382
- current += char;
383
- }
384
- }
385
- if (current.trim()) {
386
- args.push(current.trim());
387
- }
388
- return args;
389
- }
390
-
391
20
  // src/index.ts
392
21
  import { resolve, join } from "path";
393
22
  import { existsSync, readdirSync } from "fs";
@@ -2361,34 +1990,6 @@ class PreparedQuery {
2361
1990
  query_params[this.paramKeys[i]] = values2[i];
2362
1991
  }
2363
1992
  }
2364
- const limitMatch = this.sql.match(/LIMIT\s+(\d+)/i);
2365
- const limit = limitMatch ? parseInt(limitMatch[1], 10) : Infinity;
2366
- const useBinary = limit >= 1e5 && this.columnTypes.length > 0;
2367
- if (useBinary) {
2368
- const resultSet2 = await this.client.query({
2369
- query: this.sql,
2370
- query_params,
2371
- format: "RowBinary"
2372
- });
2373
- const { BinaryReader: BinaryReader2, createBinaryDecoder: createBinaryDecoder2 } = await Promise.resolve().then(() => exports_binary_reader);
2374
- const response = await resultSet2.stream();
2375
- const decoder = this.columnTypes.map((type) => createBinaryDecoder2(type));
2376
- const results = [];
2377
- const chunks = [];
2378
- for await (const chunk of response) {
2379
- chunks.push(chunk);
2380
- }
2381
- const fullBuffer = Buffer.concat(chunks);
2382
- const reader = new BinaryReader2(fullBuffer);
2383
- while (!reader.isEOF()) {
2384
- const row = {};
2385
- for (let i = 0;i < this.columnNames.length; i++) {
2386
- row[this.columnNames[i]] = decoder[i](reader);
2387
- }
2388
- results.push(row);
2389
- }
2390
- return results;
2391
- }
2392
1993
  const resultSet = await this.client.query({
2393
1994
  query: this.sql,
2394
1995
  query_params,
@@ -2753,6 +2354,11 @@ class QueryCompiler {
2753
2354
  Object.assign(params, res.params);
2754
2355
  colSql = res.sql;
2755
2356
  }
2357
+ const sqlUpper = colSql.trim().toUpperCase();
2358
+ const alreadyHasDirection = sqlUpper.endsWith(" ASC") || sqlUpper.endsWith(" DESC");
2359
+ if (alreadyHasDirection) {
2360
+ return colSql;
2361
+ }
2756
2362
  return `${colSql} ${o.dir}`;
2757
2363
  });
2758
2364
  orderSql = `ORDER BY ${parts.join(", ")}`;
@@ -3285,104 +2891,20 @@ class ClickHouseQueryBuilder {
3285
2891
  return this[Symbol.asyncIterator]();
3286
2892
  }
3287
2893
  async native() {
3288
- return this.executeBinary("object");
2894
+ console.warn("[HouseKit] native() is deprecated. RowBinary streaming is not supported by @clickhouse/client 1.15+. Using JSONEachRow instead.");
2895
+ return this.then((data) => data);
3289
2896
  }
3290
2897
  async vector() {
3291
- return this.executeBinary("vector");
3292
- }
3293
- async executeBinary(mode) {
3294
- const compiler = new QueryCompiler;
3295
- const state = this.getState();
3296
- const columns = [];
3297
- if (state.select) {
3298
- for (const [key, val] of Object.entries(state.select)) {
3299
- if (val instanceof ClickHouseColumn) {
3300
- columns.push({ name: key, type: val.type, decoder: createBinaryDecoder(val.type) });
3301
- } else if (val && typeof val === "object" && "_type" in val && val.type) {
3302
- throw new Error(`[HouseKit] Binary mode requires strictly typed columns. Field '${key}' is an expression without type info.`);
3303
- } else {
3304
- throw new Error(`[HouseKit] Binary mode requires strictly typed columns. Field '${key}' is not a ClickHouseColumn.`);
3305
- }
3306
- }
3307
- } else if (state.table) {
3308
- const tableCols = state.table.$columns;
3309
- for (const [key, col] of Object.entries(tableCols)) {
3310
- columns.push({ name: key, type: col.type, decoder: createBinaryDecoder(col.type) });
3311
- }
3312
- } else {
3313
- throw new Error("[HouseKit] Binary mode requires a table or explicit selection.");
3314
- }
3315
- const { cachedQuery, values: values2 } = compiler.compileWithCache(state, this.client);
3316
- const query_params = {};
3317
- for (let i = 0;i < values2.length; i++) {
3318
- query_params[`p_${i + 1}`] = values2[i];
3319
- }
3320
- const resultSet = await this.client.query({
3321
- query: cachedQuery.sql,
3322
- query_params,
3323
- format: "RowBinary"
3324
- });
3325
- const chunks = [];
3326
- const stream = resultSet.stream();
3327
- for await (const chunk of stream) {
3328
- chunks.push(chunk);
3329
- }
3330
- const fullBuffer = Buffer.concat(chunks);
3331
- const reader = new BinaryReader(fullBuffer);
3332
- const count = fullBuffer.length;
3333
- if (mode === "object") {
3334
- const rows = [];
3335
- while (!reader.isEOF()) {
3336
- const row = {};
3337
- for (const col of columns) {
3338
- row[col.name] = col.decoder(reader);
3339
- }
3340
- rows.push(row);
3341
- }
3342
- return rows;
3343
- } else {
3344
- const data = {};
3345
- for (const col of columns) {
3346
- data[col.name] = [];
3347
- }
3348
- while (!reader.isEOF()) {
3349
- for (const col of columns) {
3350
- data[col.name].push(col.decoder(reader));
3351
- }
3352
- }
3353
- const finalVectors = {};
3354
- for (const col of columns) {
3355
- const arr = data[col.name];
3356
- const type = col.type.toLowerCase();
3357
- if (type.includes("int") || type.includes("float")) {
3358
- if (type === "int8")
3359
- finalVectors[col.name] = new Int8Array(arr);
3360
- else if (type === "uint8")
3361
- finalVectors[col.name] = new Uint8Array(arr);
3362
- else if (type === "int16")
3363
- finalVectors[col.name] = new Int16Array(arr);
3364
- else if (type === "uint16")
3365
- finalVectors[col.name] = new Uint16Array(arr);
3366
- else if (type === "int32")
3367
- finalVectors[col.name] = new Int32Array(arr);
3368
- else if (type === "uint32")
3369
- finalVectors[col.name] = new Uint32Array(arr);
3370
- else if (type === "int64")
3371
- finalVectors[col.name] = new BigInt64Array(arr);
3372
- else if (type === "uint64")
3373
- finalVectors[col.name] = new BigUint64Array(arr);
3374
- else if (type === "float32")
3375
- finalVectors[col.name] = new Float32Array(arr);
3376
- else if (type === "float64")
3377
- finalVectors[col.name] = new Float64Array(arr);
3378
- else
3379
- finalVectors[col.name] = arr;
3380
- } else {
3381
- finalVectors[col.name] = arr;
3382
- }
3383
- }
3384
- return finalVectors;
2898
+ console.warn("[HouseKit] vector() is deprecated. RowBinary streaming is not supported by @clickhouse/client 1.15+. Using JSONEachRow instead.");
2899
+ const rows = await this.then((data) => data);
2900
+ if (rows.length === 0)
2901
+ return {};
2902
+ const result = {};
2903
+ const keys = Object.keys(rows[0]);
2904
+ for (const key of keys) {
2905
+ result[key] = rows.map((row) => row[key]);
3385
2906
  }
2907
+ return result;
3386
2908
  }
3387
2909
  async explain() {
3388
2910
  const { query, params } = this.toSQL();
@@ -4022,7 +3544,7 @@ class BinaryWriter {
4022
3544
  this.writeInt16(value);
4023
3545
  }
4024
3546
  }
4025
- function parseGenericTypes2(typeString) {
3547
+ function parseGenericTypes(typeString) {
4026
3548
  const args = [];
4027
3549
  let current = "";
4028
3550
  let parenDepth = 0;
@@ -4071,7 +3593,7 @@ function createBinaryEncoder(clickhouseType, isNullable = false) {
4071
3593
  }
4072
3594
  const mapMatch = type.match(/^map\((.+)\)$/);
4073
3595
  if (mapMatch) {
4074
- const [keyType, valueType] = parseGenericTypes2(mapMatch[1]);
3596
+ const [keyType, valueType] = parseGenericTypes(mapMatch[1]);
4075
3597
  if (!keyType || !valueType)
4076
3598
  throw new Error(`Invalid Map type: ${type}`);
4077
3599
  const keyEncoder = createBinaryEncoder(keyType);
@@ -4094,7 +3616,7 @@ function createBinaryEncoder(clickhouseType, isNullable = false) {
4094
3616
  }
4095
3617
  const tupleMatch = type.match(/^tuple\((.+)\)$/);
4096
3618
  if (tupleMatch) {
4097
- const types = parseGenericTypes2(tupleMatch[1]);
3619
+ const types = parseGenericTypes(tupleMatch[1]);
4098
3620
  const encoders = types.map((t) => createBinaryEncoder(t));
4099
3621
  return (writer, value) => {
4100
3622
  const arr = Array.isArray(value) ? value : [];
@@ -4105,7 +3627,7 @@ function createBinaryEncoder(clickhouseType, isNullable = false) {
4105
3627
  }
4106
3628
  const nestedMatch = type.match(/^nested\((.+)\)$/);
4107
3629
  if (nestedMatch) {
4108
- const fields = parseGenericTypes2(nestedMatch[1]);
3630
+ const fields = parseGenericTypes(nestedMatch[1]);
4109
3631
  const types = fields.map((f) => {
4110
3632
  const parts = f.trim().split(/\s+/);
4111
3633
  if (parts.length < 2)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@housekit/orm",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "description": "Type-safe ClickHouse ORM with modern DX and ClickHouse-specific optimizations. Features optimized JSONCompact streaming, full engine support, and advanced query capabilities.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",