@@ -104,111 +104,161 @@ def test_table_clustering_fields_dialect_option_type_error(faux_conn):
104
104
)
105
105
106
106
107
- def test_table_time_partitioning_dialect_option (faux_conn ):
108
- # expect table creation to fail as SQLite does not support partitioned tables
109
- with pytest .raises (sqlite3 .OperationalError ):
110
- setup_table (
111
- faux_conn ,
112
- "some_table" ,
113
- sqlalchemy .Column ("id" , sqlalchemy .Integer ),
114
- sqlalchemy .Column ("createdAt" , sqlalchemy .DateTime ),
115
- bigquery_time_partitioning = TimePartitioning (),
116
- )
107
+ @pytest .mark .parametrize (
108
+ "column_dtype,time_partitioning_type,func_name" ,
109
+ [
110
+ # DATE dtype
111
+ pytest .param (
112
+ sqlalchemy .DATE ,
113
+ TimePartitioningType .HOUR , # Only MONTH/YEAR are permitted in BigQuery
114
+ "DATE_TRUNC" ,
115
+ marks = pytest .mark .xfail ,
116
+ ),
117
+ pytest .param (
118
+ sqlalchemy .DATE ,
119
+ TimePartitioningType .DAY , # Only MONTH/YEAR are permitted in BigQuery
120
+ "DATE_TRUNC" ,
121
+ marks = pytest .mark .xfail ,
122
+ ),
123
+ (sqlalchemy .DATE , TimePartitioningType .MONTH , "DATE_TRUNC" ),
124
+ (sqlalchemy .DATE , TimePartitioningType .YEAR , "DATE_TRUNC" ),
125
+ # TIMESTAMP dtype
126
+ (sqlalchemy .TIMESTAMP , TimePartitioningType .HOUR , "TIMESTAMP_TRUNC" ),
127
+ (sqlalchemy .TIMESTAMP , TimePartitioningType .DAY , "TIMESTAMP_TRUNC" ),
128
+ (sqlalchemy .TIMESTAMP , TimePartitioningType .MONTH , "TIMESTAMP_TRUNC" ),
129
+ (sqlalchemy .TIMESTAMP , TimePartitioningType .YEAR , "TIMESTAMP_TRUNC" ),
130
+ # DATETIME dtype
131
+ (sqlalchemy .DATETIME , TimePartitioningType .HOUR , "DATETIME_TRUNC" ),
132
+ (sqlalchemy .DATETIME , TimePartitioningType .DAY , "DATETIME_TRUNC" ),
133
+ (sqlalchemy .DATETIME , TimePartitioningType .MONTH , "DATETIME_TRUNC" ),
134
+ (sqlalchemy .DATETIME , TimePartitioningType .YEAR , "DATETIME_TRUNC" ),
135
+ # TimePartitioning.type_ == None
136
+ (sqlalchemy .DATETIME , None , "DATETIME_TRUNC" ),
137
+ ],
138
+ )
139
+ def test_table_time_partitioning_given_field_and_type__dialect_options (
140
+ faux_conn , column_dtype , time_partitioning_type , func_name
141
+ ):
142
+ """NOTE: Expect table creation to fail as SQLite does not support
143
+ partitioned tables, despite that, we are still able to test the generation
144
+ of SQL statements.
117
145
118
- assert " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ()) == (
119
- "CREATE TABLE `some_table` ( `id` INT64, `createdAt` DATETIME )"
120
- " PARTITION BY DATE_TRUNC(_PARTITIONDATE, DAY)"
121
- )
146
+ Each parametrization ensures that the appropriate function is generated
147
+ depending on whether the column datatype is DATE, TIMESTAMP, DATETIME and
148
+ whether the TimePartitioningType is HOUR, DAY, MONTH, YEAR.
122
149
150
+ `DATE_TRUNC` only returns a result if TimePartitioningType is DAY, MONTH,
151
+ YEAR. BigQuery cannot partition on DATE by HOUR, so that is expected to
152
+ xfail.
123
153
124
- def test_table_require_partition_filter_dialect_option (faux_conn ):
125
- # expect table creation to fail as SQLite does not support partitioned tables
126
- with pytest .raises (sqlite3 .OperationalError ):
127
- setup_table (
128
- faux_conn ,
129
- "some_table" ,
130
- sqlalchemy .Column ("createdAt" , sqlalchemy .DateTime ),
131
- bigquery_time_partitioning = TimePartitioning (field = "createdAt" ),
132
- bigquery_require_partition_filter = True ,
133
- )
154
+ A distinguishing characteristic of this test is we provide an argument to
155
+ the TimePartitioning class for both field and type_.
134
156
135
- assert " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ()) == (
136
- "CREATE TABLE `some_table` ( `createdAt` DATETIME )"
137
- " PARTITION BY DATE_TRUNC(createdAt, DAY)"
138
- " OPTIONS(require_partition_filter=true)"
139
- )
157
+ Special case: IF time_partitioning_type is None, the __init__() in the
158
+ TimePartitioning class will overwrite it with TimePartitioningType.DAY as
159
+ the default.
160
+ """
140
161
162
+ if time_partitioning_type is None :
163
+ time_partitioning_type = TimePartitioningType .DAY
141
164
142
- def test_table_time_partitioning_with_field_dialect_option (faux_conn ):
143
- # expect table creation to fail as SQLite does not support partitioned tables
144
165
with pytest .raises (sqlite3 .OperationalError ):
145
166
setup_table (
146
167
faux_conn ,
147
168
"some_table" ,
148
169
sqlalchemy .Column ("id" , sqlalchemy .Integer ),
149
- sqlalchemy .Column ("createdAt" , sqlalchemy .DateTime ),
150
- bigquery_time_partitioning = TimePartitioning (field = "createdAt" ),
170
+ sqlalchemy .Column ("createdAt" , column_dtype ),
171
+ bigquery_time_partitioning = TimePartitioning (
172
+ field = "createdAt" , type_ = time_partitioning_type
173
+ ),
151
174
)
152
175
153
- assert " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ()) == (
154
- "CREATE TABLE `some_table` ( `id` INT64, `createdAt` DATETIME )"
155
- " PARTITION BY DATE_TRUNC(createdAt, DAY)"
176
+ result = " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ())
177
+ expected = (
178
+ f"CREATE TABLE `some_table` ( `id` INT64, `createdAt` { column_dtype .__visit_name__ } )"
179
+ f" PARTITION BY { func_name } (createdAt, { time_partitioning_type } )"
156
180
)
181
+ assert result == expected
157
182
158
183
159
- def test_table_time_partitioning_by_month_dialect_option (faux_conn ):
160
- # expect table creation to fail as SQLite does not support partitioned tables
161
- with pytest .raises (sqlite3 .OperationalError ):
162
- setup_table (
163
- faux_conn ,
164
- "some_table" ,
165
- sqlalchemy .Column ("id" , sqlalchemy .Integer ),
166
- sqlalchemy .Column ("createdAt" , sqlalchemy .DateTime ),
167
- bigquery_time_partitioning = TimePartitioning (
168
- field = "createdAt" ,
169
- type_ = TimePartitioningType .MONTH ,
170
- ),
171
- )
184
+ def test_table_time_partitioning_given_field_but_no_type__dialect_option (faux_conn ):
185
+ """Expect table creation to fail as SQLite does not support partitioned tables
172
186
173
- assert " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ()) == (
174
- "CREATE TABLE `some_table` ( `id` INT64, `createdAt` DATETIME )"
175
- " PARTITION BY DATE_TRUNC(createdAt, MONTH)"
176
- )
187
+ Confirms that if the column datatype is DATETIME but no TimePartitioning.type_
188
+ has been supplied, the system will default to DAY.
177
189
190
+ A distinguishing characteristic of this test is we provide an argument to
191
+ the TimePartitioning class for field but not type_.
192
+ """
178
193
179
- def test_table_time_partitioning_with_timestamp_dialect_option (faux_conn ):
180
- # expect table creation to fail as SQLite does not support partitioned tables
181
194
with pytest .raises (sqlite3 .OperationalError ):
182
195
setup_table (
183
196
faux_conn ,
184
197
"some_table" ,
185
198
sqlalchemy .Column ("id" , sqlalchemy .Integer ),
186
- sqlalchemy .Column ("createdAt" , sqlalchemy .TIMESTAMP ),
199
+ sqlalchemy .Column ("createdAt" , sqlalchemy .DateTime ),
187
200
bigquery_time_partitioning = TimePartitioning (field = "createdAt" ),
188
201
)
189
-
190
- assert " " . join ( faux_conn . test_data [ "execute" ][ - 1 ][ 0 ]. strip (). split ()) = = (
191
- "CREATE TABLE `some_table` ( `id` INT64, `createdAt` TIMESTAMP )"
192
- " PARTITION BY TIMESTAMP_TRUNC (createdAt, DAY)"
202
+ result = " " . join ( faux_conn . test_data [ "execute" ][ - 1 ][ 0 ]. strip (). split ())
203
+ expected = (
204
+ "CREATE TABLE `some_table` ( `id` INT64, `createdAt` DATETIME )"
205
+ " PARTITION BY DATETIME_TRUNC (createdAt, DAY)"
193
206
)
207
+ assert result == expected
194
208
195
209
196
- def test_table_time_partitioning_with_date_dialect_option (faux_conn ):
197
- # expect table creation to fail as SQLite does not support partitioned tables
210
+ @pytest .mark .parametrize (
211
+ "column_dtype,time_partitioning_type" ,
212
+ [
213
+ pytest .param (
214
+ sqlalchemy .DATE ,
215
+ TimePartitioningType .HOUR ,
216
+ marks = pytest .mark .xfail ,
217
+ ),
218
+ (sqlalchemy .DATE , TimePartitioningType .DAY ),
219
+ (sqlalchemy .DATE , TimePartitioningType .MONTH ),
220
+ (sqlalchemy .DATE , TimePartitioningType .YEAR ),
221
+ ],
222
+ )
223
+ def test_table_time_partitioning_given_type__but_no_field_dialect_option (
224
+ faux_conn ,
225
+ column_dtype ,
226
+ time_partitioning_type ,
227
+ ):
228
+ """NOTE: Expect table creation to fail as SQLite does not support
229
+ partitioned tables, despite that, we are still able to test the generation
230
+ of SQL statements
231
+
232
+ If the `field` argument to TimePartitioning() is not provided, it defaults to
233
+ None. That causes the pseudocolumn "_PARTITIONDATE" to be used by default as
234
+ the column to partition by.
235
+
236
+ _PARTITIONTIME only returns a result if TimePartitioningType is DAY, MONTH,
237
+ YEAR. BigQuery cannot partition on _PARTITIONDATE by HOUR, so that is
238
+ expected to xfail.
239
+
240
+ A distinguishing characteristic of this test is we provide an argument to
241
+ the TimePartitioning class for type_ but not field.
242
+ """
243
+
198
244
with pytest .raises (sqlite3 .OperationalError ):
199
245
setup_table (
200
246
faux_conn ,
201
247
"some_table_2" ,
202
248
sqlalchemy .Column ("id" , sqlalchemy .Integer ),
203
- sqlalchemy .Column ("createdAt" , sqlalchemy . DATE ),
204
- bigquery_time_partitioning = TimePartitioning (field = "createdAt" ),
249
+ sqlalchemy .Column ("createdAt" , column_dtype ),
250
+ bigquery_time_partitioning = TimePartitioning (type_ = time_partitioning_type ),
205
251
)
206
252
207
253
# confirm that the following code creates the correct SQL string
208
- assert " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ()) == (
209
- "CREATE TABLE `some_table_2` ( `id` INT64, `createdAt` DATE )"
210
- " PARTITION BY createdAt"
254
+ result = " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ())
255
+
256
+ # We need two versions of expected depending on whether we use _PARTITIONDATE
257
+ expected = (
258
+ f"CREATE TABLE `some_table_2` ( `id` INT64, `createdAt` { column_dtype .__visit_name__ } )"
259
+ f" PARTITION BY _PARTITIONDATE"
211
260
)
261
+ assert result == expected
212
262
213
263
214
264
def test_table_time_partitioning_dialect_option_partition_expiration_days (faux_conn ):
@@ -227,7 +277,7 @@ def test_table_time_partitioning_dialect_option_partition_expiration_days(faux_c
227
277
228
278
assert " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ()) == (
229
279
"CREATE TABLE `some_table` ( `createdAt` DATETIME )"
230
- " PARTITION BY DATE_TRUNC (createdAt, DAY)"
280
+ " PARTITION BY DATETIME_TRUNC (createdAt, DAY)"
231
281
" OPTIONS(partition_expiration_days=0.25)"
232
282
)
233
283
@@ -400,13 +450,16 @@ def test_table_all_dialect_option(faux_conn):
400
450
),
401
451
)
402
452
403
- assert " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ()) == (
453
+ result = " " .join (faux_conn .test_data ["execute" ][- 1 ][0 ].strip ().split ())
454
+ expected = (
404
455
"CREATE TABLE `some_table` ( `id` INT64, `country` STRING, `town` STRING, `createdAt` DATETIME )"
405
- " PARTITION BY DATE_TRUNC (createdAt, DAY)"
456
+ " PARTITION BY DATETIME_TRUNC (createdAt, DAY)"
406
457
" CLUSTER BY country, town"
407
458
" OPTIONS(partition_expiration_days=30.0, expiration_timestamp=TIMESTAMP '2038-01-01 00:00:00+00:00', require_partition_filter=true, default_rounding_mode='ROUND_HALF_EVEN')"
408
459
)
409
460
461
+ assert result == expected
462
+
410
463
411
464
def test_validate_friendly_name_value_type (ddl_compiler ):
412
465
# expect option value to be transformed as a string expression
0 commit comments