@@ -324,6 +324,224 @@ func TestParseDecimal(t *testing.T) {
324
324
assert .Error (t , err )
325
325
}
326
326
327
+ func TestColumnTyping (t * testing.T ) {
328
+ maxStringLength := 1000
329
+
330
+ type testCase struct {
331
+ name string
332
+ column Column
333
+ columnTyping ColumnTyping
334
+
335
+ expectedDecimalPrecision int
336
+ expectedDecimalScale int
337
+ expectedStringLength int
338
+ }
339
+
340
+ testCases := []testCase {
341
+ // Decimal column typing tests
342
+ {
343
+ name : "decimal_sourced_precision_scale" ,
344
+ column : Column {Name : "test" , Type : DecimalType , DbPrecision : 10 , DbScale : 2 , Sourced : true },
345
+ columnTyping : ColumnTyping {Decimal : & DecimalColumnTyping {}},
346
+ expectedDecimalPrecision : 10 ,
347
+ expectedDecimalScale : 2 ,
348
+ },
349
+ {
350
+ name : "decimal_sourced_precision_scale_2" ,
351
+ column : Column {Name : "test" , Type : DecimalType , DbPrecision : 10 , DbScale : 2 , Sourced : true },
352
+ columnTyping : ColumnTyping {Decimal : & DecimalColumnTyping {}},
353
+ expectedDecimalPrecision : 10 ,
354
+ expectedDecimalScale : 2 ,
355
+ },
356
+ {
357
+ name : "decimal_min_precision_scale" ,
358
+ column : Column {Name : "test" , Type : DecimalType , DbPrecision : 5 , DbScale : 1 , Sourced : false },
359
+ columnTyping : ColumnTyping {Decimal : & DecimalColumnTyping {MinPrecision : g .Ptr (10 ), MinScale : g .Ptr (3 )}},
360
+ expectedDecimalPrecision : 24 ,
361
+ expectedDecimalScale : 3 ,
362
+ },
363
+ {
364
+ name : "decimal_max_precision_scale" ,
365
+ column : Column {Name : "test" , Type : DecimalType , DbPrecision : 50 , DbScale : 15 , Sourced : false },
366
+ columnTyping : ColumnTyping {Decimal : & DecimalColumnTyping {MaxPrecision : 20 , MaxScale : 10 }},
367
+ expectedDecimalPrecision : 20 ,
368
+ expectedDecimalScale : 10 ,
369
+ },
370
+ {
371
+ name : "decimal_with_stats" ,
372
+ column : Column {Name : "test" , Type : DecimalType , Stats : ColumnStats {MaxLen : 8 , MaxDecLen : 3 }, Sourced : false },
373
+ columnTyping : ColumnTyping {Decimal : & DecimalColumnTyping {}},
374
+ expectedDecimalPrecision : 24 ,
375
+ expectedDecimalScale : 6 ,
376
+ },
377
+ {
378
+ name : "decimal_zero_precision_scale" ,
379
+ column : Column {Name : "test" , Type : DecimalType , DbPrecision : 0 , DbScale : 0 , Sourced : false },
380
+ columnTyping : ColumnTyping {Decimal : & DecimalColumnTyping {}},
381
+ expectedDecimalPrecision : 24 ,
382
+ expectedDecimalScale : 6 ,
383
+ },
384
+ {
385
+ name : "decimal_delta" ,
386
+ column : Column {Name : "test" , Type : DecimalType , DbPrecision : 0 , DbScale : 19 , Sourced : false },
387
+ columnTyping : ColumnTyping {Decimal : & DecimalColumnTyping {}},
388
+ expectedDecimalPrecision : 38 ,
389
+ expectedDecimalScale : 19 ,
390
+ },
391
+
392
+ // String column typing tests
393
+ {
394
+ name : "string_basic_length" ,
395
+ column : Column {Name : "test" , Type : StringType , Stats : ColumnStats {MaxLen : 50 }},
396
+ columnTyping : ColumnTyping {String : & StringColumnTyping {}},
397
+ expectedStringLength : 50 ,
398
+ },
399
+ {
400
+ name : "string_length_factor" ,
401
+ column : Column {Name : "test" , Type : StringType , Stats : ColumnStats {MaxLen : 50 }},
402
+ columnTyping : ColumnTyping {String : & StringColumnTyping {LengthFactor : 2 }},
403
+ expectedStringLength : 100 ,
404
+ },
405
+ {
406
+ name : "string_length_factor_exceeds_max" ,
407
+ column : Column {Name : "test" , Type : StringType , Stats : ColumnStats {MaxLen : 600 }},
408
+ columnTyping : ColumnTyping {String : & StringColumnTyping {LengthFactor : 2 }},
409
+ expectedStringLength : 1000 , // should cap at maxStringLength
410
+ },
411
+ {
412
+ name : "string_min_length" ,
413
+ column : Column {Name : "test" , Type : StringType , Stats : ColumnStats {MaxLen : 10 }},
414
+ columnTyping : ColumnTyping {String : & StringColumnTyping {MinLength : 50 }},
415
+ expectedStringLength : 50 ,
416
+ },
417
+ {
418
+ name : "string_max_length" ,
419
+ column : Column {Name : "test" , Type : StringType , Stats : ColumnStats {MaxLen : 200 }},
420
+ columnTyping : ColumnTyping {String : & StringColumnTyping {MaxLength : 150 }},
421
+ expectedStringLength : 200 , // original length since MaxLength doesn't override max
422
+ },
423
+ {
424
+ name : "string_use_max" ,
425
+ column : Column {Name : "test" , Type : StringType , Stats : ColumnStats {MaxLen : 50 }},
426
+ columnTyping : ColumnTyping {String : & StringColumnTyping {UseMax : true }},
427
+ expectedStringLength : 1000 , // should use maxStringLength
428
+ },
429
+ {
430
+ name : "string_use_max_with_custom_max" ,
431
+ column : Column {Name : "test" , Type : StringType , Stats : ColumnStats {MaxLen : 50 }},
432
+ columnTyping : ColumnTyping {String : & StringColumnTyping {UseMax : true , MaxLength : 2000 }},
433
+ expectedStringLength : 2000 , // should use custom MaxLength
434
+ },
435
+ {
436
+ name : "string_min_length_with_factor" ,
437
+ column : Column {Name : "test" , Type : StringType , Stats : ColumnStats {MaxLen : 10 }},
438
+ columnTyping : ColumnTyping {String : & StringColumnTyping {LengthFactor : 2 , MinLength : 50 }},
439
+ expectedStringLength : 50 , // factor gives 20, but min is 50
440
+ },
441
+
442
+ // Sourced column precision tests
443
+ {
444
+ name : "string_sourced_precision" ,
445
+ column : Column {Name : "test" , Type : StringType , DbPrecision : 100 , Sourced : true },
446
+ columnTyping : ColumnTyping {String : & StringColumnTyping {}},
447
+ expectedStringLength : 100 ,
448
+ },
449
+ {
450
+ name : "string_sourced_precision_with_factor" ,
451
+ column : Column {Name : "test" , Type : StringType , DbPrecision : 50 , Sourced : true },
452
+ columnTyping : ColumnTyping {String : & StringColumnTyping {LengthFactor : 2 }},
453
+ expectedStringLength : 100 ,
454
+ },
455
+ }
456
+
457
+ for _ , testCase := range testCases {
458
+ t .Run (testCase .name , func (t * testing.T ) {
459
+ if sct := testCase .columnTyping .String ; sct != nil {
460
+ var length int
461
+ if testCase .column .Sourced && testCase .column .DbPrecision > 0 {
462
+ length = sct .Apply (testCase .column .DbPrecision , maxStringLength )
463
+ } else {
464
+ length = sct .Apply (testCase .column .Stats .MaxLen , maxStringLength )
465
+ }
466
+ assert .Equal (t , testCase .expectedStringLength , length )
467
+
468
+ } else if dct := testCase .columnTyping .Decimal ; dct != nil {
469
+ precision , scale := dct .Apply (& testCase .column )
470
+ assert .Equal (t , testCase .expectedDecimalPrecision , precision )
471
+ assert .Equal (t , testCase .expectedDecimalScale , scale )
472
+ }
473
+ })
474
+ }
475
+
476
+ // Keep the original hardcoded test for backward compatibility
477
+ col := Column {Name : "test" , Type : DecimalType , DbPrecision : 10 , DbScale : 0 , Sourced : true }
478
+ ct := ColumnTyping {Decimal : & DecimalColumnTyping {}}
479
+ precision , scale := ct .Decimal .Apply (& col )
480
+ assert .Equal (t , 10 , precision )
481
+ assert .Equal (t , 0 , scale )
482
+ }
483
+
484
+ // Additional test for JSON column typing
485
+ func TestColumnTypingJSON (t * testing.T ) {
486
+ t .Run ("json_as_text_false" , func (t * testing.T ) {
487
+ col := Column {Name : "test" , Type : JsonType }
488
+ jct := JsonColumnTyping {AsText : false }
489
+ jct .Apply (& col )
490
+ assert .Equal (t , JsonType , col .Type )
491
+ })
492
+
493
+ t .Run ("json_as_text_true" , func (t * testing.T ) {
494
+ col := Column {Name : "test" , Type : JsonType }
495
+ jct := JsonColumnTyping {AsText : true }
496
+ jct .Apply (& col )
497
+ assert .Equal (t , TextType , col .Type )
498
+ })
499
+ }
500
+
501
+ // Test for MaxDecimals method
502
+ func TestColumnTypingMaxDecimals (t * testing.T ) {
503
+ t .Run ("nil_column_typing" , func (t * testing.T ) {
504
+ var ct * ColumnTyping
505
+ assert .Equal (t , - 1 , ct .MaxDecimals ())
506
+ })
507
+
508
+ t .Run ("nil_decimal_typing" , func (t * testing.T ) {
509
+ ct := & ColumnTyping {}
510
+ assert .Equal (t , - 1 , ct .MaxDecimals ())
511
+ })
512
+
513
+ t .Run ("max_scale_set" , func (t * testing.T ) {
514
+ ct := & ColumnTyping {
515
+ Decimal : & DecimalColumnTyping {MaxScale : 5 },
516
+ }
517
+ assert .Equal (t , 5 , ct .MaxDecimals ())
518
+ })
519
+
520
+ t .Run ("min_scale_set_no_max" , func (t * testing.T ) {
521
+ ct := & ColumnTyping {
522
+ Decimal : & DecimalColumnTyping {MinScale : g .Ptr (3 )},
523
+ }
524
+ assert .Equal (t , 3 , ct .MaxDecimals ())
525
+ })
526
+
527
+ t .Run ("both_scales_set" , func (t * testing.T ) {
528
+ ct := & ColumnTyping {
529
+ Decimal : & DecimalColumnTyping {
530
+ MaxScale : 5 ,
531
+ MinScale : g .Ptr (3 ),
532
+ },
533
+ }
534
+ assert .Equal (t , 5 , ct .MaxDecimals ()) // MaxScale takes precedence
535
+ })
536
+
537
+ t .Run ("no_scales_set" , func (t * testing.T ) {
538
+ ct := & ColumnTyping {
539
+ Decimal : & DecimalColumnTyping {},
540
+ }
541
+ assert .Equal (t , - 1 , ct .MaxDecimals ())
542
+ })
543
+ }
544
+
327
545
func TestDatasetSort (t * testing.T ) {
328
546
columns := NewColumnsFromFields ("col1" , "col2" )
329
547
data := NewDataset (columns )
0 commit comments