1010# See the License for the specific language governing permissions and
1111# limitations under the License.
1212import math
13- from datetime import datetime
13+ from datetime import datetime , time , date , timezone , timedelta
14+ from decimal import Decimal
1415
1516import pytest
1617import pytz
@@ -123,22 +124,267 @@ def test_string_query_param(trino_connection):
123124 assert rows [0 ][0 ] == "six'"
124125
125126
126- def test_datetime_query_param (trino_connection ):
127+ def test_python_types_not_used_when_experimental_python_types_is_not_set (trino_connection ):
127128 cur = trino_connection .cursor ()
128129
129- cur .execute ("SELECT ?" , params = (datetime (2020 , 1 , 1 , 0 , 0 , 0 ),))
130+ cur .execute ("""
131+ SELECT
132+ DECIMAL '0.142857',
133+ DATE '2018-01-01',
134+ TIMESTAMP '2019-01-01 00:00:00.000+01:00',
135+ TIMESTAMP '2019-01-01 00:00:00.000 UTC',
136+ TIMESTAMP '2019-01-01 00:00:00.000',
137+ TIME '00:00:00.000'
138+ """ )
139+ rows = cur .fetchall ()
140+
141+ assert rows [0 ][0 ] == '0.142857'
142+ assert rows [0 ][1 ] == '2018-01-01'
143+ assert rows [0 ][2 ] == '2019-01-01 00:00:00.000 +01:00'
144+ assert rows [0 ][3 ] == '2019-01-01 00:00:00.000 UTC'
145+ assert rows [0 ][4 ] == '2019-01-01 00:00:00.000'
146+ assert rows [0 ][5 ] == '00:00:00.000'
147+
148+
149+ def test_decimal_query_param (trino_connection ):
150+ cur = trino_connection .cursor (experimental_python_types = True )
151+
152+ cur .execute ("SELECT ?" , params = (Decimal ('0.142857' ),))
153+ rows = cur .fetchall ()
154+
155+ assert rows [0 ][0 ] == Decimal ('0.142857' )
156+
157+
158+ def test_null_decimal (trino_connection ):
159+ cur = trino_connection .cursor (experimental_python_types = True )
160+
161+ cur .execute ("SELECT CAST(NULL AS DECIMAL)" )
162+ rows = cur .fetchall ()
163+
164+ assert rows [0 ][0 ] is None
165+
166+
167+ def test_biggest_decimal (trino_connection ):
168+ cur = trino_connection .cursor (experimental_python_types = True )
169+
170+ params = Decimal ('99999999999999999999999999999999999999' )
171+ cur .execute ("SELECT ?" , params = (params ,))
130172 rows = cur .fetchall ()
131173
132- assert rows [0 ][0 ] == "2020-01-01 00:00:00.000"
174+ assert rows [0 ][0 ] == params
133175
134- cur .execute ("SELECT ?" ,
135- params = (datetime (2020 , 1 , 1 , 0 , 0 , 0 , tzinfo = pytz .utc ),))
176+
177+ def test_smallest_decimal (trino_connection ):
178+ cur = trino_connection .cursor (experimental_python_types = True )
179+
180+ params = Decimal ('-99999999999999999999999999999999999999' )
181+ cur .execute ("SELECT ?" , params = (params ,))
136182 rows = cur .fetchall ()
137183
138- assert rows [0 ][0 ] == "2020-01-01 00:00:00.000 UTC"
184+ assert rows [0 ][0 ] == params
185+
186+
187+ def test_highest_precision_decimal (trino_connection ):
188+ cur = trino_connection .cursor (experimental_python_types = True )
189+
190+ params = Decimal ('0.99999999999999999999999999999999999999' )
191+ cur .execute ("SELECT ?" , params = (params ,))
192+ rows = cur .fetchall ()
193+
194+ assert rows [0 ][0 ] == params
195+
196+
197+ def test_datetime_query_param (trino_connection ):
198+ cur = trino_connection .cursor (experimental_python_types = True )
199+
200+ params = datetime (2020 , 1 , 1 , 16 , 43 , 22 , 320000 )
201+
202+ cur .execute ("SELECT ?" , params = (params ,))
203+ rows = cur .fetchall ()
204+
205+ assert rows [0 ][0 ] == params
206+ assert cur .description [0 ][1 ] == "timestamp"
207+
208+
209+ def test_datetime_with_trailing_zeros (trino_connection ):
210+ cur = trino_connection .cursor (experimental_python_types = True )
211+
212+ cur .execute ("SELECT TIMESTAMP '2001-08-22 03:04:05.321000'" )
213+ rows = cur .fetchall ()
214+
215+ assert rows [0 ][0 ] == datetime .strptime ("2001-08-22 03:04:05.321000" , "%Y-%m-%d %H:%M:%S.%f" )
216+
217+
218+ def test_datetime_with_utc_time_zone_query_param (trino_connection ):
219+ cur = trino_connection .cursor (experimental_python_types = True )
220+
221+ params = datetime (2020 , 1 , 1 , 16 , 43 , 22 , 320000 , tzinfo = pytz .timezone ('UTC' ))
222+
223+ cur .execute ("SELECT ?" , params = (params ,))
224+ rows = cur .fetchall ()
225+
226+ assert rows [0 ][0 ] == params
139227 assert cur .description [0 ][1 ] == "timestamp with time zone"
140228
141229
230+ def test_datetime_with_numeric_offset_time_zone_query_param (trino_connection ):
231+ cur = trino_connection .cursor (experimental_python_types = True )
232+
233+ tz = timezone (- timedelta (hours = 5 , minutes = 30 ))
234+
235+ params = datetime (2020 , 1 , 1 , 16 , 43 , 22 , 320000 , tzinfo = tz )
236+
237+ cur .execute ("SELECT ?" , params = (params ,))
238+ rows = cur .fetchall ()
239+
240+ assert rows [0 ][0 ] == params
241+ assert cur .description [0 ][1 ] == "timestamp with time zone"
242+
243+
244+ def test_datetime_with_named_time_zone_query_param (trino_connection ):
245+ cur = trino_connection .cursor (experimental_python_types = True )
246+
247+ params = datetime (2020 , 1 , 1 , 16 , 43 , 22 , 320000 , tzinfo = pytz .timezone ('America/Los_Angeles' ))
248+
249+ cur .execute ("SELECT ?" , params = (params ,))
250+ rows = cur .fetchall ()
251+
252+ assert rows [0 ][0 ] == params
253+ assert cur .description [0 ][1 ] == "timestamp with time zone"
254+
255+
256+ def test_null_datetime_with_time_zone (trino_connection ):
257+ cur = trino_connection .cursor (experimental_python_types = True )
258+
259+ cur .execute ("SELECT CAST(NULL AS TIMESTAMP WITH TIME ZONE)" )
260+ rows = cur .fetchall ()
261+
262+ assert rows [0 ][0 ] is None
263+
264+
265+ def test_datetime_with_time_zone_numeric_offset (trino_connection ):
266+ cur = trino_connection .cursor (experimental_python_types = True )
267+
268+ cur .execute ("SELECT TIMESTAMP '2001-08-22 03:04:05.321 -08:00'" )
269+ rows = cur .fetchall ()
270+
271+ assert rows [0 ][0 ] == datetime .strptime ("2001-08-22 03:04:05.321 -08:00" , "%Y-%m-%d %H:%M:%S.%f %z" )
272+
273+
274+ def test_unexisting_datetimes_with_time_zone_query_param (trino_connection ):
275+ cur = trino_connection .cursor (experimental_python_types = True )
276+
277+ params = datetime (2021 , 3 , 28 , 2 , 30 , 0 , tzinfo = pytz .timezone ('Europe/Brussels' ))
278+ with pytest .raises (trino .exceptions .TrinoUserError ):
279+ cur .execute ("SELECT ?" , params = (params ,))
280+ cur .fetchall ()
281+
282+
283+ def test_doubled_datetimes_first_query_param (trino_connection ):
284+ cur = trino_connection .cursor (experimental_python_types = True )
285+
286+ params = pytz .timezone ('US/Eastern' ).localize (datetime (2002 , 10 , 27 , 1 , 30 , 0 ), is_dst = True )
287+
288+ cur .execute ("SELECT ?" , params = (params ,))
289+ rows = cur .fetchall ()
290+
291+ assert rows [0 ][0 ] == datetime (2002 , 10 , 27 , 1 , 30 , 0 , tzinfo = pytz .timezone ('US/Eastern' ))
292+
293+
294+ def test_doubled_datetimes_second_query_param (trino_connection ):
295+ cur = trino_connection .cursor (experimental_python_types = True )
296+
297+ params = pytz .timezone ('US/Eastern' ).localize (datetime (2002 , 10 , 27 , 1 , 30 , 0 ), is_dst = False )
298+
299+ cur .execute ("SELECT ?" , params = (params ,))
300+ rows = cur .fetchall ()
301+
302+ assert rows [0 ][0 ] == datetime (2002 , 10 , 27 , 1 , 30 , 0 , tzinfo = pytz .timezone ('US/Eastern' ))
303+
304+
305+ def test_date_query_param (trino_connection ):
306+ cur = trino_connection .cursor (experimental_python_types = True )
307+
308+ params = datetime (2020 , 1 , 1 , 0 , 0 , 0 ).date ()
309+
310+ cur .execute ("SELECT ?" , params = (params ,))
311+ rows = cur .fetchall ()
312+
313+ assert rows [0 ][0 ] == params
314+
315+
316+ def test_null_date (trino_connection ):
317+ cur = trino_connection .cursor (experimental_python_types = True )
318+
319+ cur .execute ("SELECT CAST(NULL AS DATE)" )
320+ rows = cur .fetchall ()
321+
322+ assert rows [0 ][0 ] is None
323+
324+
325+ def test_unsupported_python_dates (trino_connection ):
326+ cur = trino_connection .cursor (experimental_python_types = True )
327+
328+ # dates not supported in Python date type
329+ for unsupported_date in [
330+ '-0001-01-01' ,
331+ '0000-01-01'
332+ ]:
333+ with pytest .raises (ValueError ):
334+ cur .execute (f"SELECT DATE '{ unsupported_date } '" )
335+ cur .fetchall ()
336+
337+
338+ def test_supported_special_dates_query_param (trino_connection ):
339+ cur = trino_connection .cursor (experimental_python_types = True )
340+
341+ for params in (
342+ # first day of AD
343+ date (1 , 1 , 1 ),
344+ date (12 , 12 , 12 ),
345+ # before julian->gregorian switch
346+ date (1500 , 1 , 1 ),
347+ # During julian->gregorian switch
348+ date (1752 , 9 , 4 ),
349+ # before epoch
350+ date (1952 , 4 , 3 ),
351+ date (1970 , 1 , 1 ),
352+ date (1970 , 2 , 3 ),
353+ # summer on northern hemisphere (possible DST)
354+ date (2017 , 7 , 1 ),
355+ # winter on northern hemisphere (possible DST on southern hemisphere)
356+ date (2017 , 1 , 1 ),
357+ # winter on southern hemisphere (possible DST on northern hemisphere)
358+ date (2017 , 12 , 31 ),
359+ date (1983 , 4 , 1 ),
360+ date (1983 , 10 , 1 ),
361+ ):
362+ cur .execute ("SELECT ?" , params = (params ,))
363+ rows = cur .fetchall ()
364+
365+ assert rows [0 ][0 ] == params
366+
367+
368+ def test_time_query_param (trino_connection ):
369+ cur = trino_connection .cursor (experimental_python_types = True )
370+
371+ params = time (12 , 3 , 44 , 333000 )
372+
373+ cur .execute ("SELECT ?" , params = (params ,))
374+ rows = cur .fetchall ()
375+
376+ assert rows [0 ][0 ] == params
377+
378+
379+ def test_time_with_time_zone_query_param (trino_connection ):
380+ with pytest .raises (trino .exceptions .NotSupportedError ):
381+ cur = trino_connection .cursor ()
382+
383+ params = time (16 , 43 , 22 , 320000 , tzinfo = pytz .timezone ('Asia/Shanghai' ))
384+
385+ cur .execute ("SELECT ?" , params = (params ,))
386+
387+
142388def test_array_query_param (trino_connection ):
143389 cur = trino_connection .cursor ()
144390
@@ -158,6 +404,38 @@ def test_array_query_param(trino_connection):
158404 assert rows [0 ][0 ] == "array(integer)"
159405
160406
407+ def test_array_timestamp_query_param (trino_connection ):
408+ cur = trino_connection .cursor (experimental_python_types = True )
409+
410+ params = [datetime (2020 , 1 , 1 , 0 , 0 , 0 ), datetime (2020 , 1 , 2 , 0 , 0 , 0 )]
411+
412+ cur .execute ("SELECT ?" , params = (params ,))
413+ rows = cur .fetchall ()
414+
415+ assert rows [0 ][0 ] == params
416+
417+ cur .execute ("SELECT TYPEOF(?)" , params = (params ,))
418+ rows = cur .fetchall ()
419+
420+ assert rows [0 ][0 ] == "array(timestamp(6))"
421+
422+
423+ def test_array_timestamp_with_timezone_query_param (trino_connection ):
424+ cur = trino_connection .cursor (experimental_python_types = True )
425+
426+ params = [datetime (2020 , 1 , 1 , 0 , 0 , 0 , tzinfo = pytz .utc ), datetime (2020 , 1 , 2 , 0 , 0 , 0 , tzinfo = pytz .utc )]
427+
428+ cur .execute ("SELECT ?" , params = (params ,))
429+ rows = cur .fetchall ()
430+
431+ assert rows [0 ][0 ] == params
432+
433+ cur .execute ("SELECT TYPEOF(?)" , params = (params ,))
434+ rows = cur .fetchall ()
435+
436+ assert rows [0 ][0 ] == "array(timestamp(6) with time zone)"
437+
438+
161439def test_dict_query_param (trino_connection ):
162440 cur = trino_connection .cursor ()
163441
0 commit comments