LCOV - code coverage report
Current view: top level - dashql-app/src/connections/trino - arrow_conversion.ts Coverage Total Hit
Test: DashQL 915828b Lines: 97.8 % 362 354
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as arrow from 'apache-arrow';
       2              : import { Logger } from '../../platform/logger/logger.js';
       3              : 
       4              : /// Create a validity bitmap from Uint8Array storing null indicators
       5              : /// Returns [bitmap, nullCount]
       6            1 : function createValidityBitmap(n: number, isNull: Uint8Array): [Uint8Array, number] {
       7            1 :     const validityBytes = Math.max(1, ((n + 63) & ~63) >> 3);
       8            1 :     const bitmap = new Uint8Array(validityBytes).fill(255); // all valid initially
       9            1 :     let nullCount = 0;
      10              : 
      11            1 :     for (let i = 0; i < n; i++) {
      12            1 :         if (isNull[i]) {
      13            1 :             const byte = i >> 3;
      14            1 :             const bit = i & 7;
      15            1 :             bitmap[byte] &= ~(1 << bit); // clear bit = null
      16            1 :             nullCount++;
      17            1 :         }
      18              :     }
      19            1 :     return [bitmap, nullCount];
      20            1 : }
      21              : 
      22              : /// Create Arrow Data for boolean column
      23            1 : function createBoolData(type: arrow.Bool, values: any[], tmpIsNull: Uint8Array): arrow.Data<arrow.Bool> {
      24            1 :     const n = values.length;
      25            1 :     tmpIsNull.fill(0);
      26            1 :     const byteLen = Math.max(1, (n + 7) >> 3);
      27            1 :     const buffer = new Uint8Array(byteLen);
      28              : 
      29            1 :     for (let i = 0; i < n; i++) {
      30            1 :         const v = values[i];
      31            1 :         if (v == null) {
      32            1 :             tmpIsNull[i] = 1;
      33            1 :         } else if (Boolean(v)) {
      34            1 :             buffer[i >> 3] |= (1 << (i & 7));
      35              :         }
      36            1 :     }
      37              : 
      38            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
      39            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
      40            1 : }
      41              : 
      42              : /// Create Arrow Data for Int types
      43            1 : function createIntData<T extends arrow.Int>(type: T, values: any[], tmpIsNull: Uint8Array): arrow.Data<T> {
      44            1 :     const n = values.length;
      45            1 :     tmpIsNull.fill(0);
      46            1 :     const buffer = new type.ArrayType(n);
      47              : 
      48            1 :     for (let i = 0; i < n; i++) {
      49            1 :         const v = values[i];
      50            1 :         if (v == null || v === '') {
      51            1 :             tmpIsNull[i] = 1;
      52            1 :         } else {
      53            1 :             const num = Number(v);
      54            1 :             if (Number.isNaN(num)) {
      55            1 :                 tmpIsNull[i] = 1;
      56              :             } else {
      57            1 :                 buffer[i] = num;
      58              :             }
      59            1 :         }
      60            1 :     }
      61              : 
      62            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
      63            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
      64            1 : }
      65              : 
      66              : /// Create Arrow Data for Float types
      67            1 : function createFloatData<T extends arrow.Float>(type: T, values: any[], tmpIsNull: Uint8Array): arrow.Data<T> {
      68            1 :     const n = values.length;
      69            1 :     tmpIsNull.fill(0);
      70            1 :     const buffer = new type.ArrayType(n);
      71              : 
      72            1 :     for (let i = 0; i < n; i++) {
      73            1 :         const v = values[i];
      74            1 :         if (v == null || v === '') {
      75            1 :             tmpIsNull[i] = 1;
      76            1 :         } else {
      77            1 :             const num = Number(v);
      78            1 :             if (Number.isNaN(num)) {
      79            1 :                 tmpIsNull[i] = 1;
      80              :             } else {
      81            1 :                 buffer[i] = num;
      82              :             }
      83            1 :         }
      84            1 :     }
      85              : 
      86            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
      87            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
      88            1 : }
      89              : 
      90              : /// Create Arrow Data for Int64/BigInt types
      91            1 : function createInt64Data(type: arrow.Int64, values: any[], tmpIsNull: Uint8Array): arrow.Data<arrow.Int64> {
      92            1 :     const n = values.length;
      93            1 :     tmpIsNull.fill(0);
      94            1 :     const buffer = new type.ArrayType(n);
      95              : 
      96            1 :     for (let i = 0; i < n; i++) {
      97            1 :         const v = values[i];
      98            1 :         if (v == null || v === '') {
      99            1 :             tmpIsNull[i] = 1;
     100              :         } else {
     101            1 :             try {
     102            1 :                 buffer[i] = BigInt(v);
     103            1 :             } catch {
     104            1 :                 tmpIsNull[i] = 1;
     105            1 :             }
     106              :         }
     107            1 :     }
     108              : 
     109            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
     110            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
     111            1 : }
     112              : 
     113              : /// Create Arrow Data for Decimal128
     114            1 : function createDecimalData(type: arrow.Decimal, values: any[], tmpIsNull: Uint8Array): arrow.Data<arrow.Decimal> {
     115            1 :     const n = values.length;
     116            1 :     const stride = type.bitWidth / 32;
     117            1 :     const buffer = new Uint32Array(n * stride);
     118            1 :     tmpIsNull.fill(0);
     119              : 
     120            1 :     for (let i = 0; i < n; i++) {
     121            1 :         const value = values[i];
     122            1 :         if (value == null || value === '') {
     123            0 :             tmpIsNull[i] = 1;
     124            0 :             continue;
     125            0 :         }
     126              : 
     127            1 :         try {
     128            1 :             const text = String(value).trim();
     129            1 :             const match = /^([+-]?)(\d*)(?:\.(\d*))?$/.exec(text);
     130            1 :             if (!match || (!match[2] && !match[3])) {
     131            1 :                 tmpIsNull[i] = 1;
     132            1 :                 continue;
     133            1 :             }
     134              : 
     135            1 :             const fraction = match[3] ?? '';
     136            1 :             if (fraction.length > type.scale) {
     137            1 :                 tmpIsNull[i] = 1;
     138            1 :                 continue;
     139            1 :             }
     140            1 :             const digits = `${match[2] || '0'}${fraction.padEnd(type.scale, '0')}`.replace(/^0+(?=\d)/, '');
     141            1 :             let unscaled = BigInt(digits || '0');
     142            1 :             if (match[1] === '-') unscaled = -unscaled;
     143              : 
     144            1 :             const maxValue = (1n << BigInt(type.bitWidth - 1)) - 1n;
     145            1 :             const minValue = -(1n << BigInt(type.bitWidth - 1));
     146            1 :             if (unscaled < minValue || unscaled > maxValue) {
     147            1 :                 tmpIsNull[i] = 1;
     148            1 :                 continue;
     149            1 :             }
     150              : 
     151            1 :             if (unscaled < 0) unscaled += 1n << BigInt(type.bitWidth);
     152            1 :             for (let word = 0; word < stride; word++) {
     153            1 :                 buffer[i * stride + word] = Number(unscaled & 0xffffffffn);
     154            1 :                 unscaled >>= 32n;
     155            1 :             }
     156            1 :         } catch {
     157            0 :             tmpIsNull[i] = 1;
     158            0 :         }
     159            1 :     }
     160              : 
     161            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
     162            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
     163            1 : }
     164              : 
     165              : /// Create Arrow Data for DateDay (days since epoch as Int32)
     166            1 : function createDateDayData(type: arrow.DateDay, values: any[], tmpIsNull: Uint8Array): arrow.Data<arrow.DateDay> {
     167            1 :     const n = values.length;
     168            1 :     tmpIsNull.fill(0);
     169            1 :     const buffer = new Int32Array(n);
     170              : 
     171            1 :     for (let i = 0; i < n; i++) {
     172            1 :         const v = values[i];
     173            1 :         if (v == null || v === '') {
     174            1 :             tmpIsNull[i] = 1;
     175            1 :         } else {
     176            1 :             const date = new Date(v);
     177            1 :             const ms = date.getTime();
     178            1 :             if (Number.isNaN(ms)) {
     179            1 :                 tmpIsNull[i] = 1;
     180              :             } else {
     181            1 :                 buffer[i] = Math.floor(ms / (24 * 60 * 60 * 1000));
     182              :             }
     183            1 :         }
     184            1 :     }
     185              : 
     186            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
     187            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
     188            1 : }
     189              : 
     190              : /// Create Arrow Data for DateMillisecond (ms since epoch as BigInt64)
     191            1 : function createDateMillisecondData(type: arrow.DateMillisecond, values: any[], tmpIsNull: Uint8Array): arrow.Data<arrow.DateMillisecond> {
     192            1 :     const n = values.length;
     193            1 :     tmpIsNull.fill(0);
     194            1 :     const buffer = new BigInt64Array(n);
     195              : 
     196            1 :     for (let i = 0; i < n; i++) {
     197            1 :         const v = values[i];
     198            1 :         if (v == null || v === '') {
     199            1 :             tmpIsNull[i] = 1;
     200            1 :         } else {
     201            1 :             const date = new Date(v);
     202            1 :             const ms = date.getTime();
     203            1 :             if (Number.isNaN(ms)) {
     204            0 :                 tmpIsNull[i] = 1;
     205              :             } else {
     206            1 :                 buffer[i] = BigInt(ms);
     207              :             }
     208            1 :         }
     209            1 :     }
     210              : 
     211            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
     212            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
     213            1 : }
     214              : 
     215              : /// Create Arrow Data for TimeMillisecond (ms since midnight as Int32)
     216            1 : function createTimeMillisecondData(type: arrow.TimeMillisecond, values: any[], tmpIsNull: Uint8Array): arrow.Data<arrow.TimeMillisecond> {
     217            1 :     const n = values.length;
     218            1 :     tmpIsNull.fill(0);
     219            1 :     const buffer = new Int32Array(n);
     220              : 
     221            1 :     for (let i = 0; i < n; i++) {
     222            1 :         const v = values[i];
     223            1 :         if (v == null) {
     224            1 :             tmpIsNull[i] = 1;
     225            1 :         } else {
     226              :             // Parse time string "HH:MM:SS.sss"
     227            1 :             const parts = String(v).split(':');
     228            1 :             const hours = parseInt(parts[0], 10) || 0;
     229            1 :             const minutes = parseInt(parts[1], 10) || 0;
     230            1 :             const secondsParts = (parts[2] || '0').split('.');
     231            1 :             const seconds = parseInt(secondsParts[0], 10) || 0;
     232            1 :             const millis = parseInt((secondsParts[1] || '0').padEnd(3, '0').slice(0, 3), 10);
     233            1 :             buffer[i] = ((hours * 60 + minutes) * 60 + seconds) * 1000 + millis;
     234            1 :         }
     235            1 :     }
     236              : 
     237            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
     238            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
     239            1 : }
     240              : 
     241              : /// A Trino timestamp: `2024-03-01 12:30:00.123`, with time zone followed by ` UTC`, ` +01:00` or ` Europe/Berlin`
     242            1 : const TRINO_TIMESTAMP_PATTERN = /^(-?\d+)-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(?: ?(\S+))?$/;
     243              : /// A zone offset like `+01:00`
     244            1 : const ZONE_OFFSET_PATTERN = /^([+-])(\d{2}):?(\d{2})$/;
     245              : /// The zones that are UTC
     246            1 : const UTC_ZONES = new Set(['UTC', 'Z', 'GMT', 'Etc/UTC', 'Etc/GMT', 'UCT', 'Zulu', 'Universal']);
     247              : 
     248              : /// The milliseconds since epoch of a wall clock time in UTC, Date.UTC would move the years 0 to 99 to the 1900s
     249            1 : function utcMs(year: number, month: number, day: number, hours: number, minutes: number, seconds: number, ms: number): number {
     250            1 :     const date = new Date(0);
     251            1 :     date.setUTCFullYear(year, month - 1, day);
     252            1 :     date.setUTCHours(hours, minutes, seconds, ms);
     253            1 :     return date.getTime();
     254            1 : }
     255              : 
     256              : /// The formatters of the wall clock in a named zone
     257            1 : const zoneFormatters = new Map<string, Intl.DateTimeFormat>();
     258              : /// The offsets of named zones by zone and minute, cleared when it grows too large
     259            1 : const zoneOffsets = new Map<string, number>();
     260            1 : const ZONE_OFFSET_CACHE_SIZE = 100_000;
     261              : 
     262              : /// The offset of a named zone at an instant: the wall clock in the zone minus the wall clock in UTC
     263            1 : function namedZoneOffsetMs(zone: string, instantMs: number): number {
     264            1 :     const minute = Math.floor(instantMs / 60_000);
     265            1 :     const key = `${zone}|${minute}`;
     266            1 :     const cached = zoneOffsets.get(key);
     267            1 :     if (cached !== undefined) return cached;
     268              : 
     269            1 :     let formatter = zoneFormatters.get(zone);
     270            1 :     if (formatter === undefined) {
     271              :         // Throws a RangeError for unknown zones
     272            1 :         formatter = new Intl.DateTimeFormat('en-US', {
     273            1 :             timeZone: zone, hourCycle: 'h23', era: 'short',
     274            1 :             year: 'numeric', month: 'numeric', day: 'numeric', hour: 'numeric', minute: 'numeric',
     275            1 :         });
     276            1 :         zoneFormatters.set(zone, formatter);
     277            1 :     }
     278            1 :     const parts: Record<string, string> = {};
     279            1 :     for (const part of formatter.formatToParts(minute * 60_000)) parts[part.type] = part.value;
     280            1 :     const year = parts.era === 'BC' ? 1 - Number(parts.year) : Number(parts.year);
     281            1 :     const wall = utcMs(year, Number(parts.month), Number(parts.day), Number(parts.hour), Number(parts.minute), 0, 0);
     282            1 :     const offset = wall - minute * 60_000;
     283              : 
     284            1 :     if (zoneOffsets.size >= ZONE_OFFSET_CACHE_SIZE) zoneOffsets.clear();
     285            1 :     zoneOffsets.set(key, offset);
     286            1 :     return offset;
     287            1 : }
     288              : 
     289              : /// Parse a Trino timestamp to milliseconds since epoch, null if it is invalid.
     290              : /// A timestamp without time zone is a wall clock time and stays as is, one with a time zone is moved to UTC.
     291            1 : export function parseTrinoTimestampMs(text: string): number | null {
     292            1 :     const match = TRINO_TIMESTAMP_PATTERN.exec(text);
     293            1 :     if (match == null) return null;
     294            1 :     const fraction = (match[7] ?? '').padEnd(3, '0').slice(0, 3);
     295            1 :     const wall = utcMs(Number(match[1]), Number(match[2]), Number(match[3]), Number(match[4]), Number(match[5]), Number(match[6]), Number(fraction));
     296            1 :     const zone = match[8];
     297            1 :     if (zone == null || UTC_ZONES.has(zone)) {
     298            1 :         return wall;
     299              :     }
     300            1 :     const offset = ZONE_OFFSET_PATTERN.exec(zone);
     301            1 :     if (offset != null) {
     302            1 :         const offsetMs = (Number(offset[2]) * 60 + Number(offset[3])) * 60_000;
     303            1 :         return offset[1] === '-' ? wall + offsetMs : wall - offsetMs;
     304            1 :     }
     305            1 :     try {
     306              :         // Guess the instant with the offset at the wall clock, then correct it with the offset at the guess
     307            1 :         const guess = wall - namedZoneOffsetMs(zone, wall);
     308            1 :         return wall - namedZoneOffsetMs(zone, guess);
     309            1 :     } catch {
     310            1 :         return null;
     311            1 :     }
     312            1 : }
     313              : 
     314              : /// Create Arrow Data for Timestamp types (ms since epoch as BigInt64)
     315            1 : function createTimestampData<T extends arrow.Timestamp>(type: T, values: any[], tmpIsNull: Uint8Array): arrow.Data<T> {
     316            1 :     const n = values.length;
     317            1 :     tmpIsNull.fill(0);
     318            1 :     const buffer = new BigInt64Array(n);
     319              : 
     320            1 :     for (let i = 0; i < n; i++) {
     321            1 :         const v = values[i];
     322            1 :         const ms = v == null || v === '' ? null : parseTrinoTimestampMs(String(v));
     323            1 :         if (ms == null || Number.isNaN(ms)) {
     324            1 :             tmpIsNull[i] = 1;
     325              :         } else {
     326            1 :             buffer[i] = BigInt(ms);
     327              :         }
     328            1 :     }
     329              : 
     330            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
     331            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, data: buffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
     332            1 : }
     333              : 
     334              : // Shared TextEncoder for string encoding
     335            1 : const textEncoder = new TextEncoder();
     336              : 
     337              : /// Create Arrow Data for Utf8 strings
     338            1 : function createUtf8Data(type: arrow.Utf8, values: any[], tmpIsNull: Uint8Array): arrow.Data<arrow.Utf8> {
     339            1 :     const n = values.length;
     340            1 :     tmpIsNull.fill(0);
     341            1 :     const encodedValues = new Array<Uint8Array | null>(n);
     342            1 :     let totalBytes = 0;
     343              : 
     344              :     // First pass: encode strings and track nulls
     345            1 :     for (let i = 0; i < n; i++) {
     346            1 :         const v = values[i];
     347            1 :         if (v == null) {
     348            1 :             tmpIsNull[i] = 1;
     349            1 :             encodedValues[i] = null;
     350            1 :         } else {
     351            1 :             const str = typeof v === 'object' ? JSON.stringify(v) : String(v);
     352            1 :             const encoded = textEncoder.encode(str);
     353            1 :             encodedValues[i] = encoded;
     354            1 :             totalBytes += encoded.length;
     355            1 :         }
     356            1 :     }
     357              : 
     358              :     // Second pass: build offsets and data buffer
     359            1 :     const offsets = new Int32Array(n + 1);
     360            1 :     const dataBuffer = new Uint8Array(totalBytes);
     361            1 :     let offset = 0;
     362              : 
     363            1 :     for (let i = 0; i < n; i++) {
     364            1 :         offsets[i] = offset;
     365            1 :         const encoded = encodedValues[i];
     366            1 :         if (encoded != null) {
     367            1 :             dataBuffer.set(encoded, offset);
     368            1 :             offset += encoded.length;
     369            1 :         }
     370            1 :     }
     371            1 :     offsets[n] = offset;
     372              : 
     373            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
     374            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, valueOffsets: offsets, data: dataBuffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
     375            1 : }
     376              : 
     377              : /// Create Arrow Data for Binary
     378            1 : function createBinaryData(type: arrow.Binary, values: any[], tmpIsNull: Uint8Array): arrow.Data<arrow.Binary> {
     379            1 :     const n = values.length;
     380            1 :     tmpIsNull.fill(0);
     381            1 :     const binaryValues = new Array<Uint8Array | null>(n);
     382            1 :     let totalBytes = 0;
     383              : 
     384              :     // First pass: decode binary values and track nulls
     385            1 :     for (let i = 0; i < n; i++) {
     386            1 :         const v = values[i];
     387            1 :         if (v == null) {
     388            0 :             tmpIsNull[i] = 1;
     389            0 :             binaryValues[i] = null;
     390            1 :         } else if (v instanceof Uint8Array) {
     391            1 :             binaryValues[i] = v;
     392            1 :             totalBytes += v.length;
     393            1 :         } else if (typeof v === 'string') {
     394              :             // Trino returns varbinary as base64
     395            1 :             const bytes = Uint8Array.from(atob(v), c => c.charCodeAt(0));
     396            1 :             binaryValues[i] = bytes;
     397            1 :             totalBytes += bytes.length;
     398            1 :         } else {
     399            1 :             tmpIsNull[i] = 1;
     400            1 :             binaryValues[i] = null;
     401            1 :         }
     402            1 :     }
     403              : 
     404              :     // Second pass: build offsets and data buffer
     405            1 :     const offsets = new Int32Array(n + 1);
     406            1 :     const dataBuffer = new Uint8Array(totalBytes);
     407            1 :     let offset = 0;
     408              : 
     409            1 :     for (let i = 0; i < n; i++) {
     410            1 :         offsets[i] = offset;
     411            1 :         const bytes = binaryValues[i];
     412            1 :         if (bytes != null) {
     413            1 :             dataBuffer.set(bytes, offset);
     414            1 :             offset += bytes.length;
     415            1 :         }
     416            1 :     }
     417            1 :     offsets[n] = offset;
     418              : 
     419            1 :     const [validityBitmap, nullCount] = createValidityBitmap(n, tmpIsNull);
     420            1 :     return arrow.makeData({ type, offset: 0, length: n, nullCount, valueOffsets: offsets, data: dataBuffer, nullBitmap: nullCount > 0 ? validityBitmap : undefined });
     421            1 : }
     422              : 
     423              : /// Create Arrow Data for a column based on its type.
     424              : /// Instances report the type family as typeId (Type.Int, Type.Date, Type.Time), never the flattened ids like
     425              : /// Type.Int32 or Type.DateDay, so we dispatch on the family and then on the bit width or the unit.
     426            1 : function createColumnData(field: arrow.Field, values: any[], tmpIsNull: Uint8Array): arrow.Data {
     427            1 :     const type = field.type;
     428            1 :     if (arrow.DataType.isBool(type)) {
     429            1 :         return createBoolData(type, values, tmpIsNull);
     430              :     }
     431            1 :     if (arrow.DataType.isInt(type)) {
     432            1 :         return type.bitWidth === 64
     433            1 :             ? createInt64Data(type as arrow.Int64, values, tmpIsNull)
     434            1 :             : createIntData(type, values, tmpIsNull);
     435              :     }
     436            1 :     if (arrow.DataType.isFloat(type)) {
     437            1 :         return createFloatData(type, values, tmpIsNull);
     438              :     }
     439            1 :     if (arrow.DataType.isDecimal(type)) {
     440            1 :         return createDecimalData(type, values, tmpIsNull);
     441              :     }
     442            1 :     if (arrow.DataType.isDate(type)) {
     443            1 :         return type.unit === arrow.DateUnit.DAY
     444            1 :             ? createDateDayData(type as arrow.DateDay, values, tmpIsNull)
     445            1 :             : createDateMillisecondData(type as arrow.DateMillisecond, values, tmpIsNull);
     446              :     }
     447            1 :     if (arrow.DataType.isTime(type) && type.unit === arrow.TimeUnit.MILLISECOND && type.bitWidth === 32) {
     448            1 :         return createTimeMillisecondData(type as arrow.TimeMillisecond, values, tmpIsNull);
     449              :     }
     450            1 :     if (arrow.DataType.isTimestamp(type)) {
     451            1 :         return createTimestampData(type, values, tmpIsNull);
     452              :     }
     453            1 :     if (arrow.DataType.isBinary(type)) {
     454            1 :         return createBinaryData(type, values, tmpIsNull);
     455              :     }
     456              :     // Utf8 and all other types (including complex types as JSON)
     457            1 :     return createUtf8Data(new arrow.Utf8(), values, tmpIsNull);
     458            1 : }
     459              : 
     460              : /// Translate any[] with a schema to an arrow batch
     461            1 : export function translateAnyRowsToArrowBatch(schema: arrow.Schema, rows: any[][], _logger: Logger): arrow.RecordBatch {
     462            1 :     const numRows = rows.length;
     463            1 :     const numCols = schema.fields.length;
     464              : 
     465              :     // Shared validity buffer reused across all columns
     466            1 :     const tmpIsNull = new Uint8Array(numRows);
     467              : 
     468              :     // Build column data directly
     469            1 :     const columnData: arrow.Data[] = [];
     470              : 
     471              : 
     472            1 :     for (let col = 0; col < numCols; col++) {
     473            1 :         const field = schema.fields[col];
     474              : 
     475              :         // Collect column values
     476            1 :         const values: any[] = [];
     477            1 :         for (let row = 0; row < numRows; row++) {
     478            1 :             values.push(rows[row][col]);
     479              :         }
     480              : 
     481              :         // Create Data for this column
     482            1 :         const data = createColumnData(field, values, tmpIsNull);
     483            1 :         columnData.push(data);
     484            1 :     }
     485              : 
     486              :     // Create struct and batch
     487            1 :     const structData = arrow.makeData({ type: new arrow.Struct(schema.fields), children: columnData, nullCount: 0 });
     488            1 :     return new arrow.RecordBatch(schema, structData);
     489            1 : }
        

Generated by: LCOV version 2.0-1