Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/cloud/bigquery/standard_sql.py: 33%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

183 statements  

1# Copyright 2021 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# https://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 

15import copy 

16import typing 

17from typing import Any, Dict, Iterable, List, Optional 

18 

19from google.cloud.bigquery.enums import StandardSqlTypeNames 

20 

21 

22class StandardSqlDataType: 

23 """The type of a variable, e.g., a function argument. 

24 

25 See: 

26 https://cloud.google.com/bigquery/docs/reference/rest/v2/StandardSqlDataType 

27 

28 Examples: 

29 

30 .. code-block:: text 

31 

32 INT64: {type_kind="INT64"} 

33 ARRAY: {type_kind="ARRAY", array_element_type="STRING"} 

34 STRUCT<x STRING, y ARRAY>: { 

35 type_kind="STRUCT", 

36 struct_type={ 

37 fields=[ 

38 {name="x", type={type_kind="STRING"}}, 

39 { 

40 name="y", 

41 type={type_kind="ARRAY", array_element_type="DATE"} 

42 } 

43 ] 

44 } 

45 } 

46 RANGE: {type_kind="RANGE", range_element_type="DATETIME"} 

47 

48 Args: 

49 type_kind: 

50 The top level type of this field. Can be any standard SQL data type, 

51 e.g. INT64, DATE, ARRAY. 

52 array_element_type: 

53 The type of the array's elements, if type_kind is ARRAY. 

54 struct_type: 

55 The fields of this struct, in order, if type_kind is STRUCT. 

56 range_element_type: 

57 The type of the range's elements, if type_kind is RANGE. 

58 """ 

59 

60 def __init__( 

61 self, 

62 type_kind: Optional[ 

63 StandardSqlTypeNames 

64 ] = StandardSqlTypeNames.TYPE_KIND_UNSPECIFIED, 

65 array_element_type: Optional["StandardSqlDataType"] = None, 

66 struct_type: Optional["StandardSqlStructType"] = None, 

67 range_element_type: Optional["StandardSqlDataType"] = None, 

68 ): 

69 self._properties: Dict[str, Any] = {} 

70 

71 self.type_kind = type_kind 

72 self.array_element_type = array_element_type 

73 self.struct_type = struct_type 

74 self.range_element_type = range_element_type 

75 

76 @property 

77 def type_kind(self) -> Optional[StandardSqlTypeNames]: 

78 """The top level type of this field. 

79 

80 Can be any standard SQL data type, e.g. INT64, DATE, ARRAY. 

81 """ 

82 kind = self._properties["typeKind"] 

83 return StandardSqlTypeNames[kind] # pytype: disable=missing-parameter 

84 

85 @type_kind.setter 

86 def type_kind(self, value: Optional[StandardSqlTypeNames]): 

87 if not value: 

88 kind = StandardSqlTypeNames.TYPE_KIND_UNSPECIFIED.value 

89 else: 

90 kind = value.value 

91 self._properties["typeKind"] = kind 

92 

93 @property 

94 def array_element_type(self) -> Optional["StandardSqlDataType"]: 

95 """The type of the array's elements, if type_kind is ARRAY.""" 

96 element_type = self._properties.get("arrayElementType") 

97 

98 if element_type is None: 

99 return None 

100 

101 result = StandardSqlDataType() 

102 result._properties = element_type # We do not use a copy on purpose. 

103 return result 

104 

105 @array_element_type.setter 

106 def array_element_type(self, value: Optional["StandardSqlDataType"]): 

107 element_type = None if value is None else value.to_api_repr() 

108 

109 if element_type is None: 

110 self._properties.pop("arrayElementType", None) 

111 else: 

112 self._properties["arrayElementType"] = element_type 

113 

114 @property 

115 def struct_type(self) -> Optional["StandardSqlStructType"]: 

116 """The fields of this struct, in order, if type_kind is STRUCT.""" 

117 struct_info = self._properties.get("structType") 

118 

119 if struct_info is None: 

120 return None 

121 

122 result = StandardSqlStructType() 

123 result._properties = struct_info # We do not use a copy on purpose. 

124 return result 

125 

126 @struct_type.setter 

127 def struct_type(self, value: Optional["StandardSqlStructType"]): 

128 struct_type = None if value is None else value.to_api_repr() 

129 

130 if struct_type is None: 

131 self._properties.pop("structType", None) 

132 else: 

133 self._properties["structType"] = struct_type 

134 

135 @property 

136 def range_element_type(self) -> Optional["StandardSqlDataType"]: 

137 """The type of the range's elements, if type_kind = "RANGE". Must be 

138 one of DATETIME, DATE, or TIMESTAMP.""" 

139 range_element_info = self._properties.get("rangeElementType") 

140 

141 if range_element_info is None: 

142 return None 

143 

144 result = StandardSqlDataType() 

145 result._properties = range_element_info # We do not use a copy on purpose. 

146 return result 

147 

148 @range_element_type.setter 

149 def range_element_type(self, value: Optional["StandardSqlDataType"]): 

150 range_element_type = None if value is None else value.to_api_repr() 

151 

152 if range_element_type is None: 

153 self._properties.pop("rangeElementType", None) 

154 else: 

155 self._properties["rangeElementType"] = range_element_type 

156 

157 def to_api_repr(self) -> Dict[str, Any]: 

158 """Construct the API resource representation of this SQL data type.""" 

159 return copy.deepcopy(self._properties) 

160 

161 @classmethod 

162 def from_api_repr(cls, resource: Dict[str, Any]): 

163 """Construct an SQL data type instance given its API representation.""" 

164 type_kind = resource.get("typeKind") 

165 if type_kind not in StandardSqlTypeNames.__members__: 

166 type_kind = StandardSqlTypeNames.TYPE_KIND_UNSPECIFIED 

167 else: 

168 # Convert string to an enum member. 

169 type_kind = StandardSqlTypeNames[ # pytype: disable=missing-parameter 

170 typing.cast(str, type_kind) 

171 ] 

172 

173 array_element_type = None 

174 if type_kind == StandardSqlTypeNames.ARRAY: 

175 element_type = resource.get("arrayElementType") 

176 if element_type: 

177 array_element_type = cls.from_api_repr(element_type) 

178 

179 struct_type = None 

180 if type_kind == StandardSqlTypeNames.STRUCT: 

181 struct_info = resource.get("structType") 

182 if struct_info: 

183 struct_type = StandardSqlStructType.from_api_repr(struct_info) 

184 

185 range_element_type = None 

186 if type_kind == StandardSqlTypeNames.RANGE: 

187 range_element_info = resource.get("rangeElementType") 

188 if range_element_info: 

189 range_element_type = cls.from_api_repr(range_element_info) 

190 

191 return cls(type_kind, array_element_type, struct_type, range_element_type) 

192 

193 def __eq__(self, other): 

194 if not isinstance(other, StandardSqlDataType): 

195 return NotImplemented 

196 else: 

197 return ( 

198 self.type_kind == other.type_kind 

199 and self.array_element_type == other.array_element_type 

200 and self.struct_type == other.struct_type 

201 and self.range_element_type == other.range_element_type 

202 ) 

203 

204 def __str__(self): 

205 result = f"{self.__class__.__name__}(type_kind={self.type_kind!r}, ...)" 

206 return result 

207 

208 

209class StandardSqlField: 

210 """A field or a column. 

211 

212 See: 

213 https://cloud.google.com/bigquery/docs/reference/rest/v2/StandardSqlField 

214 

215 Args: 

216 name: 

217 The name of this field. Can be absent for struct fields. 

218 type: 

219 The type of this parameter. Absent if not explicitly specified. 

220 

221 For example, CREATE FUNCTION statement can omit the return type; in this 

222 case the output parameter does not have this "type" field). 

223 """ 

224 

225 def __init__( 

226 self, name: Optional[str] = None, type: Optional[StandardSqlDataType] = None 

227 ): 

228 type_repr = None if type is None else type.to_api_repr() 

229 self._properties = {"name": name, "type": type_repr} 

230 

231 @property 

232 def name(self) -> Optional[str]: 

233 """The name of this field. Can be absent for struct fields.""" 

234 return typing.cast(Optional[str], self._properties["name"]) 

235 

236 @name.setter 

237 def name(self, value: Optional[str]): 

238 self._properties["name"] = value 

239 

240 @property 

241 def type(self) -> Optional[StandardSqlDataType]: 

242 """The type of this parameter. Absent if not explicitly specified. 

243 

244 For example, CREATE FUNCTION statement can omit the return type; in this 

245 case the output parameter does not have this "type" field). 

246 """ 

247 type_info = self._properties["type"] 

248 

249 if type_info is None: 

250 return None 

251 

252 result = StandardSqlDataType() 

253 # We do not use a properties copy on purpose. 

254 result._properties = typing.cast(Dict[str, Any], type_info) 

255 

256 return result 

257 

258 @type.setter 

259 def type(self, value: Optional[StandardSqlDataType]): 

260 value_repr = None if value is None else value.to_api_repr() 

261 self._properties["type"] = value_repr 

262 

263 def to_api_repr(self) -> Dict[str, Any]: 

264 """Construct the API resource representation of this SQL field.""" 

265 return copy.deepcopy(self._properties) 

266 

267 @classmethod 

268 def from_api_repr(cls, resource: Dict[str, Any]): 

269 """Construct an SQL field instance given its API representation.""" 

270 result = cls( 

271 name=resource.get("name"), 

272 type=StandardSqlDataType.from_api_repr(resource.get("type", {})), 

273 ) 

274 return result 

275 

276 def __eq__(self, other): 

277 if not isinstance(other, StandardSqlField): 

278 return NotImplemented 

279 else: 

280 return self.name == other.name and self.type == other.type 

281 

282 

283class StandardSqlStructType: 

284 """Type of a struct field. 

285 

286 See: 

287 https://cloud.google.com/bigquery/docs/reference/rest/v2/StandardSqlDataType#StandardSqlStructType 

288 

289 Args: 

290 fields: The fields in this struct. 

291 """ 

292 

293 def __init__(self, fields: Optional[Iterable[StandardSqlField]] = None): 

294 if fields is None: 

295 fields = [] 

296 self._properties = {"fields": [field.to_api_repr() for field in fields]} 

297 

298 @property 

299 def fields(self) -> List[StandardSqlField]: 

300 """The fields in this struct.""" 

301 result = [] 

302 

303 for field_resource in self._properties.get("fields", []): 

304 field = StandardSqlField() 

305 field._properties = field_resource # We do not use a copy on purpose. 

306 result.append(field) 

307 

308 return result 

309 

310 @fields.setter 

311 def fields(self, value: Iterable[StandardSqlField]): 

312 self._properties["fields"] = [field.to_api_repr() for field in value] 

313 

314 def to_api_repr(self) -> Dict[str, Any]: 

315 """Construct the API resource representation of this SQL struct type.""" 

316 return copy.deepcopy(self._properties) 

317 

318 @classmethod 

319 def from_api_repr(cls, resource: Dict[str, Any]) -> "StandardSqlStructType": 

320 """Construct an SQL struct type instance given its API representation.""" 

321 fields = ( 

322 StandardSqlField.from_api_repr(field_resource) 

323 for field_resource in resource.get("fields", []) 

324 ) 

325 return cls(fields=fields) 

326 

327 def __eq__(self, other): 

328 if not isinstance(other, StandardSqlStructType): 

329 return NotImplemented 

330 else: 

331 return self.fields == other.fields 

332 

333 

334class StandardSqlTableType: 

335 """A table type. 

336 

337 See: 

338 https://cloud.google.com/workflows/docs/reference/googleapis/bigquery/v2/Overview#StandardSqlTableType 

339 

340 Args: 

341 columns: The columns in this table type. 

342 """ 

343 

344 def __init__(self, columns: Iterable[StandardSqlField]): 

345 self._properties = {"columns": [col.to_api_repr() for col in columns]} 

346 

347 @property 

348 def columns(self) -> List[StandardSqlField]: 

349 """The columns in this table type.""" 

350 result = [] 

351 

352 for column_resource in self._properties.get("columns", []): 

353 column = StandardSqlField() 

354 column._properties = column_resource # We do not use a copy on purpose. 

355 result.append(column) 

356 

357 return result 

358 

359 @columns.setter 

360 def columns(self, value: Iterable[StandardSqlField]): 

361 self._properties["columns"] = [col.to_api_repr() for col in value] 

362 

363 def to_api_repr(self) -> Dict[str, Any]: 

364 """Construct the API resource representation of this SQL table type.""" 

365 return copy.deepcopy(self._properties) 

366 

367 @classmethod 

368 def from_api_repr(cls, resource: Dict[str, Any]) -> "StandardSqlTableType": 

369 """Construct an SQL table type instance given its API representation.""" 

370 columns = [] 

371 

372 for column_resource in resource.get("columns", []): 

373 type_ = column_resource.get("type") 

374 if type_ is None: 

375 type_ = {} 

376 

377 column = StandardSqlField( 

378 name=column_resource.get("name"), 

379 type=StandardSqlDataType.from_api_repr(type_), 

380 ) 

381 columns.append(column) 

382 

383 return cls(columns=columns) 

384 

385 def __eq__(self, other): 

386 if not isinstance(other, StandardSqlTableType): 

387 return NotImplemented 

388 else: 

389 return self.columns == other.columns