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 15package bigquery 16 17import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "reflect" 22 "strconv" 23 "time" 24 25 "cloud.google.com/go/civil" 26) 27 28// NullInt64 represents a BigQuery INT64 that may be NULL. 29type NullInt64 struct { 30 Int64 int64 31 Valid bool // Valid is true if Int64 is not NULL. 32} 33 34func (n NullInt64) String() string { return nullstr(n.Valid, n.Int64) } 35 36// NullString represents a BigQuery STRING that may be NULL. 37type NullString struct { 38 StringVal string 39 Valid bool // Valid is true if StringVal is not NULL. 40} 41 42func (n NullString) String() string { return nullstr(n.Valid, n.StringVal) } 43 44// NullGeography represents a BigQuery GEOGRAPHY string that may be NULL. 45type NullGeography struct { 46 GeographyVal string 47 Valid bool // Valid is true if GeographyVal is not NULL. 48} 49 50func (n NullGeography) String() string { return nullstr(n.Valid, n.GeographyVal) } 51 52// NullFloat64 represents a BigQuery FLOAT64 that may be NULL. 53type NullFloat64 struct { 54 Float64 float64 55 Valid bool // Valid is true if Float64 is not NULL. 56} 57 58func (n NullFloat64) String() string { return nullstr(n.Valid, n.Float64) } 59 60// NullBool represents a BigQuery BOOL that may be NULL. 61type NullBool struct { 62 Bool bool 63 Valid bool // Valid is true if Bool is not NULL. 64} 65 66func (n NullBool) String() string { return nullstr(n.Valid, n.Bool) } 67 68// NullTimestamp represents a BigQuery TIMESTAMP that may be null. 69type NullTimestamp struct { 70 Timestamp time.Time 71 Valid bool // Valid is true if Time is not NULL. 72} 73 74func (n NullTimestamp) String() string { return nullstr(n.Valid, n.Timestamp) } 75 76// NullDate represents a BigQuery DATE that may be null. 77type NullDate struct { 78 Date civil.Date 79 Valid bool // Valid is true if Date is not NULL. 80} 81 82func (n NullDate) String() string { return nullstr(n.Valid, n.Date) } 83 84// NullTime represents a BigQuery TIME that may be null. 85type NullTime struct { 86 Time civil.Time 87 Valid bool // Valid is true if Time is not NULL. 88} 89 90func (n NullTime) String() string { 91 if !n.Valid { 92 return "<null>" 93 } 94 return CivilTimeString(n.Time) 95} 96 97// NullDateTime represents a BigQuery DATETIME that may be null. 98type NullDateTime struct { 99 DateTime civil.DateTime 100 Valid bool // Valid is true if DateTime is not NULL. 101} 102 103func (n NullDateTime) String() string { 104 if !n.Valid { 105 return "<null>" 106 } 107 return CivilDateTimeString(n.DateTime) 108} 109 110// MarshalJSON converts the NullInt64 to JSON. 111func (n NullInt64) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Int64) } 112 113// MarshalJSON converts the NullFloat64 to JSON. 114func (n NullFloat64) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Float64) } 115 116// MarshalJSON converts the NullBool to JSON. 117func (n NullBool) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Bool) } 118 119// MarshalJSON converts the NullString to JSON. 120func (n NullString) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.StringVal) } 121 122// MarshalJSON converts the NullGeography to JSON. 123func (n NullGeography) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.GeographyVal) } 124 125// MarshalJSON converts the NullTimestamp to JSON. 126func (n NullTimestamp) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Timestamp) } 127 128// MarshalJSON converts the NullDate to JSON. 129func (n NullDate) MarshalJSON() ([]byte, error) { return nulljson(n.Valid, n.Date) } 130 131// MarshalJSON converts the NullTime to JSON. 132func (n NullTime) MarshalJSON() ([]byte, error) { 133 if !n.Valid { 134 return jsonNull, nil 135 } 136 return []byte(`"` + CivilTimeString(n.Time) + `"`), nil 137} 138 139// MarshalJSON converts the NullDateTime to JSON. 140func (n NullDateTime) MarshalJSON() ([]byte, error) { 141 if !n.Valid { 142 return jsonNull, nil 143 } 144 return []byte(`"` + CivilDateTimeString(n.DateTime) + `"`), nil 145} 146 147func nullstr(valid bool, v interface{}) string { 148 if !valid { 149 return "NULL" 150 } 151 return fmt.Sprint(v) 152} 153 154var jsonNull = []byte("null") 155 156func nulljson(valid bool, v interface{}) ([]byte, error) { 157 if !valid { 158 return jsonNull, nil 159 } 160 return json.Marshal(v) 161} 162 163// UnmarshalJSON converts JSON into a NullInt64. 164func (n *NullInt64) UnmarshalJSON(b []byte) error { 165 n.Valid = false 166 n.Int64 = 0 167 if bytes.Equal(b, jsonNull) { 168 return nil 169 } 170 171 if err := json.Unmarshal(b, &n.Int64); err != nil { 172 return err 173 } 174 n.Valid = true 175 return nil 176} 177 178// UnmarshalJSON converts JSON into a NullFloat64. 179func (n *NullFloat64) UnmarshalJSON(b []byte) error { 180 n.Valid = false 181 n.Float64 = 0 182 if bytes.Equal(b, jsonNull) { 183 return nil 184 } 185 186 if err := json.Unmarshal(b, &n.Float64); err != nil { 187 return err 188 } 189 n.Valid = true 190 return nil 191} 192 193// UnmarshalJSON converts JSON into a NullBool. 194func (n *NullBool) UnmarshalJSON(b []byte) error { 195 n.Valid = false 196 n.Bool = false 197 if bytes.Equal(b, jsonNull) { 198 return nil 199 } 200 201 if err := json.Unmarshal(b, &n.Bool); err != nil { 202 return err 203 } 204 n.Valid = true 205 return nil 206} 207 208// UnmarshalJSON converts JSON into a NullString. 209func (n *NullString) UnmarshalJSON(b []byte) error { 210 n.Valid = false 211 n.StringVal = "" 212 if bytes.Equal(b, jsonNull) { 213 return nil 214 } 215 216 if err := json.Unmarshal(b, &n.StringVal); err != nil { 217 return err 218 } 219 n.Valid = true 220 return nil 221} 222 223// UnmarshalJSON converts JSON into a NullGeography. 224func (n *NullGeography) UnmarshalJSON(b []byte) error { 225 n.Valid = false 226 n.GeographyVal = "" 227 if bytes.Equal(b, jsonNull) { 228 return nil 229 } 230 if err := json.Unmarshal(b, &n.GeographyVal); err != nil { 231 return err 232 } 233 n.Valid = true 234 return nil 235} 236 237// UnmarshalJSON converts JSON into a NullTimestamp. 238func (n *NullTimestamp) UnmarshalJSON(b []byte) error { 239 n.Valid = false 240 n.Timestamp = time.Time{} 241 if bytes.Equal(b, jsonNull) { 242 return nil 243 } 244 245 if err := json.Unmarshal(b, &n.Timestamp); err != nil { 246 return err 247 } 248 n.Valid = true 249 return nil 250} 251 252// UnmarshalJSON converts JSON into a NullDate. 253func (n *NullDate) UnmarshalJSON(b []byte) error { 254 n.Valid = false 255 n.Date = civil.Date{} 256 if bytes.Equal(b, jsonNull) { 257 return nil 258 } 259 260 if err := json.Unmarshal(b, &n.Date); err != nil { 261 return err 262 } 263 n.Valid = true 264 return nil 265} 266 267// UnmarshalJSON converts JSON into a NullTime. 268func (n *NullTime) UnmarshalJSON(b []byte) error { 269 n.Valid = false 270 n.Time = civil.Time{} 271 if bytes.Equal(b, jsonNull) { 272 return nil 273 } 274 275 s, err := strconv.Unquote(string(b)) 276 if err != nil { 277 return err 278 } 279 280 t, err := civil.ParseTime(s) 281 if err != nil { 282 return err 283 } 284 n.Time = t 285 286 n.Valid = true 287 return nil 288} 289 290// UnmarshalJSON converts JSON into a NullDateTime. 291func (n *NullDateTime) UnmarshalJSON(b []byte) error { 292 n.Valid = false 293 n.DateTime = civil.DateTime{} 294 if bytes.Equal(b, jsonNull) { 295 return nil 296 } 297 298 s, err := strconv.Unquote(string(b)) 299 if err != nil { 300 return err 301 } 302 303 dt, err := parseCivilDateTime(s) 304 if err != nil { 305 return err 306 } 307 n.DateTime = dt 308 309 n.Valid = true 310 return nil 311} 312 313var ( 314 typeOfNullInt64 = reflect.TypeOf(NullInt64{}) 315 typeOfNullFloat64 = reflect.TypeOf(NullFloat64{}) 316 typeOfNullBool = reflect.TypeOf(NullBool{}) 317 typeOfNullString = reflect.TypeOf(NullString{}) 318 typeOfNullGeography = reflect.TypeOf(NullGeography{}) 319 typeOfNullTimestamp = reflect.TypeOf(NullTimestamp{}) 320 typeOfNullDate = reflect.TypeOf(NullDate{}) 321 typeOfNullTime = reflect.TypeOf(NullTime{}) 322 typeOfNullDateTime = reflect.TypeOf(NullDateTime{}) 323) 324 325func nullableFieldType(t reflect.Type) FieldType { 326 switch t { 327 case typeOfNullInt64: 328 return IntegerFieldType 329 case typeOfNullFloat64: 330 return FloatFieldType 331 case typeOfNullBool: 332 return BooleanFieldType 333 case typeOfNullString: 334 return StringFieldType 335 case typeOfNullGeography: 336 return GeographyFieldType 337 case typeOfNullTimestamp: 338 return TimestampFieldType 339 case typeOfNullDate: 340 return DateFieldType 341 case typeOfNullTime: 342 return TimeFieldType 343 case typeOfNullDateTime: 344 return DateTimeFieldType 345 default: 346 return "" 347 } 348} 349