github.com/snowflakedb/gosnowflake@v1.9.0/bindings_test.go (about) 1 // Copyright (c) 2021-2022 Snowflake Computing Inc. All rights reserved. 2 3 package gosnowflake 4 5 import ( 6 "bytes" 7 "context" 8 "database/sql" 9 "fmt" 10 "log" 11 "math/big" 12 "math/rand" 13 "reflect" 14 "strconv" 15 "strings" 16 "testing" 17 "time" 18 ) 19 20 const ( 21 createTableSQL = `create or replace table test_prep_statement(c1 INTEGER, 22 c2 FLOAT, c3 BOOLEAN, c4 STRING, C5 BINARY, C6 TIMESTAMP_NTZ, 23 C7 TIMESTAMP_LTZ, C8 TIMESTAMP_TZ, C9 DATE, C10 TIME)` 24 deleteTableSQL = "drop table if exists TEST_PREP_STATEMENT" 25 insertSQL = "insert into TEST_PREP_STATEMENT values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" 26 selectAllSQL = "select * from TEST_PREP_STATEMENT ORDER BY 1" 27 28 createTableSQLBulkArray = `create or replace table test_bulk_array(c1 INTEGER, 29 c2 FLOAT, c3 BOOLEAN, c4 STRING, C5 BINARY, C6 INTEGER)` 30 deleteTableSQLBulkArray = "drop table if exists test_bulk_array" 31 insertSQLBulkArray = "insert into test_bulk_array values(?, ?, ?, ?, ?, ?)" 32 selectAllSQLBulkArray = "select * from test_bulk_array ORDER BY 1" 33 34 createTableSQLBulkArrayDateTimeTimestamp = `create or replace table test_bulk_array_DateTimeTimestamp( 35 C1 TIMESTAMP_NTZ, C2 TIMESTAMP_LTZ, C3 TIMESTAMP_TZ, C4 DATE, C5 TIME)` 36 deleteTableSQLBulkArrayDateTimeTimestamp = "drop table if exists test_bulk_array_DateTimeTimestamp" 37 insertSQLBulkArrayDateTimeTimestamp = "insert into test_bulk_array_DateTimeTimestamp values(?, ?, ?, ?, ?)" 38 selectAllSQLBulkArrayDateTimeTimestamp = "select * from test_bulk_array_DateTimeTimestamp ORDER BY 1" 39 40 enableFeatureMaxLOBSize = "ALTER SESSION SET FEATURE_INCREASED_MAX_LOB_SIZE_IN_MEMORY='ENABLED'" 41 unsetFeatureMaxLOBSize = "ALTER SESSION UNSET FEATURE_INCREASED_MAX_LOB_SIZE_IN_MEMORY" 42 43 // For max LOB size tests 44 // maxLOBSize = 128 * 1024 * 1024 // new max LOB size 45 maxLOBSize = 16 * 1024 * 1024 // current max LOB size 46 largeSize = maxLOBSize / 2 47 mediumSize = largeSize / 2 48 originSize = 16 * 1024 * 1024 49 smallSize = 16 50 // range to use for generating random numbers 51 lobRandomRange = 100000 52 ) 53 54 func TestBindingFloat64(t *testing.T) { 55 runDBTest(t, func(dbt *DBTest) { 56 types := [2]string{"FLOAT", "DOUBLE"} 57 expected := 42.23 58 var out float64 59 var rows *RowsExtended 60 for _, v := range types { 61 t.Run(v, func(t *testing.T) { 62 dbt.mustExec(fmt.Sprintf("CREATE OR REPLACE TABLE test (id int, value %v)", v)) 63 dbt.mustExec("INSERT INTO test VALUES (1, ?)", expected) 64 rows = dbt.mustQuery("SELECT value FROM test WHERE id = ?", 1) 65 defer rows.Close() 66 if rows.Next() { 67 rows.Scan(&out) 68 if expected != out { 69 dbt.Errorf("%s: %g != %g", v, expected, out) 70 } 71 } else { 72 dbt.Errorf("%s: no data", v) 73 } 74 }) 75 } 76 dbt.mustExec("DROP TABLE IF EXISTS test") 77 }) 78 } 79 80 // TestBindingUint64 tests uint64 binding. Should fail as unit64 is not a 81 // supported binding value by Go's sql package. 82 func TestBindingUint64(t *testing.T) { 83 runDBTest(t, func(dbt *DBTest) { 84 expected := uint64(18446744073709551615) 85 dbt.mustExec("CREATE OR REPLACE TABLE test (id int, value INTEGER)") 86 if _, err := dbt.exec("INSERT INTO test VALUES (1, ?)", expected); err == nil { 87 dbt.Fatal("should fail as uint64 values with high bit set are not supported.") 88 } else { 89 logger.Infof("expected err: %v", err) 90 } 91 dbt.mustExec("DROP TABLE IF EXISTS test") 92 }) 93 } 94 95 func TestBindingDateTimeTimestamp(t *testing.T) { 96 createDSN(PSTLocation) 97 runDBTest(t, func(dbt *DBTest) { 98 expected := time.Now() 99 dbt.mustExec( 100 "CREATE OR REPLACE TABLE tztest (id int, ntz timestamp_ntz, ltz timestamp_ltz, dt date, tm time)") 101 stmt, err := dbt.prepare("INSERT INTO tztest(id,ntz,ltz,dt,tm) VALUES(1,?,?,?,?)") 102 if err != nil { 103 dbt.Fatal(err.Error()) 104 } 105 defer stmt.Close() 106 if _, err = stmt.Exec( 107 DataTypeTimestampNtz, expected, 108 DataTypeTimestampLtz, expected, 109 DataTypeDate, expected, 110 DataTypeTime, expected); err != nil { 111 dbt.Fatal(err) 112 } 113 rows := dbt.mustQuery("SELECT ntz,ltz,dt,tm FROM tztest WHERE id=?", 1) 114 defer rows.Close() 115 var ntz, vltz, dt, tm time.Time 116 columnTypes, err := rows.ColumnTypes() 117 if err != nil { 118 dbt.Errorf("column type error. err: %v", err) 119 } 120 if columnTypes[0].Name() != "NTZ" { 121 dbt.Errorf("expected column name: %v, got: %v", "TEST", columnTypes[0]) 122 } 123 canNull := dbt.mustNullable(columnTypes[0]) 124 if !canNull { 125 dbt.Errorf("expected nullable: %v, got: %v", true, canNull) 126 } 127 if columnTypes[0].DatabaseTypeName() != "TIMESTAMP_NTZ" { 128 dbt.Errorf("expected database type: %v, got: %v", "TIMESTAMP_NTZ", columnTypes[0].DatabaseTypeName()) 129 } 130 dbt.mustFailDecimalSize(columnTypes[0]) 131 dbt.mustFailLength(columnTypes[0]) 132 cols, err := rows.Columns() 133 if err != nil { 134 dbt.Errorf("failed to get columns. err: %v", err) 135 } 136 if len(cols) != 4 || cols[0] != "NTZ" || cols[1] != "LTZ" || cols[2] != "DT" || cols[3] != "TM" { 137 dbt.Errorf("failed to get columns. got: %v", cols) 138 } 139 if rows.Next() { 140 rows.Scan(&ntz, &vltz, &dt, &tm) 141 if expected.UnixNano() != ntz.UnixNano() { 142 dbt.Errorf("returned TIMESTAMP_NTZ value didn't match. expected: %v:%v, got: %v:%v", 143 expected.UnixNano(), expected, ntz.UnixNano(), ntz) 144 } 145 if expected.UnixNano() != vltz.UnixNano() { 146 dbt.Errorf("returned TIMESTAMP_LTZ value didn't match. expected: %v:%v, got: %v:%v", 147 expected.UnixNano(), expected, vltz.UnixNano(), vltz) 148 } 149 if expected.Year() != dt.Year() || expected.Month() != dt.Month() || expected.Day() != dt.Day() { 150 dbt.Errorf("returned DATE value didn't match. expected: %v:%v, got: %v:%v", 151 expected.Unix()*1000, expected, dt.Unix()*1000, dt) 152 } 153 if expected.Hour() != tm.Hour() || expected.Minute() != tm.Minute() || expected.Second() != tm.Second() || expected.Nanosecond() != tm.Nanosecond() { 154 dbt.Errorf("returned TIME value didn't match. expected: %v:%v, got: %v:%v", 155 expected.UnixNano(), expected, tm.UnixNano(), tm) 156 } 157 } else { 158 dbt.Error("no data") 159 } 160 dbt.mustExec("DROP TABLE tztest") 161 }) 162 163 createDSN("UTC") 164 } 165 166 func TestBindingBinary(t *testing.T) { 167 runDBTest(t, func(dbt *DBTest) { 168 dbt.mustExec("CREATE OR REPLACE TABLE bintest (id int, b binary)") 169 var b = []byte{0x01, 0x02, 0x03} 170 dbt.mustExec("INSERT INTO bintest(id,b) VALUES(1, ?)", DataTypeBinary, b) 171 rows := dbt.mustQuery("SELECT b FROM bintest WHERE id=?", 1) 172 defer rows.Close() 173 if rows.Next() { 174 var rb []byte 175 if err := rows.Scan(&rb); err != nil { 176 dbt.Errorf("failed to scan data. err: %v", err) 177 } 178 if !bytes.Equal(b, rb) { 179 dbt.Errorf("failed to match data. expected: %v, got: %v", b, rb) 180 } 181 } else { 182 dbt.Errorf("no data") 183 } 184 dbt.mustExec("DROP TABLE bintest") 185 }) 186 } 187 188 func TestBindingTimestampTZ(t *testing.T) { 189 runDBTest(t, func(dbt *DBTest) { 190 expected := time.Now() 191 dbt.mustExec("CREATE OR REPLACE TABLE tztest (id int, tz timestamp_tz)") 192 stmt, err := dbt.prepare("INSERT INTO tztest(id,tz) VALUES(1, ?)") 193 if err != nil { 194 dbt.Fatal(err.Error()) 195 } 196 defer stmt.Close() 197 if _, err = stmt.Exec(DataTypeTimestampTz, expected); err != nil { 198 dbt.Fatal(err) 199 } 200 rows := dbt.mustQuery("SELECT tz FROM tztest WHERE id=?", 1) 201 defer rows.Close() 202 var v time.Time 203 if rows.Next() { 204 rows.Scan(&v) 205 if expected.UnixNano() != v.UnixNano() { 206 dbt.Errorf("returned value didn't match. expected: %v:%v, got: %v:%v", 207 expected.UnixNano(), expected, v.UnixNano(), v) 208 } 209 } else { 210 dbt.Error("no data") 211 } 212 dbt.mustExec("DROP TABLE tztest") 213 }) 214 } 215 216 // SNOW-755844: Test the use of a pointer *time.Time type in user-defined structures to perform updates/inserts 217 func TestBindingTimePtrInStruct(t *testing.T) { 218 runDBTest(t, func(dbt *DBTest) { 219 type timePtrStruct struct { 220 id *int 221 timeVal *time.Time 222 } 223 var expectedID int = 1 224 var expectedTime time.Time = time.Now() 225 var testStruct timePtrStruct = timePtrStruct{id: &expectedID, timeVal: &expectedTime} 226 dbt.mustExec("CREATE OR REPLACE TABLE timeStructTest (id int, tz timestamp_tz)") 227 228 runInsertQuery := false 229 for i := 0; i < 2; i++ { 230 if !runInsertQuery { 231 _, err := dbt.exec("INSERT INTO timeStructTest(id,tz) VALUES(?, ?)", testStruct.id, testStruct.timeVal) 232 if err != nil { 233 dbt.Fatal(err.Error()) 234 } 235 runInsertQuery = true 236 } else { 237 // Update row with a new time value 238 expectedTime = time.Now().Add(1) 239 testStruct.timeVal = &expectedTime 240 _, err := dbt.exec("UPDATE timeStructTest SET tz = ? where id = ?", testStruct.timeVal, testStruct.id) 241 if err != nil { 242 dbt.Fatal(err.Error()) 243 } 244 } 245 246 rows := dbt.mustQuery("SELECT tz FROM timeStructTest WHERE id=?", &expectedID) 247 defer rows.Close() 248 var v time.Time 249 if rows.Next() { 250 rows.Scan(&v) 251 if expectedTime.UnixNano() != v.UnixNano() { 252 dbt.Errorf("returned value didn't match. expected: %v:%v, got: %v:%v", 253 expectedTime.UnixNano(), expectedTime, v.UnixNano(), v) 254 } 255 } else { 256 dbt.Error("no data") 257 } 258 } 259 dbt.mustExec("DROP TABLE timeStructTest") 260 }) 261 } 262 263 // SNOW-755844: Test the use of a time.Time type in user-defined structures to perform updates/inserts 264 func TestBindingTimeInStruct(t *testing.T) { 265 runDBTest(t, func(dbt *DBTest) { 266 type timeStruct struct { 267 id int 268 timeVal time.Time 269 } 270 var expectedID int = 1 271 var expectedTime time.Time = time.Now() 272 var testStruct timeStruct = timeStruct{id: expectedID, timeVal: expectedTime} 273 dbt.mustExec("CREATE OR REPLACE TABLE timeStructTest (id int, tz timestamp_tz)") 274 275 runInsertQuery := false 276 for i := 0; i < 2; i++ { 277 if !runInsertQuery { 278 _, err := dbt.exec("INSERT INTO timeStructTest(id,tz) VALUES(?, ?)", testStruct.id, testStruct.timeVal) 279 if err != nil { 280 dbt.Fatal(err.Error()) 281 } 282 runInsertQuery = true 283 } else { 284 // Update row with a new time value 285 expectedTime = time.Now().Add(1) 286 testStruct.timeVal = expectedTime 287 _, err := dbt.exec("UPDATE timeStructTest SET tz = ? where id = ?", testStruct.timeVal, testStruct.id) 288 if err != nil { 289 dbt.Fatal(err.Error()) 290 } 291 } 292 293 rows := dbt.mustQuery("SELECT tz FROM timeStructTest WHERE id=?", &expectedID) 294 defer rows.Close() 295 var v time.Time 296 if rows.Next() { 297 rows.Scan(&v) 298 if expectedTime.UnixNano() != v.UnixNano() { 299 dbt.Errorf("returned value didn't match. expected: %v:%v, got: %v:%v", 300 expectedTime.UnixNano(), expectedTime, v.UnixNano(), v) 301 } 302 } else { 303 dbt.Error("no data") 304 } 305 } 306 dbt.mustExec("DROP TABLE timeStructTest") 307 }) 308 } 309 310 func TestBindingInterface(t *testing.T) { 311 runDBTest(t, func(dbt *DBTest) { 312 rows := dbt.mustQueryContext( 313 WithHigherPrecision(context.Background()), selectVariousTypes) 314 defer rows.Close() 315 if !rows.Next() { 316 dbt.Error("failed to query") 317 } 318 var v1, v2, v3, v4, v5, v6 any 319 if err := rows.Scan(&v1, &v2, &v3, &v4, &v5, &v6); err != nil { 320 dbt.Errorf("failed to scan: %#v", err) 321 } 322 if s1, ok := v1.(*big.Float); !ok || s1.Cmp(big.NewFloat(1.0)) != 0 { 323 dbt.Fatalf("failed to fetch. ok: %v, value: %v", ok, v1) 324 } 325 if s2, ok := v2.(int64); !ok || s2 != 2 { 326 dbt.Fatalf("failed to fetch. ok: %v, value: %v", ok, v2) 327 } 328 if s3, ok := v3.(string); !ok || s3 != "t3" { 329 dbt.Fatalf("failed to fetch. ok: %v, value: %v", ok, v3) 330 } 331 if s4, ok := v4.(float64); !ok || s4 != 4.2 { 332 dbt.Fatalf("failed to fetch. ok: %v, value: %v", ok, v4) 333 } 334 }) 335 } 336 337 func TestBindingInterfaceString(t *testing.T) { 338 runDBTest(t, func(dbt *DBTest) { 339 rows := dbt.mustQuery(selectVariousTypes) 340 defer rows.Close() 341 if !rows.Next() { 342 dbt.Error("failed to query") 343 } 344 var v1, v2, v3, v4, v5, v6 any 345 if err := rows.Scan(&v1, &v2, &v3, &v4, &v5, &v6); err != nil { 346 dbt.Errorf("failed to scan: %#v", err) 347 } 348 if s, ok := v1.(string); !ok { 349 dbt.Error("failed to convert to string") 350 } else if d, err := strconv.ParseFloat(s, 64); err != nil { 351 dbt.Errorf("failed to convert to float. value: %v, err: %v", v1, err) 352 } else if d != 1.00 { 353 dbt.Errorf("failed to fetch. expected: 1.00, value: %v", v1) 354 } 355 if s, ok := v2.(string); !ok || s != "2" { 356 dbt.Fatalf("failed to fetch. ok: %v, value: %v", ok, v2) 357 } 358 if s, ok := v3.(string); !ok || s != "t3" { 359 dbt.Fatalf("failed to fetch. ok: %v, value: %v", ok, v3) 360 } 361 }) 362 } 363 364 func TestBulkArrayBindingInterfaceNil(t *testing.T) { 365 nilArray := make([]any, 1) 366 367 runDBTest(t, func(dbt *DBTest) { 368 dbt.mustExec(createTableSQL) 369 defer dbt.mustExec(deleteTableSQL) 370 371 dbt.mustExec(insertSQL, Array(&nilArray), Array(&nilArray), 372 Array(&nilArray), Array(&nilArray), Array(&nilArray), 373 Array(&nilArray, TimestampNTZType), Array(&nilArray, TimestampTZType), 374 Array(&nilArray, TimestampTZType), Array(&nilArray, DateType), 375 Array(&nilArray, TimeType)) 376 rows := dbt.mustQuery(selectAllSQL) 377 defer rows.Close() 378 379 var v0 sql.NullInt32 380 var v1 sql.NullFloat64 381 var v2 sql.NullBool 382 var v3 sql.NullString 383 var v4 []byte 384 var v5, v6, v7, v8, v9 sql.NullTime 385 386 cnt := 0 387 for i := 0; rows.Next(); i++ { 388 if err := rows.Scan(&v0, &v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9); err != nil { 389 t.Fatal(err) 390 } 391 if v0.Valid { 392 t.Fatalf("failed to fetch the sql.NullInt32 column v0. expected %v, got: %v", nilArray[i], v0) 393 } 394 if v1.Valid { 395 t.Fatalf("failed to fetch the sql.NullFloat64 column v1. expected %v, got: %v", nilArray[i], v1) 396 } 397 if v2.Valid { 398 t.Fatalf("failed to fetch the sql.NullBool column v2. expected %v, got: %v", nilArray[i], v2) 399 } 400 if v3.Valid { 401 t.Fatalf("failed to fetch the sql.NullString column v3. expected %v, got: %v", nilArray[i], v3) 402 } 403 if v4 != nil { 404 t.Fatalf("failed to fetch the []byte column v4. expected %v, got: %v", nilArray[i], v4) 405 } 406 if v5.Valid { 407 t.Fatalf("failed to fetch the sql.NullTime column v5. expected %v, got: %v", nilArray[i], v5) 408 } 409 if v6.Valid { 410 t.Fatalf("failed to fetch the sql.NullTime column v6. expected %v, got: %v", nilArray[i], v6) 411 } 412 if v7.Valid { 413 t.Fatalf("failed to fetch the sql.NullTime column v7. expected %v, got: %v", nilArray[i], v7) 414 } 415 if v8.Valid { 416 t.Fatalf("failed to fetch the sql.NullTime column v8. expected %v, got: %v", nilArray[i], v8) 417 } 418 if v9.Valid { 419 t.Fatalf("failed to fetch the sql.NullTime column v9. expected %v, got: %v", nilArray[i], v9) 420 } 421 cnt++ 422 } 423 if cnt != len(nilArray) { 424 t.Fatal("failed to query") 425 } 426 }) 427 } 428 429 func TestBulkArrayBindingInterface(t *testing.T) { 430 intArray := make([]any, 3) 431 intArray[0] = int32(100) 432 intArray[1] = int32(200) 433 434 fltArray := make([]any, 3) 435 fltArray[0] = float64(0.1) 436 fltArray[2] = float64(5.678) 437 438 boolArray := make([]any, 3) 439 boolArray[1] = false 440 boolArray[2] = true 441 442 strArray := make([]any, 3) 443 strArray[2] = "test3" 444 445 byteArray := make([]any, 3) 446 byteArray[0] = []byte{0x01, 0x02, 0x03} 447 byteArray[2] = []byte{0x07, 0x08, 0x09} 448 449 int64Array := make([]any, 3) 450 int64Array[0] = int64(100) 451 int64Array[1] = int64(200) 452 453 runDBTest(t, func(dbt *DBTest) { 454 dbt.mustExec(createTableSQLBulkArray) 455 defer dbt.mustExec(deleteTableSQLBulkArray) 456 457 dbt.mustExec(insertSQLBulkArray, Array(&intArray), Array(&fltArray), 458 Array(&boolArray), Array(&strArray), Array(&byteArray), Array(&int64Array)) 459 rows := dbt.mustQuery(selectAllSQLBulkArray) 460 defer rows.Close() 461 462 var v0 sql.NullInt32 463 var v1 sql.NullFloat64 464 var v2 sql.NullBool 465 var v3 sql.NullString 466 var v4 []byte 467 var v5 sql.NullInt64 468 469 cnt := 0 470 for i := 0; rows.Next(); i++ { 471 if err := rows.Scan(&v0, &v1, &v2, &v3, &v4, &v5); err != nil { 472 t.Fatal(err) 473 } 474 if v0.Valid { 475 if v0.Int32 != intArray[i] { 476 t.Fatalf("failed to fetch the sql.NullInt32 column v0. expected %v, got: %v", intArray[i], v0.Int32) 477 } 478 } else if intArray[i] != nil { 479 t.Fatalf("failed to fetch the sql.NullInt32 column v0. expected %v, got: %v", intArray[i], v0) 480 } 481 if v1.Valid { 482 if v1.Float64 != fltArray[i] { 483 t.Fatalf("failed to fetch the sql.NullFloat64 column v1. expected %v, got: %v", fltArray[i], v1.Float64) 484 } 485 } else if fltArray[i] != nil { 486 t.Fatalf("failed to fetch the sql.NullFloat64 column v1. expected %v, got: %v", fltArray[i], v1) 487 } 488 if v2.Valid { 489 if v2.Bool != boolArray[i] { 490 t.Fatalf("failed to fetch the sql.NullBool column v2. expected %v, got: %v", boolArray[i], v2.Bool) 491 } 492 } else if boolArray[i] != nil { 493 t.Fatalf("failed to fetch the sql.NullBool column v2. expected %v, got: %v", boolArray[i], v2) 494 } 495 if v3.Valid { 496 if v3.String != strArray[i] { 497 t.Fatalf("failed to fetch the sql.NullString column v3. expected %v, got: %v", strArray[i], v3.String) 498 } 499 } else if strArray[i] != nil { 500 t.Fatalf("failed to fetch the sql.NullString column v3. expected %v, got: %v", strArray[i], v3) 501 } 502 if byteArray[i] != nil { 503 if !bytes.Equal(v4, byteArray[i].([]byte)) { 504 t.Fatalf("failed to fetch the []byte column v4. expected %v, got: %v", byteArray[i], v4) 505 } 506 } else if v4 != nil { 507 t.Fatalf("failed to fetch the []byte column v4. expected %v, got: %v", byteArray[i], v4) 508 } 509 if v5.Valid { 510 if v5.Int64 != int64Array[i] { 511 t.Fatalf("failed to fetch the sql.NullInt64 column v5. expected %v, got: %v", int64Array[i], v5.Int64) 512 } 513 } else if int64Array[i] != nil { 514 t.Fatalf("failed to fetch the sql.NullInt64 column v5. expected %v, got: %v", int64Array[i], v5) 515 } 516 cnt++ 517 } 518 if cnt != len(intArray) { 519 t.Fatal("failed to query") 520 } 521 }) 522 } 523 524 func TestBulkArrayBindingInterfaceDateTimeTimestamp(t *testing.T) { 525 tz := time.Now() 526 createDSN(PSTLocation) 527 528 now := time.Now() 529 loc, err := time.LoadLocation(PSTLocation) 530 if err != nil { 531 t.Error(err) 532 } 533 ntzArray := make([]any, 3) 534 ntzArray[0] = now 535 ntzArray[1] = now.Add(1) 536 537 ltzArray := make([]any, 3) 538 ltzArray[1] = now.Add(2).In(loc) 539 ltzArray[2] = now.Add(3).In(loc) 540 541 tzArray := make([]any, 3) 542 tzArray[0] = tz.Add(4).In(loc) 543 tzArray[2] = tz.Add(5).In(loc) 544 545 dtArray := make([]any, 3) 546 dtArray[0] = tz.Add(6).In(loc) 547 dtArray[1] = now.Add(7).In(loc) 548 549 tmArray := make([]any, 3) 550 tmArray[1] = now.Add(8).In(loc) 551 tmArray[2] = now.Add(9).In(loc) 552 553 runDBTest(t, func(dbt *DBTest) { 554 dbt.mustExec(createTableSQLBulkArrayDateTimeTimestamp) 555 defer dbt.mustExec(deleteTableSQLBulkArrayDateTimeTimestamp) 556 557 dbt.mustExec(insertSQLBulkArrayDateTimeTimestamp, 558 Array(&ntzArray, TimestampNTZType), Array(<zArray, TimestampLTZType), 559 Array(&tzArray, TimestampTZType), Array(&dtArray, DateType), 560 Array(&tmArray, TimeType)) 561 562 rows := dbt.mustQuery(selectAllSQLBulkArrayDateTimeTimestamp) 563 defer rows.Close() 564 565 var v0, v1, v2, v3, v4 sql.NullTime 566 567 cnt := 0 568 for i := 0; rows.Next(); i++ { 569 if err := rows.Scan(&v0, &v1, &v2, &v3, &v4); err != nil { 570 t.Fatal(err) 571 } 572 if v0.Valid { 573 if v0.Time.UnixNano() != ntzArray[i].(time.Time).UnixNano() { 574 t.Fatalf("failed to fetch the column v0. expected %v, got: %v", ntzArray[i], v0) 575 } 576 } else if ntzArray[i] != nil { 577 t.Fatalf("failed to fetch the column v0. expected %v, got: %v", ntzArray[i], v0) 578 } 579 if v1.Valid { 580 if v1.Time.UnixNano() != ltzArray[i].(time.Time).UnixNano() { 581 t.Fatalf("failed to fetch the column v1. expected %v, got: %v", ltzArray[i], v1) 582 } 583 } else if ltzArray[i] != nil { 584 t.Fatalf("failed to fetch the column v1. expected %v, got: %v", ltzArray[i], v1) 585 } 586 if v2.Valid { 587 if v2.Time.UnixNano() != tzArray[i].(time.Time).UnixNano() { 588 t.Fatalf("failed to fetch the column v2. expected %v, got: %v", tzArray[i], v2) 589 } 590 } else if tzArray[i] != nil { 591 t.Fatalf("failed to fetch the column v2. expected %v, got: %v", tzArray[i], v2) 592 } 593 if v3.Valid { 594 if v3.Time.Year() != dtArray[i].(time.Time).Year() || 595 v3.Time.Month() != dtArray[i].(time.Time).Month() || 596 v3.Time.Day() != dtArray[i].(time.Time).Day() { 597 t.Fatalf("failed to fetch the column v3. expected %v, got: %v", dtArray[i], v3) 598 } 599 } else if dtArray[i] != nil { 600 t.Fatalf("failed to fetch the column v3. expected %v, got: %v", dtArray[i], v3) 601 } 602 if v4.Valid { 603 if v4.Time.Hour() != tmArray[i].(time.Time).Hour() || 604 v4.Time.Minute() != tmArray[i].(time.Time).Minute() || 605 v4.Time.Second() != tmArray[i].(time.Time).Second() { 606 t.Fatalf("failed to fetch the column v4. expected %v, got: %v", tmArray[i], v4) 607 } 608 } else if tmArray[i] != nil { 609 t.Fatalf("failed to fetch the column v4. expected %v, got: %v", tmArray[i], v4) 610 } 611 cnt++ 612 } 613 if cnt != len(ntzArray) { 614 t.Fatal("failed to query") 615 } 616 }) 617 createDSN("UTC") 618 } 619 620 // TestBindingArray tests basic array binding via the usage of the Array 621 // function that converts the passed Golang slice to a Snowflake array type 622 func TestBindingArray(t *testing.T) { 623 testBindingArray(t, false) 624 } 625 626 // TestBindingBulkArray tests bulk array binding via the usage of the Array 627 // function that converts the passed Golang slice to a Snowflake array type 628 func TestBindingBulkArray(t *testing.T) { 629 if runningOnGithubAction() { 630 t.Skip("client_stage_array_binding_threshold value is internal") 631 } 632 testBindingArray(t, true) 633 } 634 635 func testBindingArray(t *testing.T, bulk bool) { 636 tz := time.Now() 637 createDSN(PSTLocation) 638 intArray := []int{1, 2, 3} 639 fltArray := []float64{0.1, 2.34, 5.678} 640 boolArray := []bool{true, false, true} 641 strArray := []string{"test1", "test2", "test3"} 642 byteArray := [][]byte{{0x01, 0x02, 0x03}, {0x04, 0x05, 0x06}, {0x07, 0x08, 0x09}} 643 644 now := time.Now() 645 loc, err := time.LoadLocation(PSTLocation) 646 if err != nil { 647 t.Error(err) 648 } 649 ntzArray := []time.Time{now, now.Add(1), now.Add(2)} 650 ltzArray := []time.Time{now.Add(3).In(loc), now.Add(4).In(loc), now.Add(5).In(loc)} 651 tzArray := []time.Time{tz.Add(6).In(loc), tz.Add(7).In(loc), tz.Add(8).In(loc)} 652 dtArray := []time.Time{now.Add(9), now.Add(10), now.Add(11)} 653 tmArray := []time.Time{now.Add(12), now.Add(13), now.Add(14)} 654 655 runDBTest(t, func(dbt *DBTest) { 656 dbt.mustExec(createTableSQL) 657 defer dbt.mustExec(deleteTableSQL) 658 if bulk { 659 if _, err := dbt.exec("ALTER SESSION SET CLIENT_STAGE_ARRAY_BINDING_THRESHOLD = 1"); err != nil { 660 t.Error(err) 661 } 662 } 663 664 dbt.mustExec(insertSQL, Array(&intArray), Array(&fltArray), 665 Array(&boolArray), Array(&strArray), Array(&byteArray), 666 Array(&ntzArray, TimestampNTZType), Array(<zArray, TimestampLTZType), 667 Array(&tzArray, TimestampTZType), Array(&dtArray, DateType), 668 Array(&tmArray, TimeType)) 669 rows := dbt.mustQuery(selectAllSQL) 670 defer rows.Close() 671 672 var v0 int 673 var v1 float64 674 var v2 bool 675 var v3 string 676 var v4 []byte 677 var v5, v6, v7, v8, v9 time.Time 678 cnt := 0 679 for i := 0; rows.Next(); i++ { 680 if err := rows.Scan(&v0, &v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9); err != nil { 681 t.Fatal(err) 682 } 683 if v0 != intArray[i] { 684 t.Fatalf("failed to fetch. expected %v, got: %v", intArray[i], v0) 685 } 686 if v1 != fltArray[i] { 687 t.Fatalf("failed to fetch. expected %v, got: %v", fltArray[i], v1) 688 } 689 if v2 != boolArray[i] { 690 t.Fatalf("failed to fetch. expected %v, got: %v", boolArray[i], v2) 691 } 692 if v3 != strArray[i] { 693 t.Fatalf("failed to fetch. expected %v, got: %v", strArray[i], v3) 694 } 695 if !bytes.Equal(v4, byteArray[i]) { 696 t.Fatalf("failed to fetch. expected %v, got: %v", byteArray[i], v4) 697 } 698 if v5.UnixNano() != ntzArray[i].UnixNano() { 699 t.Fatalf("failed to fetch. expected %v, got: %v", ntzArray[i], v5) 700 } 701 if v6.UnixNano() != ltzArray[i].UnixNano() { 702 t.Fatalf("failed to fetch. expected %v, got: %v", ltzArray[i], v6) 703 } 704 if v7.UnixNano() != tzArray[i].UnixNano() { 705 t.Fatalf("failed to fetch. expected %v, got: %v", tzArray[i], v7) 706 } 707 if v8.Year() != dtArray[i].Year() || v8.Month() != dtArray[i].Month() || v8.Day() != dtArray[i].Day() { 708 t.Fatalf("failed to fetch. expected %v, got: %v", dtArray[i], v8) 709 } 710 if v9.Hour() != tmArray[i].Hour() || v9.Minute() != tmArray[i].Minute() || v9.Second() != tmArray[i].Second() { 711 t.Fatalf("failed to fetch. expected %v, got: %v", tmArray[i], v9) 712 } 713 cnt++ 714 } 715 if cnt != len(intArray) { 716 t.Fatal("failed to query") 717 } 718 }) 719 createDSN("UTC") 720 } 721 722 func TestBulkArrayBinding(t *testing.T) { 723 runDBTest(t, func(dbt *DBTest) { 724 dbt.mustExec(fmt.Sprintf("create or replace table %v (c1 integer, c2 string, c3 timestamp_ltz, c4 timestamp_tz, c5 timestamp_ntz, c6 date, c7 time, c8 binary)", dbname)) 725 now := time.Now() 726 someTime := time.Date(1, time.January, 1, 12, 34, 56, 123456789, time.UTC) 727 someDate := time.Date(2024, time.March, 18, 0, 0, 0, 0, time.UTC) 728 someBinary := []byte{0x01, 0x02, 0x03} 729 numRows := 100000 730 intArr := make([]int, numRows) 731 strArr := make([]string, numRows) 732 ltzArr := make([]time.Time, numRows) 733 tzArr := make([]time.Time, numRows) 734 ntzArr := make([]time.Time, numRows) 735 dateArr := make([]time.Time, numRows) 736 timeArr := make([]time.Time, numRows) 737 binArr := make([][]byte, numRows) 738 for i := 0; i < numRows; i++ { 739 intArr[i] = i 740 strArr[i] = "test" + strconv.Itoa(i) 741 ltzArr[i] = now 742 tzArr[i] = now.Add(time.Hour).UTC() 743 ntzArr[i] = now.Add(2 * time.Hour) 744 dateArr[i] = someDate 745 timeArr[i] = someTime 746 binArr[i] = someBinary 747 } 748 dbt.mustExec(fmt.Sprintf("insert into %v values (?, ?, ?, ?, ?, ?, ?, ?)", dbname), Array(&intArr), Array(&strArr), Array(<zArr, TimestampLTZType), Array(&tzArr, TimestampTZType), Array(&ntzArr, TimestampNTZType), Array(&dateArr, DateType), Array(&timeArr, TimeType), Array(&binArr)) 749 rows := dbt.mustQuery("select * from " + dbname + " order by c1") 750 defer rows.Close() 751 cnt := 0 752 var i int 753 var s string 754 var ltz, tz, ntz, date, tt time.Time 755 var b []byte 756 for rows.Next() { 757 if err := rows.Scan(&i, &s, <z, &tz, &ntz, &date, &tt, &b); err != nil { 758 t.Fatal(err) 759 } 760 assertEqualE(t, i, cnt) 761 assertEqualE(t, "test"+strconv.Itoa(cnt), s) 762 assertEqualE(t, ltz.UTC(), now.UTC()) 763 assertEqualE(t, tz.UTC(), now.Add(time.Hour).UTC()) 764 assertEqualE(t, ntz.UTC(), now.Add(2*time.Hour).UTC()) 765 assertEqualE(t, date, someDate) 766 assertEqualE(t, tt, someTime) 767 assertBytesEqualE(t, b, someBinary) 768 cnt++ 769 } 770 if cnt != numRows { 771 t.Fatalf("expected %v rows, got %v", numRows, cnt) 772 } 773 }) 774 } 775 776 func TestBulkArrayBindingTimeWithPrecision(t *testing.T) { 777 runDBTest(t, func(dbt *DBTest) { 778 dbt.mustExec(fmt.Sprintf("create or replace table %v (s time(0), ms time(3), us time(6), ns time(9))", dbname)) 779 someTimeWithSeconds := time.Date(1, time.January, 1, 1, 1, 1, 0, time.UTC) 780 someTimeWithMilliseconds := time.Date(1, time.January, 1, 2, 2, 2, 123000000, time.UTC) 781 someTimeWithMicroseconds := time.Date(1, time.January, 1, 3, 3, 3, 123456000, time.UTC) 782 someTimeWithNanoseconds := time.Date(1, time.January, 1, 4, 4, 4, 123456789, time.UTC) 783 numRows := 100000 784 secondsArr := make([]time.Time, numRows) 785 millisecondsArr := make([]time.Time, numRows) 786 microsecondsArr := make([]time.Time, numRows) 787 nanosecondsArr := make([]time.Time, numRows) 788 for i := 0; i < numRows; i++ { 789 secondsArr[i] = someTimeWithSeconds 790 millisecondsArr[i] = someTimeWithMilliseconds 791 microsecondsArr[i] = someTimeWithMicroseconds 792 nanosecondsArr[i] = someTimeWithNanoseconds 793 } 794 dbt.mustExec(fmt.Sprintf("insert into %v values (?, ?, ?, ?)", dbname), Array(&secondsArr, TimeType), Array(&millisecondsArr, TimeType), Array(µsecondsArr, TimeType), Array(&nanosecondsArr, TimeType)) 795 rows := dbt.mustQuery("select * from " + dbname) 796 defer rows.Close() 797 cnt := 0 798 var s, ms, us, ns time.Time 799 for rows.Next() { 800 if err := rows.Scan(&s, &ms, &us, &ns); err != nil { 801 t.Fatal(err) 802 } 803 assertEqualE(t, s, someTimeWithSeconds) 804 assertEqualE(t, ms, someTimeWithMilliseconds) 805 assertEqualE(t, us, someTimeWithMicroseconds) 806 assertEqualE(t, ns, someTimeWithNanoseconds) 807 cnt++ 808 } 809 if cnt != numRows { 810 t.Fatalf("expected %v rows, got %v", numRows, cnt) 811 } 812 }) 813 } 814 815 func TestBulkArrayMultiPartBinding(t *testing.T) { 816 rowCount := 1000000 // large enough to be partitioned into multiple files 817 rand.Seed(time.Now().UnixNano()) 818 randomIter := rand.Intn(3) + 2 819 randomStrings := make([]string, rowCount) 820 str := randomString(30) 821 for i := 0; i < rowCount; i++ { 822 randomStrings[i] = str 823 } 824 tempTableName := fmt.Sprintf("test_table_%v", randomString(5)) 825 ctx := context.Background() 826 827 runDBTest(t, func(dbt *DBTest) { 828 dbt.mustExec(fmt.Sprintf("CREATE TABLE %s (C VARCHAR(64) NOT NULL)", tempTableName)) 829 defer dbt.mustExec("drop table " + tempTableName) 830 831 for i := 0; i < randomIter; i++ { 832 dbt.mustExecContext(ctx, 833 fmt.Sprintf("INSERT INTO %s VALUES (?)", tempTableName), 834 Array(&randomStrings)) 835 rows := dbt.mustQuery("select count(*) from " + tempTableName) 836 defer rows.Close() 837 if rows.Next() { 838 var count int 839 if err := rows.Scan(&count); err != nil { 840 t.Error(err) 841 } 842 } 843 } 844 845 rows := dbt.mustQuery("select count(*) from " + tempTableName) 846 defer rows.Close() 847 if rows.Next() { 848 var count int 849 if err := rows.Scan(&count); err != nil { 850 t.Error(err) 851 } 852 if count != randomIter*rowCount { 853 t.Errorf("expected %v rows, got %v rows intead", randomIter*rowCount, count) 854 } 855 } 856 }) 857 } 858 859 func TestBulkArrayMultiPartBindingInt(t *testing.T) { 860 runDBTest(t, func(dbt *DBTest) { 861 dbt.mustExec("create or replace table binding_test (c1 integer)") 862 startNum := 1000000 863 endNum := 3000000 864 numRows := endNum - startNum 865 intArr := make([]int, numRows) 866 for i := startNum; i < endNum; i++ { 867 intArr[i-startNum] = i 868 } 869 _, err := dbt.exec("insert into binding_test values (?)", Array(&intArr)) 870 if err != nil { 871 t.Errorf("Should have succeeded to insert. err: %v", err) 872 } 873 874 rows := dbt.mustQuery("select * from binding_test order by c1") 875 defer rows.Close() 876 cnt := startNum 877 var i int 878 for rows.Next() { 879 if err := rows.Scan(&i); err != nil { 880 t.Fatal(err) 881 } 882 if i != cnt { 883 t.Errorf("expected: %v, got: %v", cnt, i) 884 } 885 cnt++ 886 } 887 if cnt != endNum { 888 t.Fatalf("expected %v rows, got %v", numRows, (cnt - startNum)) 889 } 890 dbt.mustExec("DROP TABLE binding_test") 891 }) 892 } 893 894 func TestBulkArrayMultiPartBindingWithNull(t *testing.T) { 895 runDBTest(t, func(dbt *DBTest) { 896 dbt.mustExec("create or replace table binding_test (c1 integer, c2 string)") 897 startNum := 1000000 898 endNum := 2000000 899 numRows := endNum - startNum 900 901 // Define the integer and string arrays 902 intArr := make([]any, numRows) 903 stringArr := make([]any, numRows) 904 for i := startNum; i < endNum; i++ { 905 intArr[i-startNum] = i 906 stringArr[i-startNum] = fmt.Sprint(i) 907 } 908 909 // Set some of the rows to NULL 910 intArr[numRows-1] = nil 911 intArr[numRows-2] = nil 912 intArr[numRows-3] = nil 913 stringArr[1] = nil 914 stringArr[2] = nil 915 stringArr[3] = nil 916 917 _, err := dbt.exec("insert into binding_test values (?, ?)", Array(&intArr), Array(&stringArr)) 918 if err != nil { 919 t.Errorf("Should have succeeded to insert. err: %v", err) 920 } 921 922 rows := dbt.mustQuery("select * from binding_test order by c1,c2") 923 defer rows.Close() 924 cnt := startNum 925 var i sql.NullInt32 926 var s sql.NullString 927 for rows.Next() { 928 if err := rows.Scan(&i, &s); err != nil { 929 t.Fatal(err) 930 } 931 // Verify integer column c1 932 if i.Valid { 933 if int(i.Int32) != intArr[cnt-startNum] { 934 t.Fatalf("expected: %v, got: %v", cnt, int(i.Int32)) 935 } 936 } else if !(cnt == startNum+numRows-1 || cnt == startNum+numRows-2 || cnt == startNum+numRows-3) { 937 t.Fatalf("expected NULL in column c1 at index: %v", cnt-startNum) 938 } 939 // Verify string column c2 940 if s.Valid { 941 if s.String != stringArr[cnt-startNum] { 942 t.Fatalf("expected: %v, got: %v", cnt, s.String) 943 } 944 } else if !(cnt == startNum+1 || cnt == startNum+2 || cnt == startNum+3) { 945 t.Fatalf("expected NULL in column c2 at index: %v", cnt-startNum) 946 } 947 cnt++ 948 } 949 if cnt != endNum { 950 t.Fatalf("expected %v rows, got %v", numRows, (cnt - startNum)) 951 } 952 dbt.mustExec("DROP TABLE binding_test") 953 }) 954 } 955 956 func TestFunctionParameters(t *testing.T) { 957 testcases := []struct { 958 testDesc string 959 paramType string 960 input any 961 nullResult bool 962 }{ 963 {"textAndNullStringResultInNull", "text", sql.NullString{}, true}, 964 {"numberAndNullInt64ResultInNull", "number", sql.NullInt64{}, true}, 965 {"floatAndNullFloat64ResultInNull", "float", sql.NullFloat64{}, true}, 966 {"booleanAndAndNullBoolResultInNull", "boolean", sql.NullBool{}, true}, 967 {"dateAndTypedNullTimeResultInNull", "date", TypedNullTime{sql.NullTime{}, DateType}, true}, 968 {"datetimeAndTypedNullTimeResultInNull", "datetime", TypedNullTime{sql.NullTime{}, TimestampNTZType}, true}, 969 {"timeAndTypedNullTimeResultInNull", "time", TypedNullTime{sql.NullTime{}, TimeType}, true}, 970 {"timestampAndTypedNullTimeResultInNull", "timestamp", TypedNullTime{sql.NullTime{}, TimestampNTZType}, true}, 971 {"timestamp_ntzAndTypedNullTimeResultInNull", "timestamp_ntz", TypedNullTime{sql.NullTime{}, TimestampNTZType}, true}, 972 {"timestamp_ltzAndTypedNullTimeResultInNull", "timestamp_ltz", TypedNullTime{sql.NullTime{}, TimestampLTZType}, true}, 973 {"timestamp_tzAndTypedNullTimeResultInNull", "timestamp_tz", TypedNullTime{sql.NullTime{}, TimestampTZType}, true}, 974 {"textAndStringResultInNotNull", "text", "string", false}, 975 {"numberAndIntegerResultInNotNull", "number", 123, false}, 976 {"floatAndFloatResultInNotNull", "float", 123.01, false}, 977 {"booleanAndBooleanResultInNotNull", "boolean", true, false}, 978 {"dateAndTimeResultInNotNull", "date", time.Now(), false}, 979 {"datetimeAndTimeResultInNotNull", "datetime", time.Now(), false}, 980 {"timeAndTimeResultInNotNull", "time", time.Now(), false}, 981 {"timestampAndTimeResultInNotNull", "timestamp", time.Now(), false}, 982 {"timestamp_ntzAndTimeResultInNotNull", "timestamp_ntz", time.Now(), false}, 983 {"timestamp_ltzAndTimeResultInNotNull", "timestamp_ltz", time.Now(), false}, 984 {"timestamp_tzAndTimeResultInNotNull", "timestamp_tz", time.Now(), false}, 985 } 986 987 runDBTest(t, func(dbt *DBTest) { 988 _, err := dbt.exec("ALTER SESSION SET BIND_NULL_VALUE_USE_NULL_DATATYPE=false") 989 if err != nil { 990 log.Println(err) 991 } 992 for _, tc := range testcases { 993 t.Run(tc.testDesc, func(t *testing.T) { 994 query := fmt.Sprintf(` 995 CREATE OR REPLACE FUNCTION NULLPARAMFUNCTION("param1" %v) 996 RETURNS TABLE("r1" %v) 997 LANGUAGE SQL 998 AS 'select param1';`, tc.paramType, tc.paramType) 999 dbt.mustExec(query) 1000 rows, err := dbt.query("select * from table(NULLPARAMFUNCTION(?))", tc.input) 1001 if err != nil { 1002 t.Fatal(err) 1003 } 1004 defer rows.Close() 1005 if rows.Err() != nil { 1006 t.Fatal(err) 1007 } 1008 if !rows.Next() { 1009 t.Fatal("no rows fetched") 1010 } 1011 var r1 any 1012 err = rows.Scan(&r1) 1013 if err != nil { 1014 t.Fatal(err) 1015 } 1016 if tc.nullResult && r1 != nil { 1017 t.Fatalf("the result for %v is of type %v but should be null", tc.paramType, reflect.TypeOf(r1)) 1018 } 1019 if !tc.nullResult && r1 == nil { 1020 t.Fatalf("the result for %v should not be null", tc.paramType) 1021 } 1022 }) 1023 } 1024 }) 1025 } 1026 1027 func TestVariousBindingModes(t *testing.T) { 1028 testcases := []struct { 1029 testDesc string 1030 paramType string 1031 input any 1032 isNil bool 1033 }{ 1034 {"textAndString", "text", "string", false}, 1035 {"numberAndInteger", "number", 123, false}, 1036 {"floatAndFloat", "float", 123.01, false}, 1037 {"booleanAndBoolean", "boolean", true, false}, 1038 {"dateAndTime", "date", time.Now().Truncate(24 * time.Hour), false}, 1039 {"datetimeAndTime", "datetime", time.Now(), false}, 1040 {"timeAndTime", "time", "12:34:56", false}, 1041 {"timestampAndTime", "timestamp", time.Now(), false}, 1042 {"timestamp_ntzAndTime", "timestamp_ntz", time.Now(), false}, 1043 {"timestamp_ltzAndTime", "timestamp_ltz", time.Now(), false}, 1044 {"timestamp_tzAndTime", "timestamp_tz", time.Now(), false}, 1045 {"textAndNullString", "text", sql.NullString{}, true}, 1046 {"numberAndNullInt64", "number", sql.NullInt64{}, true}, 1047 {"floatAndNullFloat64", "float", sql.NullFloat64{}, true}, 1048 {"booleanAndAndNullBool", "boolean", sql.NullBool{}, true}, 1049 {"dateAndTypedNullTime", "date", TypedNullTime{sql.NullTime{}, DateType}, true}, 1050 {"datetimeAndTypedNullTime", "datetime", TypedNullTime{sql.NullTime{}, TimestampNTZType}, true}, 1051 {"timeAndTypedNullTime", "time", TypedNullTime{sql.NullTime{}, TimeType}, true}, 1052 {"timestampAndTypedNullTime", "timestamp", TypedNullTime{sql.NullTime{}, TimestampNTZType}, true}, 1053 {"timestamp_ntzAndTypedNullTime", "timestamp_ntz", TypedNullTime{sql.NullTime{}, TimestampNTZType}, true}, 1054 {"timestamp_ltzAndTypedNullTime", "timestamp_ltz", TypedNullTime{sql.NullTime{}, TimestampLTZType}, true}, 1055 {"timestamp_tzAndTypedNullTime", "timestamp_tz", TypedNullTime{sql.NullTime{}, TimestampTZType}, true}, 1056 {"LOBSmallSize", fmt.Sprintf("varchar(%v)", smallSize), randomString(smallSize), false}, 1057 {"LOBOriginSize", fmt.Sprintf("varchar(%v)", originSize), randomString(originSize), false}, 1058 {"LOBMediumSize", fmt.Sprintf("varchar(%v)", mediumSize), randomString(mediumSize), false}, 1059 {"LOBLargeSize", fmt.Sprintf("varchar(%v)", largeSize), randomString(largeSize), false}, 1060 {"LOBMaxSize", fmt.Sprintf("varchar(%v)", maxLOBSize), randomString(maxLOBSize), false}, 1061 } 1062 1063 bindingModes := []struct { 1064 param string 1065 query string 1066 transform func(any) any 1067 }{ 1068 { 1069 param: "?", 1070 transform: func(v any) any { return v }, 1071 }, 1072 { 1073 param: ":1", 1074 transform: func(v any) any { return v }, 1075 }, 1076 { 1077 param: ":param", 1078 transform: func(v any) any { return sql.Named("param", v) }, 1079 }, 1080 } 1081 1082 runDBTest(t, func(dbt *DBTest) { 1083 for _, tc := range testcases { 1084 // TODO SNOW-1264687 1085 if strings.Contains(tc.testDesc, "LOB") { 1086 skipOnJenkins(t, "skipped until SNOW-1264687 is fixed") 1087 } 1088 for _, bindingMode := range bindingModes { 1089 t.Run(tc.testDesc+" "+bindingMode.param, func(t *testing.T) { 1090 query := fmt.Sprintf(`CREATE OR REPLACE TABLE BINDING_MODES(param1 %v)`, tc.paramType) 1091 dbt.mustExec(query) 1092 if _, err := dbt.exec(fmt.Sprintf("INSERT INTO BINDING_MODES VALUES (%v)", bindingMode.param), bindingMode.transform(tc.input)); err != nil { 1093 t.Fatal(err) 1094 } 1095 if tc.isNil { 1096 query = "SELECT * FROM BINDING_MODES WHERE param1 IS NULL" 1097 } else { 1098 query = fmt.Sprintf("SELECT * FROM BINDING_MODES WHERE param1 = %v", bindingMode.param) 1099 } 1100 rows, err := dbt.query(query, bindingMode.transform(tc.input)) 1101 if err != nil { 1102 t.Fatal(err) 1103 } 1104 defer rows.Close() 1105 if !rows.Next() { 1106 t.Fatal("Expected to return a row") 1107 } 1108 }) 1109 } 1110 } 1111 }) 1112 } 1113 1114 func TestLOBRetrievalWithArrow(t *testing.T) { 1115 testLOBRetrieval(t, true) 1116 } 1117 1118 func TestLOBRetrievalWithJSON(t *testing.T) { 1119 testLOBRetrieval(t, false) 1120 } 1121 1122 func testLOBRetrieval(t *testing.T, useArrowFormat bool) { 1123 // the LOB sizes to be tested 1124 testSizes := [5]int{smallSize, originSize, mediumSize, largeSize, maxLOBSize} 1125 var res string 1126 1127 runDBTest(t, func(dbt *DBTest) { 1128 dbt.exec(enableFeatureMaxLOBSize) 1129 if useArrowFormat { 1130 dbt.mustExec(forceARROW) 1131 } else { 1132 dbt.mustExec(forceJSON) 1133 } 1134 1135 for _, testSize := range testSizes { 1136 t.Run(fmt.Sprintf("testLOB_%v_useArrowFormat=%v", strconv.Itoa(testSize), strconv.FormatBool(useArrowFormat)), func(t *testing.T) { 1137 rows, err := dbt.query(fmt.Sprintf("SELECT randstr(%v, 124)", testSize)) 1138 assertNilF(t, err) 1139 defer rows.Close() 1140 assertTrueF(t, rows.Next(), fmt.Sprintf("no rows returned for the LOB size %v", testSize)) 1141 1142 // retrieve the result 1143 err = rows.Scan(&res) 1144 assertNilF(t, err) 1145 1146 // verify the length of the result 1147 assertEqualF(t, len(res), testSize) 1148 }) 1149 } 1150 dbt.exec(unsetFeatureMaxLOBSize) 1151 }) 1152 } 1153 1154 func TestInsertLobDataWithLiteralArrow(t *testing.T) { 1155 // TODO SNOW-1264687 1156 skipOnJenkins(t, "skipped until SNOW-1264687 is fixed") 1157 testInsertLOBData(t, true, true) 1158 } 1159 1160 func TestInsertLobDataWithLiteralJSON(t *testing.T) { 1161 // TODO SNOW-1264687 1162 skipOnJenkins(t, "skipped until SNOW-1264687 is fixed") 1163 testInsertLOBData(t, false, true) 1164 } 1165 1166 func TestInsertLobDataWithBindingsArrow(t *testing.T) { 1167 // TODO SNOW-1264687 1168 skipOnJenkins(t, "skipped until SNOW-1264687 is fixed") 1169 testInsertLOBData(t, true, false) 1170 } 1171 1172 func TestInsertLobDataWithBindingsJSON(t *testing.T) { 1173 // TODO SNOW-1264687 1174 skipOnJenkins(t, "skipped until SNOW-1264687 is fixed") 1175 testInsertLOBData(t, false, false) 1176 } 1177 1178 func testInsertLOBData(t *testing.T, useArrowFormat bool, isLiteral bool) { 1179 expectedNumCols := 3 1180 columnMeta := []struct { 1181 columnName string 1182 columnType reflect.Type 1183 }{ 1184 {"C1", reflect.TypeOf("")}, 1185 {"C2", reflect.TypeOf("")}, 1186 {"C3", reflect.TypeOf(int64(0))}, 1187 } 1188 testCases := []struct { 1189 testDesc string 1190 c1Size int 1191 c2Size int 1192 c3Size int 1193 }{ 1194 {"testLOBInsertSmallSize", smallSize, smallSize, lobRandomRange}, 1195 {"testLOBInsertOriginSize", originSize, originSize, lobRandomRange}, 1196 {"testLOBInsertMediumSize", mediumSize, originSize, lobRandomRange}, 1197 {"testLOBInsertLargeSize", largeSize, originSize, lobRandomRange}, 1198 {"testLOBInsertMaxSize", maxLOBSize, originSize, lobRandomRange}, 1199 } 1200 1201 runDBTest(t, func(dbt *DBTest) { 1202 var c1 string 1203 var c2 string 1204 var c3 int 1205 1206 dbt.exec(enableFeatureMaxLOBSize) 1207 if useArrowFormat { 1208 dbt.mustExec(forceARROW) 1209 } else { 1210 dbt.mustExec(forceJSON) 1211 } 1212 1213 for _, tc := range testCases { 1214 t.Run(tc.testDesc, func(t *testing.T) { 1215 // initialize test data 1216 c1Data := randomString(tc.c1Size) 1217 c2Data := randomString(tc.c2Size) 1218 c3Data := rand.Intn(tc.c3Size) 1219 1220 dbt.mustExec(fmt.Sprintf("CREATE OR REPLACE TABLE lob_test_table (c1 varchar(%v), c2 varchar(%v), c3 int)", tc.c1Size, tc.c2Size)) 1221 if isLiteral { 1222 dbt.mustExec(fmt.Sprintf("INSERT INTO lob_test_table VALUES ('%s', '%s', %v)", c1Data, c2Data, c3Data)) 1223 } else { 1224 dbt.mustExec("INSERT INTO lob_test_table VALUES (?, ?, ?)", c1Data, c2Data, c3Data) 1225 } 1226 rows, err := dbt.query("SELECT * FROM lob_test_table") 1227 assertNilF(t, err) 1228 defer rows.Close() 1229 assertTrueF(t, rows.Next(), fmt.Sprintf("%s: no rows returned", tc.testDesc)) 1230 1231 err = rows.Scan(&c1, &c2, &c3) 1232 assertNilF(t, err) 1233 1234 // check the number of columns 1235 columnTypes, err := rows.ColumnTypes() 1236 assertNilF(t, err) 1237 assertEqualF(t, len(columnTypes), expectedNumCols) 1238 1239 // verify the column metadata: name, type and length 1240 for colIdx := 0; colIdx < expectedNumCols; colIdx++ { 1241 colName := columnTypes[colIdx].Name() 1242 assertEqualF(t, colName, columnMeta[colIdx].columnName) 1243 1244 colType := columnTypes[colIdx].ScanType() 1245 assertEqualF(t, colType, columnMeta[colIdx].columnType) 1246 1247 colLength, ok := columnTypes[colIdx].Length() 1248 1249 switch colIdx { 1250 case 0: 1251 assertTrueF(t, ok) 1252 assertEqualF(t, colLength, int64(tc.c1Size)) 1253 // verify the data 1254 assertEqualF(t, c1, c1Data) 1255 case 1: 1256 assertTrueF(t, ok) 1257 assertEqualF(t, colLength, int64(tc.c2Size)) 1258 // verify the data 1259 assertEqualF(t, c2, c2Data) 1260 case 2: 1261 assertFalseF(t, ok) 1262 // verify the data 1263 assertEqualF(t, c3, c3Data) 1264 } 1265 } 1266 }) 1267 dbt.mustExec("DROP TABLE IF EXISTS lob_test_table") 1268 } 1269 dbt.exec(unsetFeatureMaxLOBSize) 1270 }) 1271 }