1616
1717UID = "elastic"
1818CONNECT_STRING = 'Driver={Elasticsearch Driver};UID=%s;PWD=%s;Secure=0;' % (UID , Elasticsearch .AUTH_PASSWORD )
19- CATALOG = "elasticsearch" # nightly built
20- #CATALOG = "distribution_run" # source built
19+ CATALOG = "distribution_run" # source built, "elasticsearch": nightly builds
2120
2221class Testing (unittest .TestCase ):
2322
2423 _data = None
2524 _dsn = None
2625 _pyodbc = None
26+ _catalog = None
2727
28- def __init__ (self , test_data , dsn = None ):
28+ def __init__ (self , test_data , catalog = CATALOG , dsn = None ):
2929 super ().__init__ ()
3030 self ._data = test_data
31- self ._dsn = dsn if dsn else CONNECT_STRING
31+ self ._catalog = catalog
32+ if dsn :
33+ if "Driver=" not in dsn :
34+ self ._dsn = CONNECT_STRING + dsn
35+ else :
36+ self ._dsn = dsn
37+ else :
38+ self ._dsn = CONNECT_STRING
3239 print ("Using DSN: '%s'." % self ._dsn )
3340
3441 # only import pyODBC if running tests (vs. for instance only loading test data in ES)
@@ -120,8 +127,8 @@ def _catalog_tables(self, no_table_type_as=""):
120127 res = curs .tables ("" , "%" , "" , no_table_type_as ).fetchall ()
121128 self .assertEqual (len (res ), 1 )
122129 for i in range (0 ,10 ):
123- self .assertEqual (res [0 ][i ], None if i else CATALOG )
124- #self.assertEqual(res, [tuple([CATALOG ] + [None for i in range(9)])]) # XXX?
130+ self .assertEqual (res [0 ][i ], None if i else self . _catalog )
131+ #self.assertEqual(res, [tuple([self._catalog ] + [None for i in range(9)])]) # XXX?
125132
126133 # enumerate table types
127134 res = curs .tables ("" , "" , "" , "%" ).fetchall ()
@@ -146,11 +153,12 @@ def _catalog_columns(self, use_catalog=False, use_surrogate=True):
146153 cnxn .autocommit = True
147154 curs = cnxn .cursor ()
148155 if not use_surrogate :
149- res = curs .columns (table = TestData .BATTERS_INDEX , catalog = CATALOG if use_catalog else None ).fetchall ()
156+ res = curs .columns (table = TestData .BATTERS_INDEX , \
157+ catalog = self ._catalog if use_catalog else None ).fetchall ()
150158 else :
151159 if use_catalog :
152160 stmt = "SYS COLUMNS CATALOG '%s' TABLE LIKE '%s' ESCAPE '\\ ' LIKE '%%' ESCAPE '\\ '" % \
153- (CATALOG , TestData .BATTERS_INDEX )
161+ (self . _catalog , TestData .BATTERS_INDEX )
154162 else :
155163 stmt = "SYS COLUMNS TABLE LIKE '%s' ESCAPE '\\ ' LIKE '%%' ESCAPE '\\ '" % TestData .BATTERS_INDEX
156164 res = curs .execute (stmt )
@@ -207,6 +215,8 @@ def _type_to_instance(self, data_type, data_val):
207215 instance = float (data_val )
208216 elif data_type == "float" :
209217 instance = float (data_val .strip ("fF" ))
218+ # reduce precision, py's float is a double
219+ instance = ctypes .c_float (instance ).value
210220 elif data_type in ["datetime" , "date" , "time" ]:
211221 fmt = "%H:%M:%S"
212222 fmt = "%Y-%m-%dT" + fmt
@@ -257,32 +267,65 @@ def _proto_tests(self):
257267 for t in tests :
258268 (query , col_name , data_type , data_val , cli_val , disp_size ) = t
259269 # print("T: %s, %s, %s, %s, %s, %s" % (query, col_name, data_type, data_val, cli_val, disp_size))
260- with cnxn .execute (query ) as curs :
261- self .assertEqual (curs .rowcount , 1 )
262- res = curs .fetchone ()[0 ]
263-
264- if data_val != cli_val : # INTERVAL tests
265- assert (query .lower ().startswith ("select interval" ))
266- # extract the literal value (`INTERVAL -'1 1' -> `-1 1``)
267- expect = re .match ("[^-]*(-?\s*'[^']*').*" , query ).groups ()[0 ]
268- expect = expect .replace ("'" , "" )
269- # filter out tests with fractional seconds:
270- # https://github.com/elastic/elasticsearch/issues/41635
271- if re .search ("\d*\.\d+" , expect ):
272- continue
273- else : # non-INTERVAL tests
274- assert (data_type .lower () == data_type )
275- # Change the value read in the tests to type and format of the result expected to be
276- # returned by driver.
277- expect = self ._type_to_instance (data_type , data_val )
278-
279- self .assertEqual (res , expect )
270+
271+ if data_val != cli_val : # INTERVAL tests
272+ assert (query .lower ().startswith ("select interval" ))
273+ # extract the literal value (`INTERVAL -'1 1' -> `-1 1``)
274+ expect = re .match ("[^-]*(-?\s*'[^']*').*" , query ).groups ()[0 ]
275+ expect = expect .replace ("'" , "" )
276+ # filter out tests with fractional seconds:
277+ # https://github.com/elastic/elasticsearch/issues/41635
278+ if re .search ("\d*\.\d+" , expect ):
279+ continue
280+ # intervals not supported as params; PyODBC has no interval type support
281+ # https://github.com/elastic/elasticsearch/issues/45915
282+ params = []
283+ else : # non-INTERVAL tests
284+ assert (data_type .lower () == data_type )
285+ # Change the value read in the tests to type and format of the result expected to be
286+ # returned by driver.
287+ expect = self ._type_to_instance (data_type , data_val )
288+
289+ if data_type .lower () == "null" :
290+ query += " WHERE ? IS NULL"
291+ params = [expect ]
292+ else :
293+ if data_type .lower () == "time" :
294+ if col_name .find ("+" ) <= 0 :
295+ # ODBC's TIME_STRUCT lacks fractional component -> strip it away
296+ col_name = re .sub (r"(\d{2})\.\d+" , "\\ 1" , col_name )
297+ query += " WHERE %s = ?" % col_name
298+ params = [expect ]
299+ else : # it's a time with offset
300+ # TIE_STRUCT lacks offset component -> perform the simple SELECT
301+ params = []
302+ else :
303+ query += " WHERE %s = ?" % col_name
304+ params = [expect ]
305+ # print("Query: %s" % query)
306+
307+ last_ex = None
308+ with cnxn .execute (query , * params ) as curs :
309+ try :
310+ self .assertEqual (curs .rowcount , 1 )
311+ res = curs .fetchone ()[0 ]
312+ if data_type == "float" :
313+ # PyODBC will fetch a REAL/float as a double => reduce precision
314+ res = ctypes .c_float (res ).value
315+ self .assertEqual (res , expect )
316+ except Exception as e :
317+ print (e )
318+ last_ex = e
319+
320+ if last_ex :
321+ raise last_ex
322+
280323 finally :
281324 cnxn .clear_output_converters ()
282325
283326 def perform (self ):
284327 self ._check_info (self ._pyodbc .SQL_USER_NAME , UID )
285- self ._check_info (self ._pyodbc .SQL_DATABASE_NAME , CATALOG )
328+ self ._check_info (self ._pyodbc .SQL_DATABASE_NAME , self . _catalog )
286329
287330 # simulate catalog querying as apps do in ES/GH#40775 do
288331 self ._catalog_tables (no_table_type_as = "" )
0 commit comments