Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

this gonna take long... #29

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 62 additions & 17 deletions pypyodbc.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
paramstyle = 'qmark'
threadsafety = 1
version = '1.3.0'
lowercase=True
lowercase = False

DEBUG = 0
# Comment out all "if DEBUG:" statements like below for production
Expand Down Expand Up @@ -475,6 +475,7 @@ def __init__(self, error_code, error_desc):
create_buffer = ctypes.create_string_buffer
wchar_pointer = ctypes.c_wchar_p
UCS_buf = lambda s: s

def UCS_dec(buffer):
i = 0
uchars = []
Expand All @@ -485,21 +486,54 @@ def UCS_dec(buffer):
uchars.append(uchar)
i += ucs_length
return ''.join(uchars)

def UTF16_BE_dec(buffer):
i = 0
uchars = []
while True:
# TODO: verify that this condition correctly identifies
# a surrogate pair in UTF-16 BE
if ord(buffer.raw[i+1]) & 0xd0 == 0xd0:
n = 2
else:
n = 1
uchar = buffer.raw[i:i + n * ucs_length].decode(odbc_decoding)
if uchar == unicode('\x00'):
break
uchars.append(uchar)
i += n * ucs_length
return ''.join(uchars)

from_buffer_u = lambda buffer: buffer.value

# This is the common case on Linux, which uses wide Python build together with
# the default unixODBC without the "-DSQL_WCHART_CONVERT" CFLAGS.
if sys.platform not in ('win32','cli'):
if UNICODE_SIZE >= SQLWCHAR_SIZE:
if sys.platform not in ('win32','cli') and UNICODE_SIZE != SQLWCHAR_SIZE:
if UNICODE_SIZE > SQLWCHAR_SIZE:
# We can only use unicode buffer if the size of wchar_t (UNICODE_SIZE) is
# the same as the size expected by the driver manager (SQLWCHAR_SIZE).
create_buffer_u = create_buffer
chars_to_bytes = lambda chars: chars * SQLWCHAR_SIZE
def create_buffer_u(init_or_size, *size_if_init):
if isinstance(init_or_size, basestring):
if size_if_init:
return create_buffer(init_or_size, chars_to_bytes(size_if_init[0]))
else:
return create_buffer(init_or_size)
else:
return create_buffer(chars_to_bytes(init_or_size))
wchar_pointer = ctypes.c_char_p

def UCS_buf(s):
return s.encode(odbc_encoding)
# c_char_p adds a single NUL-terminating byte because it assumes its argument is being
# passed to a function expecting a single NUL byte. But these functions actually take an
# array of two-byte integers, and so expect two NUL bytes' termination.
return (s + u'\x00').encode(odbc_encoding)

if odbc_encoding == 'utf_16':
from_buffer_u = UTF16_BE_dec
else:
from_buffer_u = UCS_dec

from_buffer_u = UCS_dec

# Exoteric case, don't really care.
elif UNICODE_SIZE < SQLWCHAR_SIZE:
Expand Down Expand Up @@ -564,8 +598,13 @@ def UCS_buf(s):
def dttm_cvt(x):
if py_v3:
x = x.decode('ascii')
if x == '': return None
else: return datetime.datetime(int(x[0:4]),int(x[5:7]),int(x[8:10]),int(x[10:13]),int(x[14:16]),int(x[17:19]),int(x[20:].ljust(6,'0')))
if x == '':
return None
else:
try:
return datetime.datetime(int(x[0:4]),int(x[5:7]),int(x[8:10]),int(x[10:13]),int(x[14:16]),int(x[17:19]),int(x[20:].ljust(6,'0')))
except ValueError:
return datetime.datetime(int("20" + x[6:8]),int(x[0:2]),int(x[3:5]))

def tm_cvt(x):
if py_v3:
Expand Down Expand Up @@ -930,7 +969,7 @@ def ctrl_err(ht, h, val_ret, ansi):
else:
raw_s = str_8b
else:
state = create_buffer_u(22)
state = create_buffer_u(24)
Message = create_buffer_u(1024*4)
ODBC_func = ODBC_API.SQLGetDiagRecW
raw_s = unicode
Expand All @@ -955,7 +994,7 @@ def ctrl_err(ht, h, val_ret, ansi):
raise IntegrityError(state,err_text)
elif state == raw_s('0A000'):
raise NotSupportedError(state,err_text)
elif state in (raw_s('HYT00'),raw_s('HYT01')):
elif state in (raw_s('HYT00'),raw_s('HYT01'),raw_s('01000')):
raise OperationalError(state,err_text)
elif state[:2] in (raw_s('IM'),raw_s('HY')):
raise Error(state,err_text)
Expand Down Expand Up @@ -1017,8 +1056,6 @@ def AllocateEnv():

def TupleRow(cursor):
"""Normal tuple with added attribute `cursor_description`, as in pyodbc.

This is the default.
"""
class Row(tuple):
cursor_description = cursor.description
Expand All @@ -1040,7 +1077,9 @@ def __getitem__(self, field):


def NamedTupleRow(cursor):
"""Named tuple to allow attribute lookup by name.
"""Named tuple to allow attribute lookup by name, as in pyodbc.

This is the default.

Requires py2.6 or above.
"""
Expand Down Expand Up @@ -1152,7 +1191,7 @@ def __init__(self, conx, row_type_callable=None):
self.stmt_h = ctypes.c_void_p()
self.connection = conx
self.ansi = conx.ansi
self.row_type_callable = row_type_callable or TupleRow
self.row_type_callable = row_type_callable or NamedTupleRow
self.statement = None
self._last_param_types = None
self._ParamBufferList = []
Expand Down Expand Up @@ -1755,7 +1794,7 @@ def _UpdateDesc(self):
if ret != SQL_SUCCESS:
check_success(self, ret)

col_name = Cname.value
col_name = from_buffer_u(Cname)
if lowercase:
col_name = col_name.lower()
#(name, type_code, display_size,
Expand All @@ -1766,11 +1805,12 @@ def _UpdateDesc(self):

if len(ColDescr) > 0:
self.description = ColDescr
# Create the row type before fetching.
self._row_type = self.row_type_callable(self)
else:
self.description = None
self._CreateColBuf()

# Create the row type before fetching.
self._row_type = self.row_type_callable(self)


def _NumOfRows(self):
Expand Down Expand Up @@ -2522,6 +2562,11 @@ def cursor(self, row_type_callable=None):
# self._cursors.append(cur)
return cur

def execute(self, sql, *args, **kwargs):
cur = self.cursor(row_type_callable=kwargs.pop('row_type_callable', None))
cur.execute(sql, *args, **kwargs)
return cur

def update_db_special_info(self):
for sql_type in (
SQL_TYPE_TIMESTAMP,
Expand Down