upgraded pypyodbc.py

This commit is contained in:
mdipierro
2014-09-14 16:41:47 -05:00
parent 0120ad5b95
commit f0461ae84a
2 changed files with 206 additions and 97 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.9.9-stable+timestamp.2014.09.14.16.37.05 Version 2.9.9-stable+timestamp.2014.09.14.16.41.44
+205 -96
View File
@@ -4,7 +4,7 @@
# The MIT License (MIT) # The MIT License (MIT)
# #
# Copyright (c) 2013 Henry Zhou <jiangwen365@gmail.com> and PyPyODBC contributors # Copyright (c) 2014 Henry Zhou <jiangwen365@gmail.com> and PyPyODBC contributors
# Copyright (c) 2004 Michele Petrazzo # Copyright (c) 2004 Michele Petrazzo
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
@@ -16,7 +16,7 @@
# of the Software. # of the Software.
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO #EVENT SHALL THE # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE. # DEALINGS IN THE SOFTWARE.
@@ -25,7 +25,7 @@ pooling = True
apilevel = '2.0' apilevel = '2.0'
paramstyle = 'qmark' paramstyle = 'qmark'
threadsafety = 1 threadsafety = 1
version = '1.2.0' version = '1.3.0'
lowercase=True lowercase=True
DEBUG = 0 DEBUG = 0
@@ -592,38 +592,38 @@ if sys.platform == 'cli':
# http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.sdk_12.5.1.aseodbc/html/aseodbc/CACFDIGH.htm # http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.sdk_12.5.1.aseodbc/html/aseodbc/CACFDIGH.htm
SQL_data_type_dict = { \ SQL_data_type_dict = { \
#SQL Data TYPE 0.Python Data Type 1.Default Output Converter 2.Buffer Type 3.Buffer Allocator 4.Default Buffer Size #SQL Data TYPE 0.Python Data Type 1.Default Output Converter 2.Buffer Type 3.Buffer Allocator 4.Default Size 5.Variable Length
SQL_TYPE_NULL : (None, lambda x: None, SQL_C_CHAR, create_buffer, 2 ), SQL_TYPE_NULL : (None, lambda x: None, SQL_C_CHAR, create_buffer, 2 , False ),
SQL_CHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ), SQL_CHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 , False ),
SQL_NUMERIC : (Decimal, Decimal_cvt, SQL_C_CHAR, create_buffer, 150 ), SQL_NUMERIC : (Decimal, Decimal_cvt, SQL_C_CHAR, create_buffer, 150 , False ),
SQL_DECIMAL : (Decimal, Decimal_cvt, SQL_C_CHAR, create_buffer, 150 ), SQL_DECIMAL : (Decimal, Decimal_cvt, SQL_C_CHAR, create_buffer, 150 , False ),
SQL_INTEGER : (int, int, SQL_C_CHAR, create_buffer, 150 ), SQL_INTEGER : (int, int, SQL_C_CHAR, create_buffer, 150 , False ),
SQL_SMALLINT : (int, int, SQL_C_CHAR, create_buffer, 150 ), SQL_SMALLINT : (int, int, SQL_C_CHAR, create_buffer, 150 , False ),
SQL_FLOAT : (float, float, SQL_C_CHAR, create_buffer, 150 ), SQL_FLOAT : (float, float, SQL_C_CHAR, create_buffer, 150 , False ),
SQL_REAL : (float, float, SQL_C_CHAR, create_buffer, 150 ), SQL_REAL : (float, float, SQL_C_CHAR, create_buffer, 150 , False ),
SQL_DOUBLE : (float, float, SQL_C_CHAR, create_buffer, 200 ), SQL_DOUBLE : (float, float, SQL_C_CHAR, create_buffer, 200 , False ),
SQL_DATE : (datetime.date, dt_cvt, SQL_C_CHAR , create_buffer, 30 ), SQL_DATE : (datetime.date, dt_cvt, SQL_C_CHAR, create_buffer, 30 , False ),
SQL_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), SQL_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 , False ),
SQL_SS_TIME2 : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), SQL_SS_TIME2 : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 , False ),
SQL_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ), SQL_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 , False ),
SQL_VARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ), SQL_VARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 , False ),
SQL_LONGVARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 20500 ), SQL_LONGVARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 20500 , True ),
SQL_BINARY : (bytearray, bytearray_cvt, SQL_C_BINARY, create_buffer, 5120 ), SQL_BINARY : (bytearray, bytearray_cvt, SQL_C_BINARY, create_buffer, 5120 , True ),
SQL_VARBINARY : (bytearray, bytearray_cvt, SQL_C_BINARY, create_buffer, 5120 ), SQL_VARBINARY : (bytearray, bytearray_cvt, SQL_C_BINARY, create_buffer, 5120 , True ),
SQL_LONGVARBINARY : (bytearray, bytearray_cvt, SQL_C_BINARY, create_buffer, 20500 ), SQL_LONGVARBINARY : (bytearray, bytearray_cvt, SQL_C_BINARY, create_buffer, 20500 , True ),
SQL_BIGINT : (long, long, SQL_C_CHAR, create_buffer, 150 ), SQL_BIGINT : (long, long, SQL_C_CHAR, create_buffer, 150 , False ),
SQL_TINYINT : (int, int, SQL_C_CHAR, create_buffer, 150 ), SQL_TINYINT : (int, int, SQL_C_CHAR, create_buffer, 150 , False ),
SQL_BIT : (bool, lambda x:x == BYTE_1, SQL_C_CHAR, create_buffer, 2 ), SQL_BIT : (bool, lambda x:x == BYTE_1, SQL_C_CHAR, create_buffer, 2 , False ),
SQL_WCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ), SQL_WCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 , False ),
SQL_WVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ), SQL_WVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 , False ),
SQL_GUID : (str, str, SQL_C_CHAR, create_buffer, 50 ), SQL_GUID : (str, str, SQL_C_CHAR, create_buffer, 2048 , False ),
SQL_WLONGVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 20500 ), SQL_WLONGVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 20500 , True ),
SQL_TYPE_DATE : (datetime.date, dt_cvt, SQL_C_CHAR, create_buffer, 30 ), SQL_TYPE_DATE : (datetime.date, dt_cvt, SQL_C_CHAR, create_buffer, 30 , False ),
SQL_TYPE_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), SQL_TYPE_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 , False ),
SQL_TYPE_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ), SQL_TYPE_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 , False ),
SQL_SS_VARIANT : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ), SQL_SS_VARIANT : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 , True ),
SQL_SS_XML : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 20500 ), SQL_SS_XML : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 20500 , True ),
SQL_SS_UDT : (bytearray, bytearray_cvt, SQL_C_BINARY, create_buffer, 5120 ), SQL_SS_UDT : (bytearray, bytearray_cvt, SQL_C_BINARY, create_buffer, 5120 , True ),
} }
@@ -645,6 +645,7 @@ SQLRETURN -> ctypes.c_short
funcs_with_ret = [ funcs_with_ret = [
"SQLAllocHandle", "SQLAllocHandle",
"SQLBindParameter", "SQLBindParameter",
"SQLBindCol",
"SQLCloseCursor", "SQLCloseCursor",
"SQLColAttribute", "SQLColAttribute",
"SQLColumns", "SQLColumns",
@@ -1175,7 +1176,9 @@ class Cursor:
"""prepare a query""" """prepare a query"""
#self._free_results(FREE_STATEMENT) #self._free_results(FREE_STATEMENT)
if not self.connection:
self.close()
if type(query_string) == unicode: if type(query_string) == unicode:
c_query_string = wchar_pointer(UCS_buf(query_string)) c_query_string = wchar_pointer(UCS_buf(query_string))
ret = ODBC_API.SQLPrepareW(self.stmt_h, c_query_string, len(query_string)) ret = ODBC_API.SQLPrepareW(self.stmt_h, c_query_string, len(query_string))
@@ -1231,6 +1234,8 @@ class Cursor:
def _BindParams(self, param_types, pram_io_list = []): def _BindParams(self, param_types, pram_io_list = []):
"""Create parameter buffers based on param types, and bind them to the statement""" """Create parameter buffers based on param types, and bind them to the statement"""
# Clear the old Parameters # Clear the old Parameters
if not self.connection:
self.close()
#self._free_results(NO_FREE_STATEMENT) #self._free_results(NO_FREE_STATEMENT)
# Get the number of query parameters judged by database. # Get the number of query parameters judged by database.
@@ -1414,7 +1419,7 @@ class Cursor:
dec_num, ADDR(ParameterBuffer), BufferLen,ADDR(LenOrIndBuf)) dec_num, ADDR(ParameterBuffer), BufferLen,ADDR(LenOrIndBuf))
if ret != SQL_SUCCESS: if ret != SQL_SUCCESS:
check_success(self, ret) check_success(self, ret)
# Append the value buffer and the lenth buffer to the array # Append the value buffer and the length buffer to the array
ParamBufferList.append((ParameterBuffer,LenOrIndBuf,sql_type)) ParamBufferList.append((ParameterBuffer,LenOrIndBuf,sql_type))
self._last_param_types = param_types self._last_param_types = param_types
@@ -1426,6 +1431,9 @@ class Cursor:
If parameters are provided, the query would first be prepared, then executed with parameters; If parameters are provided, the query would first be prepared, then executed with parameters;
If parameters are not provided, only th query sting, it would be executed directly If parameters are not provided, only th query sting, it would be executed directly
""" """
if not self.connection:
self.close()
self._free_stmt(SQL_CLOSE) self._free_stmt(SQL_CLOSE)
if params: if params:
# If parameters exist, first prepare the query then executed with parameters # If parameters exist, first prepare the query then executed with parameters
@@ -1549,7 +1557,7 @@ class Cursor:
c_buf_len = len(c_char_buf) c_buf_len = len(c_char_buf)
elif param_types[col_num][0] == 'bi': elif param_types[col_num][0] == 'bi':
c_char_buf = str(param_val) c_char_buf = str_8b(param_val)
c_buf_len = len(c_char_buf) c_buf_len = len(c_char_buf)
else: else:
@@ -1587,6 +1595,8 @@ class Cursor:
def _SQLExecute(self): def _SQLExecute(self):
if not self.connection:
self.close()
ret = SQLExecute(self.stmt_h) ret = SQLExecute(self.stmt_h)
if ret != SQL_SUCCESS: if ret != SQL_SUCCESS:
check_success(self, ret) check_success(self, ret)
@@ -1594,6 +1604,9 @@ class Cursor:
def execdirect(self, query_string): def execdirect(self, query_string):
"""Execute a query directly""" """Execute a query directly"""
if not self.connection:
self.close()
self._free_stmt() self._free_stmt()
self._last_param_types = None self._last_param_types = None
self.statement = None self.statement = None
@@ -1611,6 +1624,8 @@ class Cursor:
def callproc(self, procname, args): def callproc(self, procname, args):
if not self.connection:
self.close()
raise Warning('', 'Still not fully implemented') raise Warning('', 'Still not fully implemented')
self._pram_io_list = [row[4] for row in self.procedurecolumns(procedure = procname).fetchall() if row[4] not in (SQL_RESULT_COL, SQL_RETURN_VALUE)] self._pram_io_list = [row[4] for row in self.procedurecolumns(procedure = procname).fetchall() if row[4] not in (SQL_RESULT_COL, SQL_RETURN_VALUE)]
@@ -1637,6 +1652,9 @@ class Cursor:
def executemany(self, query_string, params_list = [None]): def executemany(self, query_string, params_list = [None]):
if not self.connection:
self.close()
for params in params_list: for params in params_list:
self.execute(query_string, params, many_mode = True) self.execute(query_string, params, many_mode = True)
self._NumOfRows() self._NumOfRows()
@@ -1647,28 +1665,38 @@ class Cursor:
def _CreateColBuf(self): def _CreateColBuf(self):
if not self.connection:
self.close()
self._free_stmt(SQL_UNBIND) self._free_stmt(SQL_UNBIND)
NOC = self._NumOfCols() NOC = self._NumOfCols()
self._ColBufferList = [] self._ColBufferList = []
bind_data = True
for col_num in range(NOC): for col_num in range(NOC):
col_name = self.description[col_num][0] col_name = self.description[col_num][0]
col_size = self.description[col_num][2]
col_sql_data_type = self._ColTypeCodeList[col_num] col_sql_data_type = self._ColTypeCodeList[col_num]
target_type = SQL_data_type_dict[col_sql_data_type][2]
dynamic_length = SQL_data_type_dict[col_sql_data_type][5]
# set default size base on the column's sql data type # set default size base on the column's sql data type
total_buf_len = SQL_data_type_dict[col_sql_data_type][4] total_buf_len = SQL_data_type_dict[col_sql_data_type][4]
# over-write if there's preset size value for "large columns"
if total_buf_len >= 20500: # over-write if there's pre-set size value for "large columns"
if total_buf_len > 20500:
total_buf_len = self._outputsize.get(None,total_buf_len) total_buf_len = self._outputsize.get(None,total_buf_len)
# over-write if there's preset size value for the "col_num" column # over-write if there's pre-set size value for the "col_num" column
total_buf_len = self._outputsize.get(col_num, total_buf_len) total_buf_len = self._outputsize.get(col_num, total_buf_len)
# if the size of the buffer is very long, do not bind
# because a large buffer decrease performance, and sometimes you only get a NULL value.
# in that case use sqlgetdata instead.
if col_size >= 1024:
dynamic_length = True
alloc_buffer = SQL_data_type_dict[col_sql_data_type][3](total_buf_len) alloc_buffer = SQL_data_type_dict[col_sql_data_type][3](total_buf_len)
used_buf_len = c_ssize_t() used_buf_len = c_ssize_t()
target_type = SQL_data_type_dict[col_sql_data_type][2]
force_unicode = self.connection.unicode_results force_unicode = self.connection.unicode_results
if force_unicode and col_sql_data_type in (SQL_CHAR,SQL_VARCHAR,SQL_LONGVARCHAR): if force_unicode and col_sql_data_type in (SQL_CHAR,SQL_VARCHAR,SQL_LONGVARCHAR):
@@ -1676,14 +1704,22 @@ class Cursor:
alloc_buffer = create_buffer_u(total_buf_len) alloc_buffer = create_buffer_u(total_buf_len)
buf_cvt_func = self.connection.output_converter[self._ColTypeCodeList[col_num]] buf_cvt_func = self.connection.output_converter[self._ColTypeCodeList[col_num]]
ADDR(alloc_buffer)
ADDR(used_buf_len) if bind_data:
self._ColBufferList.append([col_name, target_type, used_buf_len, ADDR(used_buf_len), alloc_buffer, ADDR(alloc_buffer), total_buf_len, buf_cvt_func]) if dynamic_length:
bind_data = False
self._ColBufferList.append([col_name, target_type, used_buf_len, ADDR(used_buf_len), alloc_buffer, ADDR(alloc_buffer), total_buf_len, buf_cvt_func, bind_data])
if bind_data:
ret = ODBC_API.SQLBindCol(self.stmt_h, col_num + 1, target_type, ADDR(alloc_buffer), total_buf_len, ADDR(used_buf_len))
if ret != SQL_SUCCESS:
check_success(self, ret)
def _UpdateDesc(self): def _UpdateDesc(self):
"Get the information of (name, type_code, display_size, internal_size, col_precision, scale, null_ok)" "Get the information of (name, type_code, display_size, internal_size, col_precision, scale, null_ok)"
if not self.connection:
self.close()
force_unicode = self.connection.unicode_results force_unicode = self.connection.unicode_results
if force_unicode: if force_unicode:
Cname = create_buffer_u(1024) Cname = create_buffer_u(1024)
@@ -1739,6 +1775,9 @@ class Cursor:
def _NumOfRows(self): def _NumOfRows(self):
"""Get the number of rows""" """Get the number of rows"""
if not self.connection:
self.close()
NOR = c_ssize_t() NOR = c_ssize_t()
ret = SQLRowCount(self.stmt_h, ADDR(NOR)) ret = SQLRowCount(self.stmt_h, ADDR(NOR))
if ret != SQL_SUCCESS: if ret != SQL_SUCCESS:
@@ -1749,6 +1788,9 @@ class Cursor:
def _NumOfCols(self): def _NumOfCols(self):
"""Get the number of cols""" """Get the number of cols"""
if not self.connection:
self.close()
NOC = c_short() NOC = c_short()
ret = SQLNumResultCols(self.stmt_h, ADDR(NOC)) ret = SQLNumResultCols(self.stmt_h, ADDR(NOC))
if ret != SQL_SUCCESS: if ret != SQL_SUCCESS:
@@ -1757,6 +1799,9 @@ class Cursor:
def fetchall(self): def fetchall(self):
if not self.connection:
self.close()
rows = [] rows = []
while True: while True:
row = self.fetchone() row = self.fetchone()
@@ -1767,6 +1812,9 @@ class Cursor:
def fetchmany(self, num = None): def fetchmany(self, num = None):
if not self.connection:
self.close()
if num is None: if num is None:
num = self.arraysize num = self.arraysize
rows = [] rows = []
@@ -1780,74 +1828,83 @@ class Cursor:
def fetchone(self): def fetchone(self):
if not self.connection:
self.close()
ret = SQLFetch(self.stmt_h) ret = SQLFetch(self.stmt_h)
if ret == SQL_SUCCESS:
if ret in (SQL_SUCCESS,SQL_SUCCESS_WITH_INFO):
'''Bind buffers for the record set columns''' '''Bind buffers for the record set columns'''
value_list = [] value_list = []
col_num = 1 col_num = 1
for col_name, target_type, used_buf_len, ADDR_used_buf_len, alloc_buffer, ADDR_alloc_buffer, total_buf_len, buf_cvt_func in self._ColBufferList: for col_name, target_type, used_buf_len, ADDR_used_buf_len, alloc_buffer, ADDR_alloc_buffer, total_buf_len, buf_cvt_func, bind_data in self._ColBufferList:
raw_data_parts = []
blocks = []
while 1: while 1:
ret = SQLGetData(self.stmt_h, col_num, target_type, ADDR_alloc_buffer, total_buf_len, ADDR_used_buf_len) if bind_data:
ret = SQL_SUCCESS
else:
ret = SQLGetData(self.stmt_h, col_num, target_type, ADDR_alloc_buffer, total_buf_len, ADDR_used_buf_len)
if ret == SQL_SUCCESS: if ret == SQL_SUCCESS:
if used_buf_len.value == SQL_NULL_DATA: if used_buf_len.value == SQL_NULL_DATA:
value_list.append(None) value_list.append(None)
else: else:
if blocks == []: if raw_data_parts == []:
# Means no previous data, no need to combine
if target_type == SQL_C_BINARY: if target_type == SQL_C_BINARY:
value_list.append(buf_cvt_func(alloc_buffer.raw[:used_buf_len.value])) value_list.append(buf_cvt_func(alloc_buffer.raw[:used_buf_len.value]))
elif target_type == SQL_C_WCHAR: elif target_type == SQL_C_WCHAR:
value_list.append(buf_cvt_func(from_buffer_u(alloc_buffer))) value_list.append(buf_cvt_func(from_buffer_u(alloc_buffer)))
else: else:
#print col_name, target_type, alloc_buffer.value
value_list.append(buf_cvt_func(alloc_buffer.value)) value_list.append(buf_cvt_func(alloc_buffer.value))
else: else:
# There are previous fetched raw data to combine
if target_type == SQL_C_BINARY: if target_type == SQL_C_BINARY:
blocks.append(alloc_buffer.raw[:used_buf_len.value]) raw_data_parts.append(alloc_buffer.raw[:used_buf_len.value])
elif target_type == SQL_C_WCHAR: elif target_type == SQL_C_WCHAR:
blocks.append(from_buffer_u(alloc_buffer)) raw_data_parts.append(from_buffer_u(alloc_buffer))
else: else:
#print col_name, target_type, alloc_buffer.value raw_data_parts.append(alloc_buffer.value)
blocks.append(alloc_buffer.value)
break break
elif ret == SQL_SUCCESS_WITH_INFO: elif ret == SQL_SUCCESS_WITH_INFO:
# Means the data is only partial
if target_type == SQL_C_BINARY: if target_type == SQL_C_BINARY:
blocks.append(alloc_buffer.raw) raw_data_parts.append(alloc_buffer.raw)
else: else:
blocks.append(alloc_buffer.value) raw_data_parts.append(alloc_buffer.value)
elif ret == SQL_NO_DATA: elif ret == SQL_NO_DATA:
# Means all data has been transmitted
break break
else: else:
check_success(self, ret) check_success(self, ret)
if blocks != []: if raw_data_parts != []:
if py_v3: if py_v3:
if target_type != SQL_C_BINARY: if target_type != SQL_C_BINARY:
raw_value = ''.join(blocks) raw_value = ''.join(raw_data_parts)
else: else:
raw_value = BLANK_BYTE.join(blocks) raw_value = BLANK_BYTE.join(raw_data_parts)
else: else:
raw_value = ''.join(blocks) raw_value = ''.join(raw_data_parts)
value_list.append(buf_cvt_func(raw_value)) value_list.append(buf_cvt_func(raw_value))
col_num += 1 col_num += 1
return self._row_type(value_list) return self._row_type(value_list)
else: else:
if ret == SQL_NO_DATA_FOUND: if ret == SQL_NO_DATA_FOUND:
return None return None
else: else:
check_success(self, ret) check_success(self, ret)
def __next__(self): def __next__(self):
self.next() return self.next()
def next(self): def next(self):
row = self.fetchone() row = self.fetchone()
if row is None: if row is None:
raise(StopIteration) raise(StopIteration)
@@ -1858,6 +1915,9 @@ class Cursor:
def skip(self, count = 0): def skip(self, count = 0):
if not self.connection:
self.close()
for i in range(count): for i in range(count):
ret = ODBC_API.SQLFetchScroll(self.stmt_h, SQL_FETCH_NEXT, 0) ret = ODBC_API.SQLFetchScroll(self.stmt_h, SQL_FETCH_NEXT, 0)
if ret != SQL_SUCCESS: if ret != SQL_SUCCESS:
@@ -1867,6 +1927,9 @@ class Cursor:
def nextset(self): def nextset(self):
if not self.connection:
self.close()
ret = ODBC_API.SQLMoreResults(self.stmt_h) ret = ODBC_API.SQLMoreResults(self.stmt_h)
if ret not in (SQL_SUCCESS, SQL_NO_DATA): if ret not in (SQL_SUCCESS, SQL_NO_DATA):
check_success(self, ret) check_success(self, ret)
@@ -1882,6 +1945,9 @@ class Cursor:
def _free_stmt(self, free_type = None): def _free_stmt(self, free_type = None):
if not self.connection:
self.close()
if not self.connection.connected: if not self.connection.connected:
raise ProgrammingError('HY000','Attempt to use a closed connection.') raise ProgrammingError('HY000','Attempt to use a closed connection.')
@@ -1903,6 +1969,9 @@ class Cursor:
def getTypeInfo(self, sqlType = None): def getTypeInfo(self, sqlType = None):
if not self.connection:
self.close()
if sqlType is None: if sqlType is None:
type = SQL_ALL_TYPES type = SQL_ALL_TYPES
else: else:
@@ -1917,6 +1986,9 @@ class Cursor:
def tables(self, table=None, catalog=None, schema=None, tableType=None): def tables(self, table=None, catalog=None, schema=None, tableType=None):
"""Return a list with all tables""" """Return a list with all tables"""
if not self.connection:
self.close()
l_catalog = l_schema = l_table = l_tableType = 0 l_catalog = l_schema = l_table = l_tableType = 0
if unicode in [type(x) for x in (table, catalog, schema,tableType)]: if unicode in [type(x) for x in (table, catalog, schema,tableType)]:
@@ -1961,7 +2033,10 @@ class Cursor:
def columns(self, table=None, catalog=None, schema=None, column=None): def columns(self, table=None, catalog=None, schema=None, column=None):
"""Return a list with all columns""" """Return a list with all columns"""
if not self.connection:
self.close()
l_catalog = l_schema = l_table = l_column = 0 l_catalog = l_schema = l_table = l_column = 0
if unicode in [type(x) for x in (table, catalog, schema,column)]: if unicode in [type(x) for x in (table, catalog, schema,column)]:
@@ -2004,6 +2079,9 @@ class Cursor:
def primaryKeys(self, table=None, catalog=None, schema=None): def primaryKeys(self, table=None, catalog=None, schema=None):
if not self.connection:
self.close()
l_catalog = l_schema = l_table = 0 l_catalog = l_schema = l_table = 0
if unicode in [type(x) for x in (table, catalog, schema)]: if unicode in [type(x) for x in (table, catalog, schema)]:
@@ -2044,6 +2122,9 @@ class Cursor:
def foreignKeys(self, table=None, catalog=None, schema=None, foreignTable=None, foreignCatalog=None, foreignSchema=None): def foreignKeys(self, table=None, catalog=None, schema=None, foreignTable=None, foreignCatalog=None, foreignSchema=None):
if not self.connection:
self.close()
l_catalog = l_schema = l_table = l_foreignTable = l_foreignCatalog = l_foreignSchema = 0 l_catalog = l_schema = l_table = l_foreignTable = l_foreignCatalog = l_foreignSchema = 0
if unicode in [type(x) for x in (table, catalog, schema,foreignTable,foreignCatalog,foreignSchema)]: if unicode in [type(x) for x in (table, catalog, schema,foreignTable,foreignCatalog,foreignSchema)]:
@@ -2092,6 +2173,9 @@ class Cursor:
def procedurecolumns(self, procedure=None, catalog=None, schema=None, column=None): def procedurecolumns(self, procedure=None, catalog=None, schema=None, column=None):
if not self.connection:
self.close()
l_catalog = l_schema = l_procedure = l_column = 0 l_catalog = l_schema = l_procedure = l_column = 0
if unicode in [type(x) for x in (procedure, catalog, schema,column)]: if unicode in [type(x) for x in (procedure, catalog, schema,column)]:
string_p = lambda x:wchar_pointer(UCS_buf(x)) string_p = lambda x:wchar_pointer(UCS_buf(x))
@@ -2132,6 +2216,9 @@ class Cursor:
def procedures(self, procedure=None, catalog=None, schema=None): def procedures(self, procedure=None, catalog=None, schema=None):
if not self.connection:
self.close()
l_catalog = l_schema = l_procedure = 0 l_catalog = l_schema = l_procedure = 0
if unicode in [type(x) for x in (procedure, catalog, schema)]: if unicode in [type(x) for x in (procedure, catalog, schema)]:
@@ -2170,6 +2257,9 @@ class Cursor:
def statistics(self, table, catalog=None, schema=None, unique=False, quick=True): def statistics(self, table, catalog=None, schema=None, unique=False, quick=True):
if not self.connection:
self.close()
l_table = l_catalog = l_schema = 0 l_table = l_catalog = l_schema = 0
if unicode in [type(x) for x in (table, catalog, schema)]: if unicode in [type(x) for x in (table, catalog, schema)]:
@@ -2217,15 +2307,23 @@ class Cursor:
def commit(self): def commit(self):
if not self.connection:
self.close()
self.connection.commit() self.connection.commit()
def rollback(self): def rollback(self):
if not self.connection:
self.close()
self.connection.rollback() self.connection.rollback()
def setoutputsize(self, size, column = None): def setoutputsize(self, size, column = None):
if not self.connection:
self.close()
self._outputsize[column] = size self._outputsize[column] = size
def setinputsizes(self, sizes): def setinputsizes(self, sizes):
if not self.connection:
self.close()
self._inputsizers = [size for size in sizes] self._inputsizers = [size for size in sizes]
@@ -2234,35 +2332,31 @@ class Cursor:
# ret = ODBC_API.SQLCloseCursor(self.stmt_h) # ret = ODBC_API.SQLCloseCursor(self.stmt_h)
# check_success(self, ret) # check_success(self, ret)
# #
ret = ODBC_API.SQLFreeStmt(self.stmt_h, SQL_CLOSE) if self.connection.connected:
check_success(self, ret) ret = ODBC_API.SQLFreeStmt(self.stmt_h, SQL_CLOSE)
check_success(self, ret)
ret = ODBC_API.SQLFreeStmt(self.stmt_h, SQL_UNBIND) ret = ODBC_API.SQLFreeStmt(self.stmt_h, SQL_UNBIND)
check_success(self, ret) check_success(self, ret)
ret = ODBC_API.SQLFreeStmt(self.stmt_h, SQL_RESET_PARAMS) ret = ODBC_API.SQLFreeStmt(self.stmt_h, SQL_RESET_PARAMS)
check_success(self, ret) check_success(self, ret)
ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_STMT, self.stmt_h) ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_STMT, self.stmt_h)
check_success(self, ret) check_success(self, ret)
self.closed = True self.closed = True
def __del__(self): def __del__(self):
if not self.closed: if not self.closed:
#if DEBUG:print 'auto closing cursor: ', self.close()
try:
self.close()
except:
#if DEBUG:print 'failed'
pass
else:
#if DEBUG:print 'succeed'
pass
def __exit__(self, type, value, traceback): def __exit__(self, type, value, traceback):
if not self.connection:
self.close()
if value: if value:
self.rollback() self.rollback()
else: else:
@@ -2291,7 +2385,7 @@ class Connection:
self.autocommit = autocommit self.autocommit = autocommit
self.readonly = False self.readonly = False
self.timeout = 0 self.timeout = 0
self._cursors = [] # self._cursors = []
for key, value in list(kargs.items()): for key, value in list(kargs.items()):
connectString = connectString + key + '=' + value + ';' connectString = connectString + key + '=' + value + ';'
self.connectString = connectString self.connectString = connectString
@@ -2425,7 +2519,7 @@ class Connection:
if not self.connected: if not self.connected:
raise ProgrammingError('HY000','Attempt to use a closed connection.') raise ProgrammingError('HY000','Attempt to use a closed connection.')
cur = Cursor(self, row_type_callable=row_type_callable) cur = Cursor(self, row_type_callable=row_type_callable)
self._cursors.append(cur) # self._cursors.append(cur)
return cur return cur
def update_db_special_info(self): def update_db_special_info(self):
@@ -2436,6 +2530,7 @@ class Connection:
SQL_SS_TIME2, SQL_SS_TIME2,
): ):
cur = Cursor(self) cur = Cursor(self)
try: try:
info_tuple = cur.getTypeInfo(sql_type) info_tuple = cur.getTypeInfo(sql_type)
if info_tuple is not None: if info_tuple is not None:
@@ -2534,10 +2629,10 @@ class Connection:
def close(self): def close(self):
if not self.connected: if not self.connected:
raise ProgrammingError('HY000','Attempt to close a closed connection.') raise ProgrammingError('HY000','Attempt to close a closed connection.')
for cur in self._cursors: # for cur in self._cursors:
if not cur is None: # if not cur is None:
if not cur.closed: # if not cur.closed:
cur.close() # cur.close()
if self.connected: if self.connected:
#if DEBUG:print 'disconnect' #if DEBUG:print 'disconnect'
@@ -2680,4 +2775,18 @@ def dataSources():
ctrl_err(SQL_HANDLE_ENV, shared_env_h, ret) ctrl_err(SQL_HANDLE_ENV, shared_env_h, ret)
else: else:
dsn_list[dsn.value] = desc.value dsn_list[dsn.value] = desc.value
return dsn_list return dsn_list
def monkey_patch_for_gevent():
import functools, gevent
apply_e = gevent.get_hub().threadpool.apply_e
def monkey_patch(func):
@functools.wraps(func)
def wrap(*args, **kwargs):
#if DEBUG:print('%s called with %s %s' % (func, args, kwargs))
return apply_e(Exception, func, args, kwargs)
return wrap
for attr in dir(ODBC_API):
if attr.startswith('SQL') and hasattr(getattr(ODBC_API, attr), 'argtypes'):
setattr(ODBC_API, attr, monkey_patch(getattr(ODBC_API, attr)))