@@ -4,6 +4,7 @@ import type {
44 FetchRowsOptions ,
55 RowArray ,
66 RowObject ,
7+ RowMapperOptions ,
78 StatementManifest ,
89} from './types.js'
910
@@ -30,12 +31,10 @@ const INTEGER_TYPES = new Set(['TINYINT', 'SMALLINT', 'INT'])
3031const BIGINT_TYPES = new Set ( [ 'BIGINT' , 'LONG' ] )
3132const FLOAT_TYPES = new Set ( [ 'FLOAT' , 'DOUBLE' ] )
3233const BOOLEAN_TYPES = new Set ( [ 'BOOLEAN' ] )
34+ const TIMESTAMP_TYPES = new Set ( [ 'TIMESTAMP' , 'TIMESTAMP_NTZ' , 'TIMESTAMP_LTZ' ] )
3335const STRING_TYPES = new Set ( [
3436 'STRING' ,
3537 'DATE' ,
36- 'TIMESTAMP' ,
37- 'TIMESTAMP_NTZ' ,
38- 'TIMESTAMP_LTZ' ,
3938 'TIME' ,
4039] )
4140
@@ -46,15 +45,16 @@ const STRING_TYPES = new Set([
4645 */
4746export function createRowMapper (
4847 manifest : StatementManifest ,
49- format : FetchRowsOptions [ 'format' ]
48+ format : FetchRowsOptions [ 'format' ] ,
49+ options : RowMapperOptions = { }
5050) : RowMapper {
5151 if ( format !== 'JSON_OBJECT' )
5252 return ( row ) => row
5353
5454 // Precompute per-column converters for fast row mapping.
5555 const columnConverters = manifest . schema . columns . map ( ( column : ColumnInfo ) => ( {
5656 name : column . name ,
57- convert : createColumnConverter ( column ) ,
57+ convert : createColumnConverter ( column , options ) ,
5858 } ) )
5959
6060 return ( row ) => {
@@ -72,9 +72,12 @@ export function createRowMapper(
7272 }
7373}
7474
75- function createColumnConverter ( column : ColumnInfo ) : ( value : unknown ) => unknown {
75+ function createColumnConverter (
76+ column : ColumnInfo ,
77+ options : RowMapperOptions
78+ ) : ( value : unknown ) => unknown {
7679 const descriptor = parseColumnType ( column )
77- return ( value ) => convertValue ( descriptor , value )
80+ return ( value ) => convertValue ( descriptor , value , options )
7881}
7982
8083function parseColumnType ( column : ColumnInfo ) : TypeDescriptor {
@@ -255,19 +258,23 @@ function stripNotNull(typeText: string): string {
255258 return trimmed
256259}
257260
258- function convertValue ( descriptor : TypeDescriptor , value : unknown ) : unknown {
261+ function convertValue (
262+ descriptor : TypeDescriptor ,
263+ value : unknown ,
264+ options : RowMapperOptions
265+ ) : unknown {
259266 if ( value === null || value === undefined )
260267 return value
261268
262269 if ( descriptor . typeName === 'STRUCT' && descriptor . fields )
263270 // STRUCT values are JSON strings in JSON_ARRAY format.
264- return convertStructValue ( descriptor . fields , value )
271+ return convertStructValue ( descriptor . fields , value , options )
265272
266273 if ( descriptor . typeName === 'ARRAY' && descriptor . elementType )
267- return convertArrayValue ( descriptor . elementType , value )
274+ return convertArrayValue ( descriptor . elementType , value , options )
268275
269276 if ( descriptor . typeName === 'MAP' && descriptor . keyType && descriptor . valueType )
270- return convertMapValue ( descriptor . keyType , descriptor . valueType , value )
277+ return convertMapValue ( descriptor . keyType , descriptor . valueType , value , options )
271278
272279 if ( descriptor . typeName === 'DECIMAL' )
273280 return convertNumber ( value )
@@ -276,45 +283,57 @@ function convertValue(descriptor: TypeDescriptor, value: unknown): unknown {
276283 return convertNumber ( value )
277284
278285 if ( BIGINT_TYPES . has ( descriptor . typeName ) )
279- return convertInteger ( value )
286+ return convertInteger ( value , options . encodeBigInt )
280287
281288 if ( FLOAT_TYPES . has ( descriptor . typeName ) )
282289 return convertNumber ( value )
283290
284291 if ( BOOLEAN_TYPES . has ( descriptor . typeName ) )
285292 return convertBoolean ( value )
286293
294+ if ( TIMESTAMP_TYPES . has ( descriptor . typeName ) )
295+ return convertTimestamp ( value , options . encodeTimestamp )
296+
287297 if ( STRING_TYPES . has ( descriptor . typeName ) )
288298 return value
289299
290300 return value
291301}
292302
293- function convertStructValue ( fields : StructField [ ] , value : unknown ) : unknown {
303+ function convertStructValue (
304+ fields : StructField [ ] ,
305+ value : unknown ,
306+ options : RowMapperOptions
307+ ) : unknown {
294308 const raw = parseStructValue ( value )
295309 if ( ! raw || typeof raw !== 'object' || Array . isArray ( raw ) )
296310 return value
297311
298312 // Apply nested field conversions based on the parsed STRUCT schema.
299313 const mapped : RowObject = { }
300314 for ( const field of fields )
301- mapped [ field . name ] = convertValue ( field . type , ( raw as RowObject ) [ field . name ] )
315+ mapped [ field . name ] = convertValue ( field . type , ( raw as RowObject ) [ field . name ] , options )
302316
303317 return mapped
304318}
305319
306- function convertArrayValue ( elementType : TypeDescriptor , value : unknown ) : unknown {
320+ function convertArrayValue (
321+ elementType : TypeDescriptor ,
322+ value : unknown ,
323+ options : RowMapperOptions
324+ ) : unknown {
307325 const raw = parseJsonValue ( value )
308326 if ( ! Array . isArray ( raw ) )
309327 return value
310328
311- return raw . map ( ( entry ) => convertValue ( elementType , entry ) )
329+ return raw . map ( ( entry ) => convertValue ( elementType , entry , options ) )
312330}
313331
314332function convertMapValue (
315333 keyType : TypeDescriptor ,
316334 valueType : TypeDescriptor ,
317- value : unknown
335+ value : unknown ,
336+ options : RowMapperOptions
318337) : unknown {
319338 const raw = parseJsonValue ( value )
320339 if ( ! raw || typeof raw !== 'object' )
@@ -325,16 +344,16 @@ function convertMapValue(
325344 for ( const entry of raw ) {
326345 if ( ! Array . isArray ( entry ) || entry . length < 2 )
327346 continue
328- const convertedKey = convertValue ( keyType , entry [ 0 ] )
329- mapped [ String ( convertedKey ) ] = convertValue ( valueType , entry [ 1 ] )
347+ const convertedKey = convertValue ( keyType , entry [ 0 ] , options )
348+ mapped [ String ( convertedKey ) ] = convertValue ( valueType , entry [ 1 ] , options )
330349 }
331350 return mapped
332351 }
333352
334353 const mapped : RowObject = { }
335354 for ( const [ key , entryValue ] of Object . entries ( raw ) ) {
336- const convertedKey = convertValue ( keyType , key )
337- mapped [ String ( convertedKey ) ] = convertValue ( valueType , entryValue )
355+ const convertedKey = convertValue ( keyType , key , options )
356+ mapped [ String ( convertedKey ) ] = convertValue ( valueType , entryValue , options )
338357 }
339358
340359 return mapped
@@ -372,20 +391,23 @@ function convertNumber(value: unknown): unknown {
372391 return value
373392}
374393
375- function convertInteger ( value : unknown ) : unknown {
394+ function convertInteger ( value : unknown , encodeBigInt ?: ( value : bigint ) => unknown ) : unknown {
376395 if ( typeof value === 'bigint' )
377- return value
396+ return encodeBigInt ? encodeBigInt ( value ) : value
378397
379398 if ( typeof value === 'number' ) {
380- if ( Number . isInteger ( value ) )
381- return BigInt ( value )
399+ if ( Number . isInteger ( value ) ) {
400+ const bigintValue = BigInt ( value )
401+ return encodeBigInt ? encodeBigInt ( bigintValue ) : bigintValue
402+ }
382403 return value
383404 }
384405
385406 if ( typeof value === 'string' ) {
386407 try {
387408 // Preserve integer semantics for BIGINT/DECIMAL(scale=0) by returning bigint.
388- return BigInt ( value )
409+ const bigintValue = BigInt ( value )
410+ return encodeBigInt ? encodeBigInt ( bigintValue ) : bigintValue
389411 } catch {
390412 return value
391413 }
@@ -394,6 +416,16 @@ function convertInteger(value: unknown): unknown {
394416 return value
395417}
396418
419+ function convertTimestamp (
420+ value : unknown ,
421+ encodeTimestamp ?: ( value : string ) => unknown
422+ ) : unknown {
423+ if ( typeof value !== 'string' )
424+ return value
425+
426+ return encodeTimestamp ? encodeTimestamp ( value ) : value
427+ }
428+
397429function convertBoolean ( value : unknown ) : unknown {
398430 if ( typeof value === 'boolean' )
399431 return value
0 commit comments