You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1241 lines
32 KiB

  1. // Copyright 2015 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package bigquery
  15. import (
  16. "encoding/base64"
  17. "fmt"
  18. "math"
  19. "math/big"
  20. "testing"
  21. "time"
  22. "cloud.google.com/go/civil"
  23. "cloud.google.com/go/internal/testutil"
  24. "github.com/google/go-cmp/cmp"
  25. bq "google.golang.org/api/bigquery/v2"
  26. )
  27. func TestConvertBasicValues(t *testing.T) {
  28. schema := Schema{
  29. {Type: StringFieldType},
  30. {Type: IntegerFieldType},
  31. {Type: FloatFieldType},
  32. {Type: BooleanFieldType},
  33. {Type: BytesFieldType},
  34. {Type: NumericFieldType},
  35. {Type: GeographyFieldType},
  36. }
  37. row := &bq.TableRow{
  38. F: []*bq.TableCell{
  39. {V: "a"},
  40. {V: "1"},
  41. {V: "1.2"},
  42. {V: "true"},
  43. {V: base64.StdEncoding.EncodeToString([]byte("foo"))},
  44. {V: "123.123456789"},
  45. {V: testGeography},
  46. },
  47. }
  48. got, err := convertRow(row, schema)
  49. if err != nil {
  50. t.Fatalf("error converting: %v", err)
  51. }
  52. want := []Value{"a", int64(1), 1.2, true, []byte("foo"), big.NewRat(123123456789, 1e9), testGeography}
  53. if !testutil.Equal(got, want) {
  54. t.Errorf("converting basic values: got:\n%v\nwant:\n%v", got, want)
  55. }
  56. }
  57. func TestConvertTime(t *testing.T) {
  58. schema := Schema{
  59. {Type: TimestampFieldType},
  60. {Type: DateFieldType},
  61. {Type: TimeFieldType},
  62. {Type: DateTimeFieldType},
  63. }
  64. ts := testTimestamp.Round(time.Millisecond)
  65. row := &bq.TableRow{
  66. F: []*bq.TableCell{
  67. {V: fmt.Sprintf("%.10f", float64(ts.UnixNano())/1e9)},
  68. {V: testDate.String()},
  69. {V: testTime.String()},
  70. {V: testDateTime.String()},
  71. },
  72. }
  73. got, err := convertRow(row, schema)
  74. if err != nil {
  75. t.Fatalf("error converting: %v", err)
  76. }
  77. want := []Value{ts, testDate, testTime, testDateTime}
  78. for i, g := range got {
  79. w := want[i]
  80. if !testutil.Equal(g, w) {
  81. t.Errorf("#%d: got:\n%v\nwant:\n%v", i, g, w)
  82. }
  83. }
  84. if got[0].(time.Time).Location() != time.UTC {
  85. t.Errorf("expected time zone UTC: got:\n%v", got)
  86. }
  87. }
  88. func TestConvertSmallTimes(t *testing.T) {
  89. for _, year := range []int{1600, 1066, 1} {
  90. want := time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC)
  91. s := fmt.Sprintf("%.10f", float64(want.Unix()))
  92. got, err := convertBasicType(s, TimestampFieldType)
  93. if err != nil {
  94. t.Fatal(err)
  95. }
  96. if !got.(time.Time).Equal(want) {
  97. t.Errorf("got %v, want %v", got, want)
  98. }
  99. }
  100. }
  101. func TestConvertNullValues(t *testing.T) {
  102. schema := Schema{{Type: StringFieldType}}
  103. row := &bq.TableRow{
  104. F: []*bq.TableCell{
  105. {V: nil},
  106. },
  107. }
  108. got, err := convertRow(row, schema)
  109. if err != nil {
  110. t.Fatalf("error converting: %v", err)
  111. }
  112. want := []Value{nil}
  113. if !testutil.Equal(got, want) {
  114. t.Errorf("converting null values: got:\n%v\nwant:\n%v", got, want)
  115. }
  116. }
  117. func TestBasicRepetition(t *testing.T) {
  118. schema := Schema{
  119. {Type: IntegerFieldType, Repeated: true},
  120. }
  121. row := &bq.TableRow{
  122. F: []*bq.TableCell{
  123. {
  124. V: []interface{}{
  125. map[string]interface{}{
  126. "v": "1",
  127. },
  128. map[string]interface{}{
  129. "v": "2",
  130. },
  131. map[string]interface{}{
  132. "v": "3",
  133. },
  134. },
  135. },
  136. },
  137. }
  138. got, err := convertRow(row, schema)
  139. if err != nil {
  140. t.Fatalf("error converting: %v", err)
  141. }
  142. want := []Value{[]Value{int64(1), int64(2), int64(3)}}
  143. if !testutil.Equal(got, want) {
  144. t.Errorf("converting basic repeated values: got:\n%v\nwant:\n%v", got, want)
  145. }
  146. }
  147. func TestNestedRecordContainingRepetition(t *testing.T) {
  148. schema := Schema{
  149. {
  150. Type: RecordFieldType,
  151. Schema: Schema{
  152. {Type: IntegerFieldType, Repeated: true},
  153. },
  154. },
  155. }
  156. row := &bq.TableRow{
  157. F: []*bq.TableCell{
  158. {
  159. V: map[string]interface{}{
  160. "f": []interface{}{
  161. map[string]interface{}{
  162. "v": []interface{}{
  163. map[string]interface{}{"v": "1"},
  164. map[string]interface{}{"v": "2"},
  165. map[string]interface{}{"v": "3"},
  166. },
  167. },
  168. },
  169. },
  170. },
  171. },
  172. }
  173. got, err := convertRow(row, schema)
  174. if err != nil {
  175. t.Fatalf("error converting: %v", err)
  176. }
  177. want := []Value{[]Value{[]Value{int64(1), int64(2), int64(3)}}}
  178. if !testutil.Equal(got, want) {
  179. t.Errorf("converting basic repeated values: got:\n%v\nwant:\n%v", got, want)
  180. }
  181. }
  182. func TestRepeatedRecordContainingRepetition(t *testing.T) {
  183. schema := Schema{
  184. {
  185. Type: RecordFieldType,
  186. Repeated: true,
  187. Schema: Schema{
  188. {Type: IntegerFieldType, Repeated: true},
  189. },
  190. },
  191. }
  192. row := &bq.TableRow{F: []*bq.TableCell{
  193. {
  194. V: []interface{}{ // repeated records.
  195. map[string]interface{}{ // first record.
  196. "v": map[string]interface{}{ // pointless single-key-map wrapper.
  197. "f": []interface{}{ // list of record fields.
  198. map[string]interface{}{ // only record (repeated ints)
  199. "v": []interface{}{ // pointless wrapper.
  200. map[string]interface{}{
  201. "v": "1",
  202. },
  203. map[string]interface{}{
  204. "v": "2",
  205. },
  206. map[string]interface{}{
  207. "v": "3",
  208. },
  209. },
  210. },
  211. },
  212. },
  213. },
  214. map[string]interface{}{ // second record.
  215. "v": map[string]interface{}{
  216. "f": []interface{}{
  217. map[string]interface{}{
  218. "v": []interface{}{
  219. map[string]interface{}{
  220. "v": "4",
  221. },
  222. map[string]interface{}{
  223. "v": "5",
  224. },
  225. map[string]interface{}{
  226. "v": "6",
  227. },
  228. },
  229. },
  230. },
  231. },
  232. },
  233. },
  234. },
  235. }}
  236. got, err := convertRow(row, schema)
  237. if err != nil {
  238. t.Fatalf("error converting: %v", err)
  239. }
  240. want := []Value{ // the row is a list of length 1, containing an entry for the repeated record.
  241. []Value{ // the repeated record is a list of length 2, containing an entry for each repetition.
  242. []Value{ // the record is a list of length 1, containing an entry for the repeated integer field.
  243. []Value{int64(1), int64(2), int64(3)}, // the repeated integer field is a list of length 3.
  244. },
  245. []Value{ // second record
  246. []Value{int64(4), int64(5), int64(6)},
  247. },
  248. },
  249. }
  250. if !testutil.Equal(got, want) {
  251. t.Errorf("converting repeated records with repeated values: got:\n%v\nwant:\n%v", got, want)
  252. }
  253. }
  254. func TestRepeatedRecordContainingRecord(t *testing.T) {
  255. schema := Schema{
  256. {
  257. Type: RecordFieldType,
  258. Repeated: true,
  259. Schema: Schema{
  260. {
  261. Type: StringFieldType,
  262. },
  263. {
  264. Type: RecordFieldType,
  265. Schema: Schema{
  266. {Type: IntegerFieldType},
  267. {Type: StringFieldType},
  268. },
  269. },
  270. },
  271. },
  272. }
  273. row := &bq.TableRow{F: []*bq.TableCell{
  274. {
  275. V: []interface{}{ // repeated records.
  276. map[string]interface{}{ // first record.
  277. "v": map[string]interface{}{ // pointless single-key-map wrapper.
  278. "f": []interface{}{ // list of record fields.
  279. map[string]interface{}{ // first record field (name)
  280. "v": "first repeated record",
  281. },
  282. map[string]interface{}{ // second record field (nested record).
  283. "v": map[string]interface{}{ // pointless single-key-map wrapper.
  284. "f": []interface{}{ // nested record fields
  285. map[string]interface{}{
  286. "v": "1",
  287. },
  288. map[string]interface{}{
  289. "v": "two",
  290. },
  291. },
  292. },
  293. },
  294. },
  295. },
  296. },
  297. map[string]interface{}{ // second record.
  298. "v": map[string]interface{}{
  299. "f": []interface{}{
  300. map[string]interface{}{
  301. "v": "second repeated record",
  302. },
  303. map[string]interface{}{
  304. "v": map[string]interface{}{
  305. "f": []interface{}{
  306. map[string]interface{}{
  307. "v": "3",
  308. },
  309. map[string]interface{}{
  310. "v": "four",
  311. },
  312. },
  313. },
  314. },
  315. },
  316. },
  317. },
  318. },
  319. },
  320. }}
  321. got, err := convertRow(row, schema)
  322. if err != nil {
  323. t.Fatalf("error converting: %v", err)
  324. }
  325. // TODO: test with flattenresults.
  326. want := []Value{ // the row is a list of length 1, containing an entry for the repeated record.
  327. []Value{ // the repeated record is a list of length 2, containing an entry for each repetition.
  328. []Value{ // record contains a string followed by a nested record.
  329. "first repeated record",
  330. []Value{
  331. int64(1),
  332. "two",
  333. },
  334. },
  335. []Value{ // second record.
  336. "second repeated record",
  337. []Value{
  338. int64(3),
  339. "four",
  340. },
  341. },
  342. },
  343. }
  344. if !testutil.Equal(got, want) {
  345. t.Errorf("converting repeated records containing record : got:\n%v\nwant:\n%v", got, want)
  346. }
  347. }
  348. func TestConvertRowErrors(t *testing.T) {
  349. // mismatched lengths
  350. if _, err := convertRow(&bq.TableRow{F: []*bq.TableCell{{V: ""}}}, Schema{}); err == nil {
  351. t.Error("got nil, want error")
  352. }
  353. v3 := map[string]interface{}{"v": 3}
  354. for _, test := range []struct {
  355. value interface{}
  356. fs FieldSchema
  357. }{
  358. {3, FieldSchema{Type: IntegerFieldType}}, // not a string
  359. {[]interface{}{v3}, // not a string, repeated
  360. FieldSchema{Type: IntegerFieldType, Repeated: true}},
  361. {map[string]interface{}{"f": []interface{}{v3}}, // not a string, nested
  362. FieldSchema{Type: RecordFieldType, Schema: Schema{{Type: IntegerFieldType}}}},
  363. {map[string]interface{}{"f": []interface{}{v3}}, // wrong length, nested
  364. FieldSchema{Type: RecordFieldType, Schema: Schema{}}},
  365. } {
  366. _, err := convertRow(
  367. &bq.TableRow{F: []*bq.TableCell{{V: test.value}}},
  368. Schema{&test.fs})
  369. if err == nil {
  370. t.Errorf("value %v, fs %v: got nil, want error", test.value, test.fs)
  371. }
  372. }
  373. // bad field type
  374. if _, err := convertBasicType("", FieldType("BAD")); err == nil {
  375. t.Error("got nil, want error")
  376. }
  377. }
  378. func TestValuesSaverConvertsToMap(t *testing.T) {
  379. testCases := []struct {
  380. vs ValuesSaver
  381. wantInsertID string
  382. wantRow map[string]Value
  383. }{
  384. {
  385. vs: ValuesSaver{
  386. Schema: Schema{
  387. {Name: "intField", Type: IntegerFieldType},
  388. {Name: "strField", Type: StringFieldType},
  389. {Name: "dtField", Type: DateTimeFieldType},
  390. {Name: "nField", Type: NumericFieldType},
  391. {Name: "geoField", Type: GeographyFieldType},
  392. },
  393. InsertID: "iid",
  394. Row: []Value{1, "a",
  395. civil.DateTime{
  396. Date: civil.Date{Year: 1, Month: 2, Day: 3},
  397. Time: civil.Time{Hour: 4, Minute: 5, Second: 6, Nanosecond: 7000}},
  398. big.NewRat(123456789000, 1e9),
  399. testGeography,
  400. },
  401. },
  402. wantInsertID: "iid",
  403. wantRow: map[string]Value{
  404. "intField": 1,
  405. "strField": "a",
  406. "dtField": "0001-02-03 04:05:06.000007",
  407. "nField": "123.456789000",
  408. "geoField": testGeography,
  409. },
  410. },
  411. {
  412. vs: ValuesSaver{
  413. Schema: Schema{
  414. {Name: "intField", Type: IntegerFieldType},
  415. {
  416. Name: "recordField",
  417. Type: RecordFieldType,
  418. Schema: Schema{
  419. {Name: "nestedInt", Type: IntegerFieldType, Repeated: true},
  420. },
  421. },
  422. },
  423. InsertID: "iid",
  424. Row: []Value{1, []Value{[]Value{2, 3}}},
  425. },
  426. wantInsertID: "iid",
  427. wantRow: map[string]Value{
  428. "intField": 1,
  429. "recordField": map[string]Value{
  430. "nestedInt": []Value{2, 3},
  431. },
  432. },
  433. },
  434. { // repeated nested field
  435. vs: ValuesSaver{
  436. Schema: Schema{
  437. {
  438. Name: "records",
  439. Type: RecordFieldType,
  440. Schema: Schema{
  441. {Name: "x", Type: IntegerFieldType},
  442. {Name: "y", Type: IntegerFieldType},
  443. },
  444. Repeated: true,
  445. },
  446. },
  447. InsertID: "iid",
  448. Row: []Value{ // a row is a []Value
  449. []Value{ // repeated field's value is a []Value
  450. []Value{1, 2}, // first record of the repeated field
  451. []Value{3, 4}, // second record
  452. },
  453. },
  454. },
  455. wantInsertID: "iid",
  456. wantRow: map[string]Value{
  457. "records": []Value{
  458. map[string]Value{"x": 1, "y": 2},
  459. map[string]Value{"x": 3, "y": 4},
  460. },
  461. },
  462. },
  463. }
  464. for _, tc := range testCases {
  465. gotRow, gotInsertID, err := tc.vs.Save()
  466. if err != nil {
  467. t.Errorf("Expected successful save; got: %v", err)
  468. continue
  469. }
  470. if !testutil.Equal(gotRow, tc.wantRow) {
  471. t.Errorf("%v row:\ngot:\n%+v\nwant:\n%+v", tc.vs, gotRow, tc.wantRow)
  472. }
  473. if !testutil.Equal(gotInsertID, tc.wantInsertID) {
  474. t.Errorf("%v ID:\ngot:\n%+v\nwant:\n%+v", tc.vs, gotInsertID, tc.wantInsertID)
  475. }
  476. }
  477. }
  478. func TestValuesToMapErrors(t *testing.T) {
  479. for _, test := range []struct {
  480. values []Value
  481. schema Schema
  482. }{
  483. { // mismatched length
  484. []Value{1},
  485. Schema{},
  486. },
  487. { // nested record not a slice
  488. []Value{1},
  489. Schema{{Type: RecordFieldType}},
  490. },
  491. { // nested record mismatched length
  492. []Value{[]Value{1}},
  493. Schema{{Type: RecordFieldType}},
  494. },
  495. { // nested repeated record not a slice
  496. []Value{[]Value{1}},
  497. Schema{{Type: RecordFieldType, Repeated: true}},
  498. },
  499. { // nested repeated record mismatched length
  500. []Value{[]Value{[]Value{1}}},
  501. Schema{{Type: RecordFieldType, Repeated: true}},
  502. },
  503. } {
  504. _, err := valuesToMap(test.values, test.schema)
  505. if err == nil {
  506. t.Errorf("%v, %v: got nil, want error", test.values, test.schema)
  507. }
  508. }
  509. }
  510. func TestStructSaver(t *testing.T) {
  511. schema := Schema{
  512. {Name: "s", Type: StringFieldType},
  513. {Name: "r", Type: IntegerFieldType, Repeated: true},
  514. {Name: "t", Type: TimeFieldType},
  515. {Name: "tr", Type: TimeFieldType, Repeated: true},
  516. {Name: "nested", Type: RecordFieldType, Schema: Schema{
  517. {Name: "b", Type: BooleanFieldType},
  518. }},
  519. {Name: "rnested", Type: RecordFieldType, Repeated: true, Schema: Schema{
  520. {Name: "b", Type: BooleanFieldType},
  521. }},
  522. {Name: "p", Type: IntegerFieldType, Required: false},
  523. {Name: "n", Type: NumericFieldType, Required: false},
  524. {Name: "nr", Type: NumericFieldType, Repeated: true},
  525. {Name: "g", Type: GeographyFieldType, Required: false},
  526. {Name: "gr", Type: GeographyFieldType, Repeated: true},
  527. }
  528. type (
  529. N struct{ B bool }
  530. T struct {
  531. S string
  532. R []int
  533. T civil.Time
  534. TR []civil.Time
  535. Nested *N
  536. Rnested []*N
  537. P NullInt64
  538. N *big.Rat
  539. NR []*big.Rat
  540. G NullGeography
  541. GR []string // Repeated Geography
  542. }
  543. )
  544. check := func(msg string, in interface{}, want map[string]Value) {
  545. ss := StructSaver{
  546. Schema: schema,
  547. InsertID: "iid",
  548. Struct: in,
  549. }
  550. got, gotIID, err := ss.Save()
  551. if err != nil {
  552. t.Fatalf("%s: %v", msg, err)
  553. }
  554. if wantIID := "iid"; gotIID != wantIID {
  555. t.Errorf("%s: InsertID: got %q, want %q", msg, gotIID, wantIID)
  556. }
  557. if diff := testutil.Diff(got, want); diff != "" {
  558. t.Errorf("%s: %s", msg, diff)
  559. }
  560. }
  561. ct1 := civil.Time{Hour: 1, Minute: 2, Second: 3, Nanosecond: 4000}
  562. ct2 := civil.Time{Hour: 5, Minute: 6, Second: 7, Nanosecond: 8000}
  563. in := T{
  564. S: "x",
  565. R: []int{1, 2},
  566. T: ct1,
  567. TR: []civil.Time{ct1, ct2},
  568. Nested: &N{B: true},
  569. Rnested: []*N{{true}, {false}},
  570. P: NullInt64{Valid: true, Int64: 17},
  571. N: big.NewRat(123456, 1000),
  572. NR: []*big.Rat{big.NewRat(3, 1), big.NewRat(56789, 1e5)},
  573. G: NullGeography{Valid: true, GeographyVal: "POINT(-122.350220 47.649154)"},
  574. GR: []string{"POINT(-122.350220 47.649154)", "POINT(-122.198939 47.669865)"},
  575. }
  576. want := map[string]Value{
  577. "s": "x",
  578. "r": []int{1, 2},
  579. "t": "01:02:03.000004",
  580. "tr": []string{"01:02:03.000004", "05:06:07.000008"},
  581. "nested": map[string]Value{"b": true},
  582. "rnested": []Value{map[string]Value{"b": true}, map[string]Value{"b": false}},
  583. "p": NullInt64{Valid: true, Int64: 17},
  584. "n": "123.456000000",
  585. "nr": []string{"3.000000000", "0.567890000"},
  586. "g": NullGeography{Valid: true, GeographyVal: "POINT(-122.350220 47.649154)"},
  587. "gr": []string{"POINT(-122.350220 47.649154)", "POINT(-122.198939 47.669865)"},
  588. }
  589. check("all values", in, want)
  590. check("all values, ptr", &in, want)
  591. check("empty struct", T{}, map[string]Value{"s": "", "t": "00:00:00", "p": NullInt64{}, "g": NullGeography{}})
  592. // Missing and extra fields ignored.
  593. type T2 struct {
  594. S string
  595. // missing R, Nested, RNested
  596. Extra int
  597. }
  598. check("missing and extra", T2{S: "x"}, map[string]Value{"s": "x"})
  599. check("nils in slice", T{Rnested: []*N{{true}, nil, {false}}},
  600. map[string]Value{
  601. "s": "",
  602. "t": "00:00:00",
  603. "p": NullInt64{},
  604. "g": NullGeography{},
  605. "rnested": []Value{map[string]Value{"b": true}, map[string]Value(nil), map[string]Value{"b": false}},
  606. })
  607. check("zero-length repeated", T{Rnested: []*N{}},
  608. map[string]Value{
  609. "rnested": []Value{},
  610. "s": "",
  611. "t": "00:00:00",
  612. "p": NullInt64{},
  613. "g": NullGeography{},
  614. })
  615. }
  616. func TestStructSaverErrors(t *testing.T) {
  617. type (
  618. badField struct {
  619. I int `bigquery:"@"`
  620. }
  621. badR struct{ R int }
  622. badRN struct{ R []int }
  623. )
  624. for i, test := range []struct {
  625. inputStruct interface{}
  626. schema Schema
  627. }{
  628. {0, nil}, // not a struct
  629. {&badField{}, nil}, // bad field name
  630. {&badR{}, Schema{{Name: "r", Repeated: true}}}, // repeated field has bad type
  631. {&badR{}, Schema{{Name: "r", Type: RecordFieldType}}}, // nested field has bad type
  632. {&badRN{[]int{0}}, // nested repeated field has bad type
  633. Schema{{Name: "r", Type: RecordFieldType, Repeated: true}}},
  634. } {
  635. ss := &StructSaver{Struct: test.inputStruct, Schema: test.schema}
  636. _, _, err := ss.Save()
  637. if err == nil {
  638. t.Errorf("#%d, %v, %v: got nil, want error", i, test.inputStruct, test.schema)
  639. }
  640. }
  641. }
  642. func TestNumericString(t *testing.T) {
  643. for _, test := range []struct {
  644. in *big.Rat
  645. want string
  646. }{
  647. {big.NewRat(2, 3), "0.666666667"}, // round to 9 places
  648. {big.NewRat(1, 2), "0.500000000"},
  649. {big.NewRat(1, 2*1e8), "0.000000005"},
  650. {big.NewRat(5, 1e10), "0.000000001"}, // round up the 5 in the 10th decimal place
  651. {big.NewRat(-5, 1e10), "-0.000000001"}, // round half away from zero
  652. } {
  653. got := NumericString(test.in)
  654. if got != test.want {
  655. t.Errorf("%v: got %q, want %q", test.in, got, test.want)
  656. }
  657. }
  658. }
  659. func TestConvertRows(t *testing.T) {
  660. schema := Schema{
  661. {Type: StringFieldType},
  662. {Type: IntegerFieldType},
  663. {Type: FloatFieldType},
  664. {Type: BooleanFieldType},
  665. {Type: GeographyFieldType},
  666. }
  667. rows := []*bq.TableRow{
  668. {F: []*bq.TableCell{
  669. {V: "a"},
  670. {V: "1"},
  671. {V: "1.2"},
  672. {V: "true"},
  673. {V: "POINT(-122.350220 47.649154)"},
  674. }},
  675. {F: []*bq.TableCell{
  676. {V: "b"},
  677. {V: "2"},
  678. {V: "2.2"},
  679. {V: "false"},
  680. {V: "POINT(-122.198939 47.669865)"},
  681. }},
  682. }
  683. want := [][]Value{
  684. {"a", int64(1), 1.2, true, "POINT(-122.350220 47.649154)"},
  685. {"b", int64(2), 2.2, false, "POINT(-122.198939 47.669865)"},
  686. }
  687. got, err := convertRows(rows, schema)
  688. if err != nil {
  689. t.Fatalf("got %v, want nil", err)
  690. }
  691. if !testutil.Equal(got, want) {
  692. t.Errorf("\ngot %v\nwant %v", got, want)
  693. }
  694. rows[0].F[0].V = 1
  695. _, err = convertRows(rows, schema)
  696. if err == nil {
  697. t.Error("got nil, want error")
  698. }
  699. }
  700. func TestValueList(t *testing.T) {
  701. schema := Schema{
  702. {Name: "s", Type: StringFieldType},
  703. {Name: "i", Type: IntegerFieldType},
  704. {Name: "f", Type: FloatFieldType},
  705. {Name: "b", Type: BooleanFieldType},
  706. }
  707. want := []Value{"x", 7, 3.14, true}
  708. var got []Value
  709. vl := (*valueList)(&got)
  710. if err := vl.Load(want, schema); err != nil {
  711. t.Fatal(err)
  712. }
  713. if !testutil.Equal(got, want) {
  714. t.Errorf("got %+v, want %+v", got, want)
  715. }
  716. // Load truncates, not appends.
  717. // https://github.com/googleapis/google-cloud-go/issues/437
  718. if err := vl.Load(want, schema); err != nil {
  719. t.Fatal(err)
  720. }
  721. if !testutil.Equal(got, want) {
  722. t.Errorf("got %+v, want %+v", got, want)
  723. }
  724. }
  725. func TestValueMap(t *testing.T) {
  726. ns := Schema{
  727. {Name: "x", Type: IntegerFieldType},
  728. {Name: "y", Type: IntegerFieldType},
  729. }
  730. schema := Schema{
  731. {Name: "s", Type: StringFieldType},
  732. {Name: "i", Type: IntegerFieldType},
  733. {Name: "f", Type: FloatFieldType},
  734. {Name: "b", Type: BooleanFieldType},
  735. {Name: "n", Type: RecordFieldType, Schema: ns},
  736. {Name: "rn", Type: RecordFieldType, Schema: ns, Repeated: true},
  737. }
  738. in := []Value{"x", 7, 3.14, true,
  739. []Value{1, 2},
  740. []Value{[]Value{3, 4}, []Value{5, 6}},
  741. }
  742. var vm valueMap
  743. if err := vm.Load(in, schema); err != nil {
  744. t.Fatal(err)
  745. }
  746. want := map[string]Value{
  747. "s": "x",
  748. "i": 7,
  749. "f": 3.14,
  750. "b": true,
  751. "n": map[string]Value{"x": 1, "y": 2},
  752. "rn": []Value{
  753. map[string]Value{"x": 3, "y": 4},
  754. map[string]Value{"x": 5, "y": 6},
  755. },
  756. }
  757. if !testutil.Equal(vm, valueMap(want)) {
  758. t.Errorf("got\n%+v\nwant\n%+v", vm, want)
  759. }
  760. in = make([]Value, len(schema))
  761. want = map[string]Value{
  762. "s": nil,
  763. "i": nil,
  764. "f": nil,
  765. "b": nil,
  766. "n": nil,
  767. "rn": nil,
  768. }
  769. var vm2 valueMap
  770. if err := vm2.Load(in, schema); err != nil {
  771. t.Fatal(err)
  772. }
  773. if !testutil.Equal(vm2, valueMap(want)) {
  774. t.Errorf("got\n%+v\nwant\n%+v", vm2, want)
  775. }
  776. }
  777. var (
  778. // For testing StructLoader
  779. schema2 = Schema{
  780. {Name: "s", Type: StringFieldType},
  781. {Name: "s2", Type: StringFieldType},
  782. {Name: "by", Type: BytesFieldType},
  783. {Name: "I", Type: IntegerFieldType},
  784. {Name: "U", Type: IntegerFieldType},
  785. {Name: "F", Type: FloatFieldType},
  786. {Name: "B", Type: BooleanFieldType},
  787. {Name: "TS", Type: TimestampFieldType},
  788. {Name: "D", Type: DateFieldType},
  789. {Name: "T", Type: TimeFieldType},
  790. {Name: "DT", Type: DateTimeFieldType},
  791. {Name: "N", Type: NumericFieldType},
  792. {Name: "G", Type: GeographyFieldType},
  793. {Name: "nested", Type: RecordFieldType, Schema: Schema{
  794. {Name: "nestS", Type: StringFieldType},
  795. {Name: "nestI", Type: IntegerFieldType},
  796. }},
  797. {Name: "t", Type: StringFieldType},
  798. }
  799. testTimestamp = time.Date(2016, 11, 5, 7, 50, 22, 8, time.UTC)
  800. testDate = civil.Date{Year: 2016, Month: 11, Day: 5}
  801. testTime = civil.Time{Hour: 7, Minute: 50, Second: 22, Nanosecond: 8}
  802. testDateTime = civil.DateTime{Date: testDate, Time: testTime}
  803. testNumeric = big.NewRat(123, 456)
  804. // testGeography is a WKT string representing a single point.
  805. testGeography = "POINT(-122.350220 47.649154)"
  806. testValues = []Value{"x", "y", []byte{1, 2, 3}, int64(7), int64(8), 3.14, true,
  807. testTimestamp, testDate, testTime, testDateTime, testNumeric, testGeography,
  808. []Value{"nested", int64(17)}, "z"}
  809. )
  810. type testStruct1 struct {
  811. B bool
  812. I int
  813. U uint16
  814. times
  815. S string
  816. S2 String
  817. By []byte
  818. F float64
  819. N *big.Rat
  820. G string
  821. Nested nested
  822. Tagged string `bigquery:"t"`
  823. }
  824. type String string
  825. type nested struct {
  826. NestS string
  827. NestI int
  828. }
  829. type times struct {
  830. TS time.Time
  831. T civil.Time
  832. D civil.Date
  833. DT civil.DateTime
  834. }
  835. func TestStructLoader(t *testing.T) {
  836. var ts1 testStruct1
  837. mustLoad(t, &ts1, schema2, testValues)
  838. // Note: the schema field named "s" gets matched to the exported struct
  839. // field "S", not the unexported "s".
  840. want := &testStruct1{
  841. B: true,
  842. I: 7,
  843. U: 8,
  844. F: 3.14,
  845. times: times{TS: testTimestamp, T: testTime, D: testDate, DT: testDateTime},
  846. S: "x",
  847. S2: "y",
  848. By: []byte{1, 2, 3},
  849. N: big.NewRat(123, 456),
  850. G: testGeography,
  851. Nested: nested{NestS: "nested", NestI: 17},
  852. Tagged: "z",
  853. }
  854. if diff := testutil.Diff(&ts1, want, cmp.AllowUnexported(testStruct1{})); diff != "" {
  855. t.Error(diff)
  856. }
  857. // Test pointers to nested structs.
  858. type nestedPtr struct{ Nested *nested }
  859. var np nestedPtr
  860. mustLoad(t, &np, schema2, testValues)
  861. want2 := &nestedPtr{Nested: &nested{NestS: "nested", NestI: 17}}
  862. if diff := testutil.Diff(&np, want2); diff != "" {
  863. t.Error(diff)
  864. }
  865. // Existing values should be reused.
  866. nst := &nested{NestS: "x", NestI: -10}
  867. np = nestedPtr{Nested: nst}
  868. mustLoad(t, &np, schema2, testValues)
  869. if diff := testutil.Diff(&np, want2); diff != "" {
  870. t.Error(diff)
  871. }
  872. if np.Nested != nst {
  873. t.Error("nested struct pointers not equal")
  874. }
  875. }
  876. type repStruct struct {
  877. Nums []int
  878. ShortNums [2]int // to test truncation
  879. LongNums [5]int // to test padding with zeroes
  880. Nested []*nested
  881. }
  882. var (
  883. repSchema = Schema{
  884. {Name: "nums", Type: IntegerFieldType, Repeated: true},
  885. {Name: "shortNums", Type: IntegerFieldType, Repeated: true},
  886. {Name: "longNums", Type: IntegerFieldType, Repeated: true},
  887. {Name: "nested", Type: RecordFieldType, Repeated: true, Schema: Schema{
  888. {Name: "nestS", Type: StringFieldType},
  889. {Name: "nestI", Type: IntegerFieldType},
  890. }},
  891. }
  892. v123 = []Value{int64(1), int64(2), int64(3)}
  893. repValues = []Value{v123, v123, v123,
  894. []Value{
  895. []Value{"x", int64(1)},
  896. []Value{"y", int64(2)},
  897. },
  898. }
  899. )
  900. func TestStructLoaderRepeated(t *testing.T) {
  901. var r1 repStruct
  902. mustLoad(t, &r1, repSchema, repValues)
  903. want := repStruct{
  904. Nums: []int{1, 2, 3},
  905. ShortNums: [...]int{1, 2}, // extra values discarded
  906. LongNums: [...]int{1, 2, 3, 0, 0},
  907. Nested: []*nested{{"x", 1}, {"y", 2}},
  908. }
  909. if diff := testutil.Diff(r1, want); diff != "" {
  910. t.Error(diff)
  911. }
  912. r2 := repStruct{
  913. Nums: []int{-1, -2, -3, -4, -5}, // truncated to zero and appended to
  914. LongNums: [...]int{-1, -2, -3, -4, -5}, // unset elements are zeroed
  915. }
  916. mustLoad(t, &r2, repSchema, repValues)
  917. if diff := testutil.Diff(r2, want); diff != "" {
  918. t.Error(diff)
  919. }
  920. if got, want := cap(r2.Nums), 5; got != want {
  921. t.Errorf("cap(r2.Nums) = %d, want %d", got, want)
  922. }
  923. // Short slice case.
  924. r3 := repStruct{Nums: []int{-1}}
  925. mustLoad(t, &r3, repSchema, repValues)
  926. if diff := testutil.Diff(r3, want); diff != "" {
  927. t.Error(diff)
  928. }
  929. if got, want := cap(r3.Nums), 3; got != want {
  930. t.Errorf("cap(r3.Nums) = %d, want %d", got, want)
  931. }
  932. }
  933. type testStructNullable struct {
  934. String NullString
  935. Bytes []byte
  936. Integer NullInt64
  937. Float NullFloat64
  938. Boolean NullBool
  939. Timestamp NullTimestamp
  940. Date NullDate
  941. Time NullTime
  942. DateTime NullDateTime
  943. Numeric *big.Rat
  944. Geography NullGeography
  945. Record *subNullable
  946. }
  947. type subNullable struct {
  948. X NullInt64
  949. }
  950. var testStructNullableSchema = Schema{
  951. {Name: "String", Type: StringFieldType, Required: false},
  952. {Name: "Bytes", Type: BytesFieldType, Required: false},
  953. {Name: "Integer", Type: IntegerFieldType, Required: false},
  954. {Name: "Float", Type: FloatFieldType, Required: false},
  955. {Name: "Boolean", Type: BooleanFieldType, Required: false},
  956. {Name: "Timestamp", Type: TimestampFieldType, Required: false},
  957. {Name: "Date", Type: DateFieldType, Required: false},
  958. {Name: "Time", Type: TimeFieldType, Required: false},
  959. {Name: "DateTime", Type: DateTimeFieldType, Required: false},
  960. {Name: "Numeric", Type: NumericFieldType, Required: false},
  961. {Name: "Geography", Type: GeographyFieldType, Required: false},
  962. {Name: "Record", Type: RecordFieldType, Required: false, Schema: Schema{
  963. {Name: "X", Type: IntegerFieldType, Required: false},
  964. }},
  965. }
  966. func TestStructLoaderNullable(t *testing.T) {
  967. var ts testStructNullable
  968. nilVals := make([]Value, len(testStructNullableSchema))
  969. mustLoad(t, &ts, testStructNullableSchema, nilVals)
  970. want := testStructNullable{}
  971. if diff := testutil.Diff(ts, want); diff != "" {
  972. t.Error(diff)
  973. }
  974. nonnilVals := []Value{"x", []byte{1, 2, 3}, int64(1), 2.3, true, testTimestamp, testDate, testTime,
  975. testDateTime, big.NewRat(1, 2), testGeography, []Value{int64(4)}}
  976. // All ts fields are nil. Loading non-nil values will cause them all to
  977. // be allocated.
  978. mustLoad(t, &ts, testStructNullableSchema, nonnilVals)
  979. want = testStructNullable{
  980. String: NullString{StringVal: "x", Valid: true},
  981. Bytes: []byte{1, 2, 3},
  982. Integer: NullInt64{Int64: 1, Valid: true},
  983. Float: NullFloat64{Float64: 2.3, Valid: true},
  984. Boolean: NullBool{Bool: true, Valid: true},
  985. Timestamp: NullTimestamp{Timestamp: testTimestamp, Valid: true},
  986. Date: NullDate{Date: testDate, Valid: true},
  987. Time: NullTime{Time: testTime, Valid: true},
  988. DateTime: NullDateTime{DateTime: testDateTime, Valid: true},
  989. Numeric: big.NewRat(1, 2),
  990. Geography: NullGeography{GeographyVal: testGeography, Valid: true},
  991. Record: &subNullable{X: NullInt64{Int64: 4, Valid: true}},
  992. }
  993. if diff := testutil.Diff(ts, want); diff != "" {
  994. t.Error(diff)
  995. }
  996. // Struct pointers are reused, byte slices are not.
  997. want = ts
  998. want.Bytes = []byte{17}
  999. vals2 := []Value{nil, []byte{17}, nil, nil, nil, nil, nil, nil, nil, nil, nil, []Value{int64(7)}}
  1000. mustLoad(t, &ts, testStructNullableSchema, vals2)
  1001. if ts.Record != want.Record {
  1002. t.Error("record pointers not identical")
  1003. }
  1004. }
  1005. func TestStructLoaderOverflow(t *testing.T) {
  1006. type S struct {
  1007. I int16
  1008. U uint16
  1009. F float32
  1010. }
  1011. schema := Schema{
  1012. {Name: "I", Type: IntegerFieldType},
  1013. {Name: "U", Type: IntegerFieldType},
  1014. {Name: "F", Type: FloatFieldType},
  1015. }
  1016. var s S
  1017. z64 := int64(0)
  1018. for _, vals := range [][]Value{
  1019. {int64(math.MaxInt16 + 1), z64, 0},
  1020. {z64, int64(math.MaxInt32), 0},
  1021. {z64, int64(-1), 0},
  1022. {z64, z64, math.MaxFloat32 * 2},
  1023. } {
  1024. if err := load(&s, schema, vals); err == nil {
  1025. t.Errorf("%+v: got nil, want error", vals)
  1026. }
  1027. }
  1028. }
  1029. func TestStructLoaderFieldOverlap(t *testing.T) {
  1030. // It's OK if the struct has fields that the schema does not, and vice versa.
  1031. type S1 struct {
  1032. I int
  1033. X [][]int // not in the schema; does not even correspond to a valid BigQuery type
  1034. // many schema fields missing
  1035. }
  1036. var s1 S1
  1037. if err := load(&s1, schema2, testValues); err != nil {
  1038. t.Fatal(err)
  1039. }
  1040. want1 := S1{I: 7}
  1041. if diff := testutil.Diff(s1, want1); diff != "" {
  1042. t.Error(diff)
  1043. }
  1044. // It's even valid to have no overlapping fields at all.
  1045. type S2 struct{ Z int }
  1046. var s2 S2
  1047. mustLoad(t, &s2, schema2, testValues)
  1048. want2 := S2{}
  1049. if diff := testutil.Diff(s2, want2); diff != "" {
  1050. t.Error(diff)
  1051. }
  1052. }
  1053. func TestStructLoaderErrors(t *testing.T) {
  1054. check := func(sp interface{}) {
  1055. var sl structLoader
  1056. err := sl.set(sp, schema2)
  1057. if err == nil {
  1058. t.Errorf("%T: got nil, want error", sp)
  1059. }
  1060. }
  1061. type bad1 struct{ F int32 } // wrong type for FLOAT column
  1062. check(&bad1{})
  1063. type bad2 struct{ I uint } // unsupported integer type
  1064. check(&bad2{})
  1065. type bad3 struct {
  1066. I int `bigquery:"@"`
  1067. } // bad field name
  1068. check(&bad3{})
  1069. type bad4 struct{ Nested int } // non-struct for nested field
  1070. check(&bad4{})
  1071. type bad5 struct{ Nested struct{ NestS int } } // bad nested struct
  1072. check(&bad5{})
  1073. bad6 := &struct{ Nums int }{} // non-slice for repeated field
  1074. sl := structLoader{}
  1075. err := sl.set(bad6, repSchema)
  1076. if err == nil {
  1077. t.Errorf("%T: got nil, want error", bad6)
  1078. }
  1079. // sl.set's error is sticky, even with good input.
  1080. err2 := sl.set(&repStruct{}, repSchema)
  1081. if err2 != err {
  1082. t.Errorf("%v != %v, expected equal", err2, err)
  1083. }
  1084. // sl.Load is similarly sticky
  1085. err2 = sl.Load(nil, nil)
  1086. if err2 != err {
  1087. t.Errorf("%v != %v, expected equal", err2, err)
  1088. }
  1089. // Null values.
  1090. schema := Schema{
  1091. {Name: "i", Type: IntegerFieldType},
  1092. {Name: "f", Type: FloatFieldType},
  1093. {Name: "b", Type: BooleanFieldType},
  1094. {Name: "s", Type: StringFieldType},
  1095. {Name: "d", Type: DateFieldType},
  1096. {Name: "r", Type: RecordFieldType, Schema: Schema{{Name: "X", Type: IntegerFieldType}}},
  1097. }
  1098. type s struct {
  1099. I int
  1100. F float64
  1101. B bool
  1102. S string
  1103. D civil.Date
  1104. }
  1105. vals := []Value{int64(0), 0.0, false, "", testDate}
  1106. mustLoad(t, &s{}, schema, vals)
  1107. for i, e := range vals {
  1108. vals[i] = nil
  1109. got := load(&s{}, schema, vals)
  1110. if got != errNoNulls {
  1111. t.Errorf("#%d: got %v, want %v", i, got, errNoNulls)
  1112. }
  1113. vals[i] = e
  1114. }
  1115. // Using more than one struct type with the same structLoader.
  1116. type different struct {
  1117. B bool
  1118. I int
  1119. times
  1120. S string
  1121. Nums []int
  1122. }
  1123. sl = structLoader{}
  1124. if err := sl.set(&testStruct1{}, schema2); err != nil {
  1125. t.Fatal(err)
  1126. }
  1127. err = sl.set(&different{}, schema2)
  1128. if err == nil {
  1129. t.Error("different struct types: got nil, want error")
  1130. }
  1131. }
  1132. func mustLoad(t *testing.T, pval interface{}, schema Schema, vals []Value) {
  1133. if err := load(pval, schema, vals); err != nil {
  1134. t.Fatalf("loading: %v", err)
  1135. }
  1136. }
  1137. func load(pval interface{}, schema Schema, vals []Value) error {
  1138. var sl structLoader
  1139. if err := sl.set(pval, schema); err != nil {
  1140. return err
  1141. }
  1142. return sl.Load(vals, nil)
  1143. }
  1144. func BenchmarkStructLoader_NoCompile(b *testing.B) {
  1145. benchmarkStructLoader(b, false)
  1146. }
  1147. func BenchmarkStructLoader_Compile(b *testing.B) {
  1148. benchmarkStructLoader(b, true)
  1149. }
  1150. func benchmarkStructLoader(b *testing.B, compile bool) {
  1151. var ts1 testStruct1
  1152. for i := 0; i < b.N; i++ {
  1153. var sl structLoader
  1154. for j := 0; j < 10; j++ {
  1155. if err := load(&ts1, schema2, testValues); err != nil {
  1156. b.Fatal(err)
  1157. }
  1158. if !compile {
  1159. sl.typ = nil
  1160. }
  1161. }
  1162. }
  1163. }