@@ -1386,7 +1386,6 @@ async function executeQuery(
13861386 try {
13871387 const rewrittenSql = rewriteQuery ( sql ) ;
13881388 const result = await conn . query ( rewrittenSql ) ;
1389- const data = result . toArray ( ) ;
13901389
13911390 // Get column types from the result schema
13921391 const schema = result . schema . fields ;
@@ -1395,14 +1394,85 @@ async function executeQuery(
13951394 columnTypes . set ( field . name , field . type . toString ( ) ) ;
13961395 } ) ;
13971396
1397+ // Extract interval columns before toArray() loses the data
1398+ const intervalColumns = new Map < string , any > ( ) ;
1399+ schema . forEach ( ( field , idx ) => {
1400+ if ( field . type . toString ( ) . toUpperCase ( ) . includes ( 'INTERVAL' ) ) {
1401+ intervalColumns . set ( field . name , result . getChildAt ( idx ) ) ;
1402+ }
1403+ } ) ;
1404+
1405+ const data = result . toArray ( ) ;
1406+
13981407 // Sanitize data and convert types in one pass
1399- const sanitizedData = data . map ( ( row ) => {
1408+ const sanitizedData = data . map ( ( row , rowIndex ) => {
14001409 const sanitizedRow : Record < string , unknown > = { } ;
14011410 for ( const [ key , value ] of Object . entries ( row ) ) {
14021411 const columnType = columnTypes . get ( key ) ;
14031412
1413+ // Handle INTERVAL columns - get from raw Arrow data
1414+ if ( columnType && columnType . toUpperCase ( ) . includes ( 'INTERVAL' ) ) {
1415+ const intervalColumn = intervalColumns . get ( key ) ;
1416+ if ( intervalColumn ) {
1417+ // Access raw values array directly since .get() loses data
1418+ const data = intervalColumn . data [ 0 ] ;
1419+ const values = data . values ;
1420+
1421+ // stride=3 means layout is: [months, days, nanos_low] repeated, with nanos_high separate
1422+ // Actually for MONTH_DAY_NANO it should be 4 int32s per interval
1423+ // Let me check the actual memory layout
1424+ const stride = 4 ; // Force 4 for MONTH_DAY_NANO (months, days, nanos as int64)
1425+ const offset = rowIndex * stride ;
1426+
1427+ console . log ( 'Stride info:' , 'column.stride:' , intervalColumn . stride , 'values.length:' , values . length , 'rowIndex:' , rowIndex , 'calculated offset:' , offset ) ;
1428+ console . log ( 'Values at offset:' , values . slice ( offset , offset + 4 ) ) ;
1429+
1430+ // MONTH_DAY_NANO intervals: [int32 months, int32 days, int64 nanos as two int32s]
1431+ const months = values [ offset ] || 0 ;
1432+ const days = values [ offset + 1 ] || 0 ;
1433+ // Reconstruct 64-bit nanoseconds from two 32-bit parts (signed!)
1434+ const nanosLow = values [ offset + 2 ] || 0 ;
1435+ const nanosHigh = values [ offset + 3 ] || 0 ;
1436+
1437+ console . log ( 'Parsed values:' , { months, days, nanosLow, nanosHigh} ) ;
1438+
1439+ // Convert to unsigned for low part, keep high part signed
1440+ const nanos = ( BigInt ( nanosHigh ) << 32n ) | BigInt ( nanosLow >>> 0 ) ;
1441+
1442+ // Convert to human-readable string
1443+ const components : string [ ] = [ ] ;
1444+
1445+ if ( months !== 0 ) {
1446+ const years = Math . floor ( Math . abs ( months ) / 12 ) ;
1447+ const remainingMonths = Math . abs ( months ) % 12 ;
1448+ if ( years > 0 ) components . push ( `${ months < 0 ? '-' : '' } ${ years } year${ years !== 1 ? 's' : '' } ` ) ;
1449+ if ( remainingMonths > 0 ) components . push ( `${ months < 0 && years === 0 ? '-' : '' } ${ remainingMonths } mon${ remainingMonths !== 1 ? 's' : '' } ` ) ;
1450+ }
1451+ if ( days !== 0 ) {
1452+ components . push ( `${ days < 0 ? '-' : '' } ${ Math . abs ( days ) } day${ Math . abs ( days ) !== 1 ? 's' : '' } ` ) ;
1453+ }
1454+ if ( nanos !== 0n ) {
1455+ // Convert nanoseconds to seconds
1456+ const totalNanos = nanos < 0n ? - nanos : nanos ;
1457+ const totalSeconds = Number ( totalNanos ) / 1000000000 ;
1458+ const hours = Math . floor ( totalSeconds / 3600 ) ;
1459+ const minutes = Math . floor ( ( totalSeconds % 3600 ) / 60 ) ;
1460+ const seconds = totalSeconds % 60 ;
1461+
1462+ const timeParts : string [ ] = [ ] ;
1463+ if ( hours > 0 ) timeParts . push ( `${ hours } :` ) ;
1464+ timeParts . push ( `${ String ( minutes ) . padStart ( hours > 0 ? 2 : 1 , '0' ) } :` ) ;
1465+ timeParts . push ( String ( seconds . toFixed ( 6 ) ) . padStart ( 9 , '0' ) ) ;
1466+ components . push ( `${ nanos < 0n ? '-' : '' } ${ timeParts . join ( '' ) } ` ) ;
1467+ }
1468+
1469+ sanitizedRow [ key ] = components . length > 0 ? components . join ( ' ' ) : '00:00:00' ;
1470+ } else {
1471+ sanitizedRow [ key ] = value ;
1472+ }
1473+ }
14041474 // Handle TIME columns (microseconds since midnight as bigint)
1405- if ( columnType && ( columnType . toUpperCase ( ) . includes ( 'TIME' ) || columnType === 'Time64[us]' ) ) {
1475+ else if ( columnType && ( columnType . toUpperCase ( ) . includes ( 'TIME' ) || columnType === 'Time64[us]' ) ) {
14061476 if ( value !== null && value !== undefined && ( typeof value === 'bigint' || typeof value === 'number' ) ) {
14071477 // Convert microseconds to HH:MM:SS.ffffff
14081478 const totalMicros = typeof value === 'bigint' ? Number ( value ) : value ;
0 commit comments