4444 QuotedNameArgumentTest ,
4545 SimpleUpdateDeleteTest as _SimpleUpdateDeleteTest ,
4646 TimestampMicrosecondsTest as _TimestampMicrosecondsTest ,
47- TrueDivTest as _TrueDivTest ,
48- IntegerTest as _IntegerTest ,
49- NumericTest as _NumericTest ,
5047)
5148
5249from sqlalchemy .testing .suite .test_types import (
6360
6461if packaging .version .parse (sqlalchemy .__version__ ) >= packaging .version .parse ("2.0" ):
6562 from sqlalchemy .sql import type_coerce
63+ from sqlalchemy .testing .suite import (
64+ TrueDivTest as _TrueDivTest ,
65+ IntegerTest as _IntegerTest ,
66+ NumericTest as _NumericTest ,
67+ DifficultParametersTest as _DifficultParametersTest ,
68+ FetchLimitOffsetTest as _FetchLimitOffsetTest ,
69+ )
6670
6771 class TimestampMicrosecondsTest (_TimestampMicrosecondsTest ):
6872 data = datetime .datetime (2012 , 10 , 15 , 12 , 57 , 18 , 396 , tzinfo = pytz .UTC )
6973
70- # TimestampMicrosecondsTest literal() no literal_execute parameter? Go back and add to literal()"
71- @pytest .mark .skip ("" )
72- def test_literal (self , literal_round_trip ):
73- pass
74-
7574 def test_select_direct (self , connection ):
7675 # This func added because this test was failing when passed the
7776 # UTC timezone.
@@ -106,14 +105,12 @@ def test_round_trip_executemany(self, connection):
106105 test_round_trip_executemany
107106 )
108107
109- # TrueDivTest issue because 1.4 always rounded down, but 2.0 rounds based on the data types. The assertion cannot reconcile 1.5==1 thusly
110108 class TrueDivTest (_TrueDivTest ):
111- @pytest .mark .skip ("SQLAlchemy 2.0 rounds based on datatype" )
109+ @pytest .mark .skip ("BQ rounds based on datatype" )
112110 def test_floordiv_integer (self ):
113- # TODO: possibly compare rounded result instead?
114111 pass
115112
116- @pytest .mark .skip ("SQLAlchemy 2.0 rounds based on datatype" )
113+ @pytest .mark .skip ("BQ rounds based on datatype" )
117114 def test_floordiv_integer_bound (self ):
118115 pass
119116
@@ -151,8 +148,9 @@ class InsertBehaviorTest(_InsertBehaviorTest):
151148 def test_insert_from_select_autoinc (cls ):
152149 pass
153150
154- # TODO: Find cause of error
155- @pytest .mark .skip ("" )
151+ @pytest .mark .skip (
152+ "BQ has no autoinc and client-side defaults can't work for select."
153+ )
156154 def test_no_results_for_non_returning_insert (cls ):
157155 pass
158156
@@ -178,7 +176,7 @@ def run(type_, input_, output, filter_=None, check_scale=False):
178176
179177 where_expr = True
180178
181- # Adding where clause
179+ # Adding where clause for 2.0 compatibility
182180 connection .execute (t .delete ().where (where_expr ))
183181
184182 # test that this is actually a number!
@@ -203,6 +201,142 @@ def run(type_, input_, output, filter_=None, check_scale=False):
203201
204202 return run
205203
204+ class DifficultParametersTest (_DifficultParametersTest ):
205+ # removed parameters that dont work with bigquery
206+ tough_parameters = testing .combinations (
207+ ("boring" ,),
208+ ("per cent" ,),
209+ ("per % cent" ,),
210+ ("%percent" ,),
211+ ("col:ons" ,),
212+ ("_starts_with_underscore" ,),
213+ ("more :: %colons%" ,),
214+ ("_name" ,),
215+ ("___name" ,),
216+ ("42numbers" ,),
217+ ("percent%signs" ,),
218+ ("has spaces" ,),
219+ ("1param" ,),
220+ ("1col:on" ,),
221+ argnames = "paramname" ,
222+ )
223+
224+ @tough_parameters
225+ @config .requirements .unusual_column_name_characters
226+ def test_round_trip_same_named_column (self , paramname , connection , metadata ):
227+ name = paramname
228+
229+ t = Table (
230+ "t" ,
231+ metadata ,
232+ Column ("id" , Integer , primary_key = True ),
233+ Column (name , String (50 ), nullable = False ),
234+ )
235+
236+ # table is created
237+ t .create (connection )
238+
239+ # automatic param generated by insert
240+ connection .execute (t .insert ().values ({"id" : 1 , name : "some name" }))
241+
242+ # automatic param generated by criteria, plus selecting the column
243+ stmt = select (t .c [name ]).where (t .c [name ] == "some name" )
244+
245+ eq_ (connection .scalar (stmt ), "some name" )
246+
247+ # use the name in a param explicitly
248+ stmt = select (t .c [name ]).where (t .c [name ] == bindparam (name ))
249+
250+ row = connection .execute (stmt , {name : "some name" }).first ()
251+
252+ # name works as the key from cursor.description
253+ eq_ (row ._mapping [name ], "some name" )
254+
255+ # use expanding IN
256+ stmt = select (t .c [name ]).where (
257+ t .c [name ].in_ (["some name" , "some other_name" ])
258+ )
259+
260+ row = connection .execute (stmt ).first ()
261+
262+ @testing .fixture
263+ def multirow_fixture (self , metadata , connection ):
264+ mytable = Table (
265+ "mytable" ,
266+ metadata ,
267+ Column ("myid" , Integer ),
268+ Column ("name" , String (50 )),
269+ Column ("desc" , String (50 )),
270+ )
271+
272+ mytable .create (connection )
273+
274+ connection .execute (
275+ mytable .insert (),
276+ [
277+ {"myid" : 1 , "name" : "a" , "desc" : "a_desc" },
278+ {"myid" : 2 , "name" : "b" , "desc" : "b_desc" },
279+ {"myid" : 3 , "name" : "c" , "desc" : "c_desc" },
280+ {"myid" : 4 , "name" : "d" , "desc" : "d_desc" },
281+ ],
282+ )
283+ yield mytable
284+
285+ @tough_parameters
286+ def test_standalone_bindparam_escape (
287+ self , paramname , connection , multirow_fixture
288+ ):
289+ tbl1 = multirow_fixture
290+ stmt = select (tbl1 .c .myid ).where (
291+ tbl1 .c .name == bindparam (paramname , value = "x" )
292+ )
293+ res = connection .scalar (stmt , {paramname : "c" })
294+ eq_ (res , 3 )
295+
296+ @tough_parameters
297+ def test_standalone_bindparam_escape_expanding (
298+ self , paramname , connection , multirow_fixture
299+ ):
300+ tbl1 = multirow_fixture
301+ stmt = (
302+ select (tbl1 .c .myid )
303+ .where (tbl1 .c .name .in_ (bindparam (paramname , value = ["a" , "b" ])))
304+ .order_by (tbl1 .c .myid )
305+ )
306+
307+ res = connection .scalars (stmt , {paramname : ["d" , "a" ]}).all ()
308+ eq_ (res , [1 , 4 ])
309+
310+ class FetchLimitOffsetTest (_FetchLimitOffsetTest ):
311+ @pytest .mark .skip ("BigQuery doesn't allow an offset without a limit." )
312+ def test_simple_offset (self ):
313+ pass
314+
315+ test_bound_offset = test_simple_offset
316+ test_expr_offset = test_simple_offset_zero = test_simple_offset
317+
318+ # The original test is missing an order by.
319+
320+ # Also, note that sqlalchemy union is a union distinct, not a
321+ # union all. This test caught that were were getting that wrong.
322+ def test_limit_render_multiple_times (self , connection ):
323+ table = self .tables .some_table
324+ stmt = select (table .c .id ).order_by (table .c .id ).limit (1 ).scalar_subquery ()
325+
326+ u = sqlalchemy .union (select (stmt ), select (stmt )).subquery ().select ()
327+
328+ self ._assert_result (
329+ connection ,
330+ u ,
331+ [(1 ,)],
332+ )
333+
334+ # from else statement ....
335+ del DistinctOnTest # expects unquoted table names.
336+ del HasIndexTest # BQ doesn't do the indexes that SQLA is loooking for.
337+ del IdentityAutoincrementTest # BQ doesn't do autoincrement
338+
339+
206340elif packaging .version .parse (sqlalchemy .__version__ ) < packaging .version .parse ("1.4" ):
207341 from sqlalchemy .testing .suite import LimitOffsetTest as _LimitOffsetTest
208342
0 commit comments