diff --git a/VERSION b/VERSION index 6ae1721b..b87e3bfc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.5-stable+timestamp.2013.03.29.02.07.58 +Version 2.4.5-stable+timestamp.2013.03.29.02.12.57 diff --git a/gluon/admin.py b/gluon/admin.py index ba035e1f..23b3a823 100644 --- a/gluon/admin.py +++ b/gluon/admin.py @@ -373,7 +373,7 @@ def check_new_version(myversion, version_URL): the most up-to-version available """ try: - from urllib import urlopen + from urllib import urlopen version = urlopen(version_URL).read() pversion = parse_version(version) pmyversion = parse_version(myversion) diff --git a/gluon/contenttype.py b/gluon/contenttype.py index 2f43982d..132fc9b2 100644 --- a/gluon/contenttype.py +++ b/gluon/contenttype.py @@ -9,7 +9,7 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) CONTENT_TYPE dictionary created against freedesktop.org' shared mime info database version 1.1. -Deviations from official standards: +Deviations from official standards: - '.md': 'application/x-genesis-rom' --> 'text/x-markdown' - '.png': 'image/x-apple-ios-png' --> 'image/png' Additions: diff --git a/gluon/contrib/login_methods/__init__.py b/gluon/contrib/login_methods/__init__.py index e69de29b..8b137891 100644 --- a/gluon/contrib/login_methods/__init__.py +++ b/gluon/contrib/login_methods/__init__.py @@ -0,0 +1 @@ + diff --git a/gluon/contrib/login_methods/ldap_auth.py b/gluon/contrib/login_methods/ldap_auth.py index 06054df9..46ca4bca 100644 --- a/gluon/contrib/login_methods/ldap_auth.py +++ b/gluon/contrib/login_methods/ldap_auth.py @@ -662,4 +662,3 @@ def ldap_auth(server='ldap', port=None, if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax filterstr = filterstr[1:-1] # parens added again where used return ldap_auth_aux - diff --git a/gluon/contrib/login_methods/linkedin_account.py b/gluon/contrib/login_methods/linkedin_account.py index 4376a628..07cb9a0f 100644 --- a/gluon/contrib/login_methods/linkedin_account.py +++ b/gluon/contrib/login_methods/linkedin_account.py @@ -1,4 +1,3 @@ - #!/usr/bin/env python # -*- coding: utf-8 -*- diff --git a/gluon/contrib/login_methods/oauth20_account.py b/gluon/contrib/login_methods/oauth20_account.py index 1bf0a551..27ff2936 100644 --- a/gluon/contrib/login_methods/oauth20_account.py +++ b/gluon/contrib/login_methods/oauth20_account.py @@ -147,7 +147,7 @@ server for requests. It can be used for the optional"scope" parameters for Face # reuse token until expiration if expires == 0 or expires > time.time(): return current.session.token['access_token'] - + code = current.request.vars.code if code: diff --git a/gluon/contrib/login_methods/openid_auth.py b/gluon/contrib/login_methods/openid_auth.py index 0c763e81..c090ba50 100644 --- a/gluon/contrib/login_methods/openid_auth.py +++ b/gluon/contrib/login_methods/openid_auth.py @@ -135,7 +135,7 @@ class OpenIDAuth(object): Otherwise it's always 'openid' and you may not need it. This should be easy to changed. (Just remove the field of "type" and remove the - "and db.alt_logins.oidtype == type_" + "and db.alt_logins.oidtype == type_" in _find_matched_openid function) """ db = self.db diff --git a/gluon/contrib/pypyodbc.py b/gluon/contrib/pypyodbc.py index addad33b..fc2b2d12 100644 --- a/gluon/contrib/pypyodbc.py +++ b/gluon/contrib/pypyodbc.py @@ -1,2364 +1,2364 @@ -# -*- coding: utf-8 -*- - -# PyPyODBC is develped from RealPyODBC 0.1 beta released in 2004 by Michele Petrazzo. Thanks Michele. - -# The MIT License (MIT) -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the "Software"), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all copies or substantial portions -# of the Software. -# -# 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 -# 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 -# DEALINGS IN THE SOFTWARE. - -import sys, os, datetime, ctypes, threading -from decimal import Decimal - -try: - bytearray -except NameError: - # pre version 2.6 python does not have the bytearray type - bytearray = str - -if not hasattr(ctypes, 'c_ssize_t'): - if ctypes.sizeof(ctypes.c_uint) == ctypes.sizeof(ctypes.c_void_p): - ctypes.c_ssize_t = ctypes.c_int - elif ctypes.sizeof(ctypes.c_ulong) == ctypes.sizeof(ctypes.c_void_p): - ctypes.c_ssize_t = ctypes.c_long - elif ctypes.sizeof(ctypes.c_ulonglong) == ctypes.sizeof(ctypes.c_void_p): - ctypes.c_ssize_t = ctypes.c_longlong - -DEBUG = 0 -# Comment out all "if DEBUG:" statements like below for production -if DEBUG: print 'DEBUGGING' - -pooling = True -lock = threading.Lock() -shared_env_h = None -apilevel = '2.0' -paramstyle = 'qmark' -threadsafety = 1 -version = '0.9.3' -lowercase=True -SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_wchar) - -#determin the size of Py_UNICODE -#sys.maxunicode > 65536 and 'UCS4' or 'UCS2' -UNICODE_SIZE = sys.maxunicode > 65536 and 4 or 2 - - -# Define ODBC constants. They are widly used in ODBC documents and programs -# They are defined in cpp header files: sql.h sqlext.h sqltypes.h sqlucode.h -# and you can get these files from the mingw32-runtime_3.13-1_all.deb package -SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC2, SQL_OV_ODBC3 = 200, 2, 3 -SQL_DRIVER_NOPROMPT = 0 -SQL_ATTR_CONNECTION_POOLING = 201; SQL_CP_ONE_PER_HENV = 2 - -SQL_FETCH_NEXT, SQL_FETCH_FIRST, SQL_FETCH_LAST = 0x01, 0x02, 0x04 -SQL_NULL_HANDLE, SQL_HANDLE_ENV, SQL_HANDLE_DBC, SQL_HANDLE_STMT = 0, 1, 2, 3 -SQL_SUCCESS, SQL_SUCCESS_WITH_INFO = 0, 1 -SQL_NO_DATA = 100; SQL_NO_TOTAL = -4 -SQL_ATTR_ACCESS_MODE = SQL_ACCESS_MODE = 101 -SQL_ATTR_AUTOCOMMIT = SQL_AUTOCOMMIT = 102 - -SQL_MODE_DEFAULT = SQL_MODE_READ_WRITE = 0; SQL_MODE_READ_ONLY = 1 -SQL_AUTOCOMMIT_OFF, SQL_AUTOCOMMIT_ON = 0, 1 -SQL_IS_UINTEGER = -5 -SQL_ATTR_LOGIN_TIMEOUT = 103; SQL_ATTR_CONNECTION_TIMEOUT = 113 -SQL_COMMIT, SQL_ROLLBACK = 0, 1 - -SQL_INDEX_UNIQUE,SQL_INDEX_ALL = 0,1 -SQL_QUICK,SQL_ENSURE = 0,1 -SQL_FETCH_NEXT = 1 -SQL_COLUMN_DISPLAY_SIZE = 6 -SQL_INVALID_HANDLE = -2 -SQL_NO_DATA_FOUND = 100; SQL_NULL_DATA = -1; SQL_NTS = -3 -SQL_HANDLE_DESCR = 4 -SQL_TABLE_NAMES = 3 -SQL_PARAM_INPUT = 1; SQL_PARAM_INPUT_OUTPUT = 2 -SQL_PARAM_TYPE_UNKNOWN = 0 -SQL_RESULT_COL = 3 -SQL_PARAM_OUTPUT = 4 -SQL_RETURN_VALUE = 5 -SQL_PARAM_TYPE_DEFAULT = SQL_PARAM_INPUT_OUTPUT - -SQL_RESET_PARAMS = 3 -SQL_UNBIND = 2 -SQL_CLOSE = 0 - -SQL_TYPE_NULL = 0 -SQL_DECIMAL = 3 -SQL_FLOAT = 6 -SQL_DATE = 9 -SQL_TIME = 10 -SQL_TIMESTAMP = 11 -SQL_VARCHAR = 12 -SQL_LONGVARCHAR = -1 -SQL_VARBINARY = -3 -SQL_LONGVARBINARY = -4 -SQL_BIGINT = -5 -SQL_WVARCHAR = -9 -SQL_WLONGVARCHAR = -10 -SQL_ALL_TYPES = 0 -SQL_SIGNED_OFFSET = -20 - -SQL_C_CHAR = SQL_CHAR = 1 -SQL_C_NUMERIC = SQL_NUMERIC = 2 -SQL_C_LONG = SQL_INTEGER = 4 -SQL_C_SLONG = SQL_C_LONG + SQL_SIGNED_OFFSET -SQL_C_SHORT = SQL_SMALLINT = 5 -SQL_C_FLOAT = SQL_REAL = 7 -SQL_C_DOUBLE = SQL_DOUBLE = 8 -SQL_C_TYPE_DATE = SQL_TYPE_DATE = 91 -SQL_C_TYPE_TIME = SQL_TYPE_TIME = 92 -SQL_C_BINARY = SQL_BINARY = -2 -SQL_C_SBIGINT = SQL_BIGINT + SQL_SIGNED_OFFSET -SQL_C_TINYINT = SQL_TINYINT = -6 -SQL_C_BIT = SQL_BIT = -7 -SQL_C_WCHAR = SQL_WCHAR = -8 -SQL_C_GUID = SQL_GUID = -11 -SQL_C_TYPE_TIMESTAMP = SQL_TYPE_TIMESTAMP = 93 -SQL_C_DEFAULT = 99 - -SQL_SS_TIME2 = -154 - -SQL_DESC_DISPLAY_SIZE = SQL_COLUMN_DISPLAY_SIZE - - -def dttm_cvt(x): - 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'))) - -def tm_cvt(x): - if x == '': return None - else: return datetime.time(int(x[0:2]),int(x[3:5]),int(x[6:8]),int(x[9:].ljust(6,'0'))) - -def dt_cvt(x): - if x == '': return None - else: return datetime.date(int(x[0:4]),int(x[5:7]),int(x[8:10])) - - -# Below defines The constants for sqlgetinfo method, and their coresponding return types -SQL_QUALIFIER_LOCATION = 114 -SQL_QUALIFIER_NAME_SEPARATOR = 41 -SQL_QUALIFIER_TERM = 42 -SQL_QUALIFIER_USAGE = 92 -SQL_OWNER_TERM = 39 -SQL_OWNER_USAGE = 91 -SQL_ACCESSIBLE_PROCEDURES = 20 -SQL_ACCESSIBLE_TABLES = 19 -SQL_ACTIVE_ENVIRONMENTS = 116 -SQL_AGGREGATE_FUNCTIONS = 169 -SQL_ALTER_DOMAIN = 117 -SQL_ALTER_TABLE = 86 -SQL_ASYNC_MODE = 10021 -SQL_BATCH_ROW_COUNT = 120 -SQL_BATCH_SUPPORT = 121 -SQL_BOOKMARK_PERSISTENCE = 82 -SQL_CATALOG_LOCATION = SQL_QUALIFIER_LOCATION -SQL_CATALOG_NAME = 10003 -SQL_CATALOG_NAME_SEPARATOR = SQL_QUALIFIER_NAME_SEPARATOR -SQL_CATALOG_TERM = SQL_QUALIFIER_TERM -SQL_CATALOG_USAGE = SQL_QUALIFIER_USAGE -SQL_COLLATION_SEQ = 10004 -SQL_COLUMN_ALIAS = 87 -SQL_CONCAT_NULL_BEHAVIOR = 22 -SQL_CONVERT_FUNCTIONS = 48 -SQL_CONVERT_VARCHAR = 70 -SQL_CORRELATION_NAME = 74 -SQL_CREATE_ASSERTION = 127 -SQL_CREATE_CHARACTER_SET = 128 -SQL_CREATE_COLLATION = 129 -SQL_CREATE_DOMAIN = 130 -SQL_CREATE_SCHEMA = 131 -SQL_CREATE_TABLE = 132 -SQL_CREATE_TRANSLATION = 133 -SQL_CREATE_VIEW = 134 -SQL_CURSOR_COMMIT_BEHAVIOR = 23 -SQL_CURSOR_ROLLBACK_BEHAVIOR = 24 -SQL_DATABASE_NAME = 16 -SQL_DATA_SOURCE_NAME = 2 -SQL_DATA_SOURCE_READ_ONLY = 25 -SQL_DATETIME_LITERALS = 119 -SQL_DBMS_NAME = 17 -SQL_DBMS_VER = 18 -SQL_DDL_INDEX = 170 -SQL_DEFAULT_TXN_ISOLATION = 26 -SQL_DESCRIBE_PARAMETER = 10002 -SQL_DM_VER = 171 -SQL_DRIVER_NAME = 6 -SQL_DRIVER_ODBC_VER = 77 -SQL_DRIVER_VER = 7 -SQL_DROP_ASSERTION = 136 -SQL_DROP_CHARACTER_SET = 137 -SQL_DROP_COLLATION = 138 -SQL_DROP_DOMAIN = 139 -SQL_DROP_SCHEMA = 140 -SQL_DROP_TABLE = 141 -SQL_DROP_TRANSLATION = 142 -SQL_DROP_VIEW = 143 -SQL_DYNAMIC_CURSOR_ATTRIBUTES1 = 144 -SQL_DYNAMIC_CURSOR_ATTRIBUTES2 = 145 -SQL_EXPRESSIONS_IN_ORDERBY = 27 -SQL_FILE_USAGE = 84 -SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 = 146 -SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 = 147 -SQL_GETDATA_EXTENSIONS = 81 -SQL_GROUP_BY = 88 -SQL_IDENTIFIER_CASE = 28 -SQL_IDENTIFIER_QUOTE_CHAR = 29 -SQL_INDEX_KEYWORDS = 148 -SQL_INFO_SCHEMA_VIEWS = 149 -SQL_INSERT_STATEMENT = 172 -SQL_INTEGRITY = 73 -SQL_KEYSET_CURSOR_ATTRIBUTES1 = 150 -SQL_KEYSET_CURSOR_ATTRIBUTES2 = 151 -SQL_KEYWORDS = 89 -SQL_LIKE_ESCAPE_CLAUSE = 113 -SQL_MAX_ASYNC_CONCURRENT_STATEMENTS = 10022 -SQL_MAX_BINARY_LITERAL_LEN = 112 -SQL_MAX_CATALOG_NAME_LEN = 34 -SQL_MAX_CHAR_LITERAL_LEN = 108 -SQL_MAX_COLUMNS_IN_GROUP_BY = 97 -SQL_MAX_COLUMNS_IN_INDEX = 98 -SQL_MAX_COLUMNS_IN_ORDER_BY = 99 -SQL_MAX_COLUMNS_IN_SELECT = 100 -SQL_MAX_COLUMNS_IN_TABLE = 101 -SQL_MAX_COLUMN_NAME_LEN = 30 -SQL_MAX_CONCURRENT_ACTIVITIES = 1 -SQL_MAX_CURSOR_NAME_LEN = 31 -SQL_MAX_DRIVER_CONNECTIONS = 0 -SQL_MAX_IDENTIFIER_LEN = 10005 -SQL_MAX_INDEX_SIZE = 102 -SQL_MAX_PROCEDURE_NAME_LEN = 33 -SQL_MAX_ROW_SIZE = 104 -SQL_MAX_ROW_SIZE_INCLUDES_LONG = 103 -SQL_MAX_SCHEMA_NAME_LEN = 32 -SQL_MAX_STATEMENT_LEN = 105 -SQL_MAX_TABLES_IN_SELECT = 106 -SQL_MAX_TABLE_NAME_LEN = 35 -SQL_MAX_USER_NAME_LEN = 107 -SQL_MULTIPLE_ACTIVE_TXN = 37 -SQL_MULT_RESULT_SETS = 36 -SQL_NEED_LONG_DATA_LEN = 111 -SQL_NON_NULLABLE_COLUMNS = 75 -SQL_NULL_COLLATION = 85 -SQL_NUMERIC_FUNCTIONS = 49 -SQL_ODBC_INTERFACE_CONFORMANCE = 152 -SQL_ODBC_VER = 10 -SQL_OJ_CAPABILITIES = 65003 -SQL_ORDER_BY_COLUMNS_IN_SELECT = 90 -SQL_PARAM_ARRAY_ROW_COUNTS = 153 -SQL_PARAM_ARRAY_SELECTS = 154 -SQL_PROCEDURES = 21 -SQL_PROCEDURE_TERM = 40 -SQL_QUOTED_IDENTIFIER_CASE = 93 -SQL_ROW_UPDATES = 11 -SQL_SCHEMA_TERM = SQL_OWNER_TERM -SQL_SCHEMA_USAGE = SQL_OWNER_USAGE -SQL_SCROLL_OPTIONS = 44 -SQL_SEARCH_PATTERN_ESCAPE = 14 -SQL_SERVER_NAME = 13 -SQL_SPECIAL_CHARACTERS = 94 -SQL_SQL92_DATETIME_FUNCTIONS = 155 -SQL_SQL92_FOREIGN_KEY_DELETE_RULE = 156 -SQL_SQL92_FOREIGN_KEY_UPDATE_RULE = 157 -SQL_SQL92_GRANT = 158 -SQL_SQL92_NUMERIC_VALUE_FUNCTIONS = 159 -SQL_SQL92_PREDICATES = 160 -SQL_SQL92_RELATIONAL_JOIN_OPERATORS = 161 -SQL_SQL92_REVOKE = 162 -SQL_SQL92_ROW_VALUE_CONSTRUCTOR = 163 -SQL_SQL92_STRING_FUNCTIONS = 164 -SQL_SQL92_VALUE_EXPRESSIONS = 165 -SQL_SQL_CONFORMANCE = 118 -SQL_STANDARD_CLI_CONFORMANCE = 166 -SQL_STATIC_CURSOR_ATTRIBUTES1 = 167 -SQL_STATIC_CURSOR_ATTRIBUTES2 = 168 -SQL_STRING_FUNCTIONS = 50 -SQL_SUBQUERIES = 95 -SQL_SYSTEM_FUNCTIONS = 51 -SQL_TABLE_TERM = 45 -SQL_TIMEDATE_ADD_INTERVALS = 109 -SQL_TIMEDATE_DIFF_INTERVALS = 110 -SQL_TIMEDATE_FUNCTIONS = 52 -SQL_TXN_CAPABLE = 46 -SQL_TXN_ISOLATION_OPTION = 72 -SQL_UNION = 96 -SQL_USER_NAME = 47 -SQL_XOPEN_CLI_YEAR = 10000 - - -aInfoTypes = { -SQL_ACCESSIBLE_PROCEDURES : 'GI_YESNO',SQL_ACCESSIBLE_TABLES : 'GI_YESNO',SQL_ACTIVE_ENVIRONMENTS : 'GI_USMALLINT', -SQL_AGGREGATE_FUNCTIONS : 'GI_UINTEGER',SQL_ALTER_DOMAIN : 'GI_UINTEGER', -SQL_ALTER_TABLE : 'GI_UINTEGER',SQL_ASYNC_MODE : 'GI_UINTEGER',SQL_BATCH_ROW_COUNT : 'GI_UINTEGER', -SQL_BATCH_SUPPORT : 'GI_UINTEGER',SQL_BOOKMARK_PERSISTENCE : 'GI_UINTEGER',SQL_CATALOG_LOCATION : 'GI_USMALLINT', -SQL_CATALOG_NAME : 'GI_YESNO',SQL_CATALOG_NAME_SEPARATOR : 'GI_STRING',SQL_CATALOG_TERM : 'GI_STRING', -SQL_CATALOG_USAGE : 'GI_UINTEGER',SQL_COLLATION_SEQ : 'GI_STRING',SQL_COLUMN_ALIAS : 'GI_YESNO', -SQL_CONCAT_NULL_BEHAVIOR : 'GI_USMALLINT',SQL_CONVERT_FUNCTIONS : 'GI_UINTEGER', -SQL_CONVERT_VARCHAR : 'GI_UINTEGER',SQL_CORRELATION_NAME : 'GI_USMALLINT', -SQL_CREATE_ASSERTION : 'GI_UINTEGER',SQL_CREATE_CHARACTER_SET : 'GI_UINTEGER', -SQL_CREATE_COLLATION : 'GI_UINTEGER',SQL_CREATE_DOMAIN : 'GI_UINTEGER',SQL_CREATE_SCHEMA : 'GI_UINTEGER', -SQL_CREATE_TABLE : 'GI_UINTEGER',SQL_CREATE_TRANSLATION : 'GI_UINTEGER',SQL_CREATE_VIEW : 'GI_UINTEGER', -SQL_CURSOR_COMMIT_BEHAVIOR : 'GI_USMALLINT',SQL_CURSOR_ROLLBACK_BEHAVIOR : 'GI_USMALLINT',SQL_DATABASE_NAME : 'GI_STRING', -SQL_DATA_SOURCE_NAME : 'GI_STRING',SQL_DATA_SOURCE_READ_ONLY : 'GI_YESNO',SQL_DATETIME_LITERALS : 'GI_UINTEGER', -SQL_DBMS_NAME : 'GI_STRING',SQL_DBMS_VER : 'GI_STRING',SQL_DDL_INDEX : 'GI_UINTEGER', -SQL_DEFAULT_TXN_ISOLATION : 'GI_UINTEGER',SQL_DESCRIBE_PARAMETER : 'GI_YESNO',SQL_DM_VER : 'GI_STRING', -SQL_DRIVER_NAME : 'GI_STRING',SQL_DRIVER_ODBC_VER : 'GI_STRING',SQL_DRIVER_VER : 'GI_STRING', -SQL_DROP_ASSERTION : 'GI_UINTEGER',SQL_DROP_CHARACTER_SET : 'GI_UINTEGER', -SQL_DROP_COLLATION : 'GI_UINTEGER',SQL_DROP_DOMAIN : 'GI_UINTEGER', -SQL_DROP_SCHEMA : 'GI_UINTEGER',SQL_DROP_TABLE : 'GI_UINTEGER',SQL_DROP_TRANSLATION : 'GI_UINTEGER', -SQL_DROP_VIEW : 'GI_UINTEGER',SQL_DYNAMIC_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_DYNAMIC_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER', -SQL_EXPRESSIONS_IN_ORDERBY : 'GI_YESNO',SQL_FILE_USAGE : 'GI_USMALLINT', -SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER', -SQL_GETDATA_EXTENSIONS : 'GI_UINTEGER',SQL_GROUP_BY : 'GI_USMALLINT',SQL_IDENTIFIER_CASE : 'GI_USMALLINT', -SQL_IDENTIFIER_QUOTE_CHAR : 'GI_STRING',SQL_INDEX_KEYWORDS : 'GI_UINTEGER',SQL_INFO_SCHEMA_VIEWS : 'GI_UINTEGER', -SQL_INSERT_STATEMENT : 'GI_UINTEGER',SQL_INTEGRITY : 'GI_YESNO',SQL_KEYSET_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER', -SQL_KEYSET_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',SQL_KEYWORDS : 'GI_STRING', -SQL_LIKE_ESCAPE_CLAUSE : 'GI_YESNO',SQL_MAX_ASYNC_CONCURRENT_STATEMENTS : 'GI_UINTEGER', -SQL_MAX_BINARY_LITERAL_LEN : 'GI_UINTEGER',SQL_MAX_CATALOG_NAME_LEN : 'GI_USMALLINT', -SQL_MAX_CHAR_LITERAL_LEN : 'GI_UINTEGER',SQL_MAX_COLUMNS_IN_GROUP_BY : 'GI_USMALLINT', -SQL_MAX_COLUMNS_IN_INDEX : 'GI_USMALLINT',SQL_MAX_COLUMNS_IN_ORDER_BY : 'GI_USMALLINT', -SQL_MAX_COLUMNS_IN_SELECT : 'GI_USMALLINT',SQL_MAX_COLUMNS_IN_TABLE : 'GI_USMALLINT', -SQL_MAX_COLUMN_NAME_LEN : 'GI_USMALLINT',SQL_MAX_CONCURRENT_ACTIVITIES : 'GI_USMALLINT', -SQL_MAX_CURSOR_NAME_LEN : 'GI_USMALLINT',SQL_MAX_DRIVER_CONNECTIONS : 'GI_USMALLINT', -SQL_MAX_IDENTIFIER_LEN : 'GI_USMALLINT',SQL_MAX_INDEX_SIZE : 'GI_UINTEGER', -SQL_MAX_PROCEDURE_NAME_LEN : 'GI_USMALLINT',SQL_MAX_ROW_SIZE : 'GI_UINTEGER', -SQL_MAX_ROW_SIZE_INCLUDES_LONG : 'GI_YESNO',SQL_MAX_SCHEMA_NAME_LEN : 'GI_USMALLINT', -SQL_MAX_STATEMENT_LEN : 'GI_UINTEGER',SQL_MAX_TABLES_IN_SELECT : 'GI_USMALLINT', -SQL_MAX_TABLE_NAME_LEN : 'GI_USMALLINT',SQL_MAX_USER_NAME_LEN : 'GI_USMALLINT', -SQL_MULTIPLE_ACTIVE_TXN : 'GI_YESNO',SQL_MULT_RESULT_SETS : 'GI_YESNO', -SQL_NEED_LONG_DATA_LEN : 'GI_YESNO',SQL_NON_NULLABLE_COLUMNS : 'GI_USMALLINT', -SQL_NULL_COLLATION : 'GI_USMALLINT',SQL_NUMERIC_FUNCTIONS : 'GI_UINTEGER', -SQL_ODBC_INTERFACE_CONFORMANCE : 'GI_UINTEGER',SQL_ODBC_VER : 'GI_STRING',SQL_OJ_CAPABILITIES : 'GI_UINTEGER', -SQL_ORDER_BY_COLUMNS_IN_SELECT : 'GI_YESNO',SQL_PARAM_ARRAY_ROW_COUNTS : 'GI_UINTEGER', -SQL_PARAM_ARRAY_SELECTS : 'GI_UINTEGER',SQL_PROCEDURES : 'GI_YESNO',SQL_PROCEDURE_TERM : 'GI_STRING', -SQL_QUOTED_IDENTIFIER_CASE : 'GI_USMALLINT',SQL_ROW_UPDATES : 'GI_YESNO',SQL_SCHEMA_TERM : 'GI_STRING', -SQL_SCHEMA_USAGE : 'GI_UINTEGER',SQL_SCROLL_OPTIONS : 'GI_UINTEGER',SQL_SEARCH_PATTERN_ESCAPE : 'GI_STRING', -SQL_SERVER_NAME : 'GI_STRING',SQL_SPECIAL_CHARACTERS : 'GI_STRING',SQL_SQL92_DATETIME_FUNCTIONS : 'GI_UINTEGER', -SQL_SQL92_FOREIGN_KEY_DELETE_RULE : 'GI_UINTEGER',SQL_SQL92_FOREIGN_KEY_UPDATE_RULE : 'GI_UINTEGER', -SQL_SQL92_GRANT : 'GI_UINTEGER',SQL_SQL92_NUMERIC_VALUE_FUNCTIONS : 'GI_UINTEGER', -SQL_SQL92_PREDICATES : 'GI_UINTEGER',SQL_SQL92_RELATIONAL_JOIN_OPERATORS : 'GI_UINTEGER', -SQL_SQL92_REVOKE : 'GI_UINTEGER',SQL_SQL92_ROW_VALUE_CONSTRUCTOR : 'GI_UINTEGER', -SQL_SQL92_STRING_FUNCTIONS : 'GI_UINTEGER',SQL_SQL92_VALUE_EXPRESSIONS : 'GI_UINTEGER', -SQL_SQL_CONFORMANCE : 'GI_UINTEGER',SQL_STANDARD_CLI_CONFORMANCE : 'GI_UINTEGER', -SQL_STATIC_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_STATIC_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER', -SQL_STRING_FUNCTIONS : 'GI_UINTEGER',SQL_SUBQUERIES : 'GI_UINTEGER', -SQL_SYSTEM_FUNCTIONS : 'GI_UINTEGER',SQL_TABLE_TERM : 'GI_STRING',SQL_TIMEDATE_ADD_INTERVALS : 'GI_UINTEGER', -SQL_TIMEDATE_DIFF_INTERVALS : 'GI_UINTEGER',SQL_TIMEDATE_FUNCTIONS : 'GI_UINTEGER', -SQL_TXN_CAPABLE : 'GI_USMALLINT',SQL_TXN_ISOLATION_OPTION : 'GI_UINTEGER', -SQL_UNION : 'GI_UINTEGER',SQL_USER_NAME : 'GI_STRING',SQL_XOPEN_CLI_YEAR : 'GI_STRING', -} - -#Definations for types -BINARY = bytearray -Binary = bytearray -DATETIME = datetime.datetime -Date = datetime.date -Time = datetime.time -Timestamp = datetime.datetime -STRING = str -NUMBER = float -ROWID = int -DateFromTicks = datetime.date.fromtimestamp -TimeFromTicks = lambda x: datetime.datetime.fromtimestamp(x).time() -TimestampFromTicks = datetime.datetime.fromtimestamp - - -#Define exceptions -class OdbcNoLibrary(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) -class OdbcLibraryError(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) -class OdbcInvalidHandle(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) -class OdbcGenericError(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - - -class Warning(StandardError): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - - -class Error(StandardError): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - -class InterfaceError(Error): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - - -class DatabaseError(Error): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - - -class InternalError(DatabaseError): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - - -class ProgrammingError(DatabaseError): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - -class DataError(DatabaseError): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - -class IntegrityError(DatabaseError): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - -class NotSupportedError(Error): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - -class OperationalError(DatabaseError): - def __init__(self, error_code, error_desc): - self.value = (error_code, error_desc) - self.args = (error_code, error_desc) - - - -# Get the References of the platform's ODBC functions via ctypes -if sys.platform in ('win32','cli'): - ODBC_API = ctypes.windll.odbc32 - # On Windows, the size of SQLWCHAR is hardcoded to 2-bytes. - SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_ushort) -else: - # Set load the library on linux - try: - # First try direct loading libodbc.so - ODBC_API = ctypes.cdll.LoadLibrary('libodbc.so') - except: - # If direct loading libodbc.so failed - # We try finding the libodbc.so by using find_library - from ctypes.util import find_library - library = find_library('odbc') - if library is None: - # If find_library still can not find the library - # we try finding it manually from where libodbc.so usually appears - lib_paths = ("/usr/lib/libodbc.so","/usr/lib/i386-linux-gnu/libodbc.so","/usr/lib/x86_64-linux-gnu/libodbc.so") - lib_paths = [path for path in lib_paths if os.path.exists(path)] - if len(lib_paths) == 0 : - raise OdbcNoLibrary, 'ODBC Library is not found' - else: - library = lib_paths[0] - - # Then we try loading the found libodbc.so again - try: - ODBC_API = ctypes.cdll.LoadLibrary(library) - except: - # If still fail loading, abort. - raise OdbcLibraryError, 'Error while loading %s' % library - - # unixODBC defaults to 2-bytes SQLWCHAR, unless "-DSQL_WCHART_CONVERT" was - # added to CFLAGS, in which case it will be the size of wchar_t. - # Note that using 4-bytes SQLWCHAR will break most ODBC drivers, as driver - # development mostly targets the Windows platform. - import commands - status, output = commands.getstatusoutput('odbc_config --cflags') - if status == 0 and 'SQL_WCHART_CONVERT' in output: - SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_wchar) - else: - SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_ushort) - - -create_buffer_u = ctypes.create_unicode_buffer -create_buffer = ctypes.create_string_buffer -wchar_type = ctypes.c_wchar_p -to_unicode = lambda s: s -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 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 - wchar_type = ctypes.c_char_p - - def to_unicode(s): - return s.encode('UTF-16LE') - - def from_buffer_u(buffer): - i = 0 - uchars = [] - while True: - uchar = buffer.raw[i:i + 2].decode('UTF-16') - if uchar == u'\x00': - break - uchars.append(uchar) - i += 2 - return ''.join(uchars) - -# Exoteric case, don't really care. -elif UNICODE_SIZE < SQLWCHAR_SIZE: - raise OdbcLibraryError('Using narrow Python build with ODBC library ' - 'expecting wide unicode is not supported.') - - -# Below Datatype mappings referenced the document at -# 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 0.Python Data Type 1.Default Output Converter 2.Buffer Type 3.Buffer Allocator 4.Default Buffer Size -SQL_TYPE_NULL : (None, lambda x: None, SQL_C_CHAR, create_buffer, 2 ), -SQL_CHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ), -SQL_NUMERIC : (Decimal, Decimal, SQL_C_CHAR, create_buffer, 150 ), -SQL_DECIMAL : (Decimal, Decimal, SQL_C_CHAR, create_buffer, 150 ), -SQL_INTEGER : (int, int, SQL_C_CHAR, create_buffer, 150 ), -SQL_SMALLINT : (int, int, SQL_C_CHAR, create_buffer, 150 ), -SQL_FLOAT : (float, float, SQL_C_CHAR, create_buffer, 150 ), -SQL_REAL : (float, float, SQL_C_CHAR, create_buffer, 150 ), -SQL_DOUBLE : (float, float, SQL_C_CHAR, create_buffer, 200 ), -SQL_DATE : (datetime.date, dt_cvt, SQL_C_CHAR , create_buffer, 30 ), -SQL_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), -SQL_SS_TIME2 : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), -SQL_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ), -SQL_VARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ), -SQL_LONGVARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 20500 ), -SQL_BINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 5120 ), -SQL_VARBINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 5120 ), -SQL_LONGVARBINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 20500 ), -SQL_BIGINT : (long, long, SQL_C_CHAR, create_buffer, 150 ), -SQL_TINYINT : (int, int, SQL_C_CHAR, create_buffer, 150 ), -SQL_BIT : (bool, lambda x:x=='1', SQL_C_CHAR, create_buffer, 2 ), -SQL_WCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ), -SQL_WVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ), -SQL_GUID : (str, str, SQL_C_CHAR, create_buffer, 50 ), -SQL_WLONGVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 20500 ), -SQL_TYPE_DATE : (datetime.date, dt_cvt, SQL_C_CHAR, create_buffer, 30 ), -SQL_TYPE_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), -SQL_TYPE_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ), -} - - -""" -Types mapping, applicable for 32-bit and 64-bit Linux / Windows / Mac OS X. - -SQLPointer -> ctypes.c_void_p -SQLCHAR * -> ctypes.c_char_p -SQLWCHAR * -> ctypes.c_wchar_p on Windows, ctypes.c_char_p with unixODBC -SQLINT -> ctypes.c_int -SQLSMALLINT -> ctypes.c_short -SQMUSMALLINT -> ctypes.c_ushort -SQLLEN -> ctypes.c_ssize_t -SQLULEN -> ctypes.c_size_t -SQLRETURN -> ctypes.c_short -""" - -# Define the python return type for ODBC functions with ret result. -funcs_with_ret = [ - "SQLAllocHandle", - "SQLBindParameter", - "SQLCloseCursor", - "SQLColAttribute", - "SQLColumns", - "SQLColumnsW", - "SQLConnect", - "SQLConnectW", - "SQLDataSources", - "SQLDataSourcesW", - "SQLDescribeCol", - "SQLDescribeColW", - "SQLDescribeParam", - "SQLDisconnect", - "SQLDriverConnect", - "SQLDriverConnectW", - "SQLEndTran", - "SQLExecDirect", - "SQLExecDirectW", - "SQLExecute", - "SQLFetch", - "SQLFetchScroll", - "SQLForeignKeys", - "SQLForeignKeysW", - "SQLFreeHandle", - "SQLFreeStmt", - "SQLGetData", - "SQLGetDiagRec", - "SQLGetInfo", - "SQLGetTypeInfo", - "SQLMoreResults", - "SQLNumParams", - "SQLNumResultCols", - "SQLPrepare", - "SQLPrepareW", - "SQLPrimaryKeys", - "SQLPrimaryKeysW", - "SQLProcedureColumns", - "SQLProcedureColumnsW", - "SQLProcedures", - "SQLProceduresW", - "SQLRowCount", - "SQLSetConnectAttr", - "SQLSetEnvAttr", - "SQLStatistics", - "SQLStatisticsW", - "SQLTables", - "SQLTablesW", -] - -for func_name in funcs_with_ret: - getattr(ODBC_API, func_name).restype = ctypes.c_short - -if sys.platform not in ('cli'): - #Seems like the IronPython can not declare ctypes.POINTER type arguments - ODBC_API.SQLAllocHandle.argtypes = [ - ctypes.c_short, - ctypes.c_void_p, - ctypes.POINTER(ctypes.c_void_p), - ] - - ODBC_API.SQLBindParameter.argtypes = [ - ctypes.c_void_p, - ctypes.c_ushort, - ctypes.c_short, - ctypes.c_short, - ctypes.c_short, - ctypes.c_size_t, - ctypes.c_short, - ctypes.c_void_p, - ctypes.c_ssize_t, - ctypes.POINTER(ctypes.c_ssize_t), - ] - - ODBC_API.SQLColAttribute.argtypes = [ - ctypes.c_void_p, - ctypes.c_ushort, - ctypes.c_ushort, - ctypes.c_void_p, - ctypes.c_short, - ctypes.POINTER(ctypes.c_short), - ctypes.POINTER(ctypes.c_ssize_t), - ] - - ODBC_API.SQLDataSources.argtypes = [ - ctypes.c_void_p, - ctypes.c_ushort, - ctypes.c_char_p, - ctypes.c_short, - ctypes.POINTER(ctypes.c_short), - ctypes.c_char_p, - ctypes.c_short, - ctypes.POINTER(ctypes.c_short), - ] - - ODBC_API.SQLDescribeCol.argtypes = [ - ctypes.c_void_p, - ctypes.c_ushort, - ctypes.c_char_p, - ctypes.c_short, - ctypes.POINTER(ctypes.c_short), - ctypes.POINTER(ctypes.c_short), - ctypes.POINTER(ctypes.c_size_t), - ctypes.POINTER(ctypes.c_short), - ctypes.POINTER(ctypes.c_short), - ] - - ODBC_API.SQLDescribeParam.argtypes = [ - ctypes.c_void_p, - ctypes.c_ushort, - ctypes.POINTER(ctypes.c_short), - ctypes.POINTER(ctypes.c_size_t), - ctypes.POINTER(ctypes.c_short), - ctypes.POINTER(ctypes.c_short), - ] - - ODBC_API.SQLDriverConnect.argtypes = [ - ctypes.c_void_p, - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.POINTER(ctypes.c_short), - ctypes.c_ushort, - ] - - ODBC_API.SQLGetData.argtypes = [ - ctypes.c_void_p, - ctypes.c_ushort, - ctypes.c_short, - ctypes.c_void_p, - ctypes.c_ssize_t, - ctypes.POINTER(ctypes.c_ssize_t), - ] - - ODBC_API.SQLGetDiagRec.argtypes = [ - ctypes.c_short, - ctypes.c_void_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.POINTER(ctypes.c_int), - ctypes.c_char_p, - ctypes.c_short, - ctypes.POINTER(ctypes.c_short), - ] - - ODBC_API.SQLGetInfo.argtypes = [ - ctypes.c_void_p, - ctypes.c_ushort, - ctypes.c_void_p, - ctypes.c_short, - ctypes.POINTER(ctypes.c_short), - ] - - ODBC_API.SQLRowCount.argtypes = [ - ctypes.c_void_p, - ctypes.POINTER(ctypes.c_ssize_t), - ] - - ODBC_API.SQLNumParams.argtypes = [ - ctypes.c_void_p, - ctypes.POINTER(ctypes.c_short), - ] - - ODBC_API.SQLNumResultCols.argtypes = [ - ctypes.c_void_p, - ctypes.POINTER(ctypes.c_short), - ] - - -ODBC_API.SQLCloseCursor.argtypes = [ctypes.c_void_p] - -ODBC_API.SQLColumns.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, -] - -ODBC_API.SQLConnect.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, -] - - - -ODBC_API.SQLDisconnect.argtypes = [ctypes.c_void_p] - - -ODBC_API.SQLEndTran.argtypes = [ - ctypes.c_short, - ctypes.c_void_p, - ctypes.c_short, -] - -ODBC_API.SQLExecute.argtypes = [ctypes.c_void_p] - -ODBC_API.SQLExecDirect.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_int, -] - -ODBC_API.SQLFetch.argtypes = [ctypes.c_void_p] - -ODBC_API.SQLFetchScroll.argtypes = [ - ctypes.c_void_p, - ctypes.c_short, - ctypes.c_ssize_t, -] - -ODBC_API.SQLForeignKeys.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, -] - -ODBC_API.SQLFreeHandle.argtypes = [ - ctypes.c_short, - ctypes.c_void_p, -] - -ODBC_API.SQLFreeStmt.argtypes = [ - ctypes.c_void_p, - ctypes.c_ushort, -] - - -ODBC_API.SQLGetTypeInfo.argtypes = [ - ctypes.c_void_p, - ctypes.c_short, -] - -ODBC_API.SQLMoreResults.argtypes = [ctypes.c_void_p] - - -ODBC_API.SQLPrepare.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_int, -] - -ODBC_API.SQLPrimaryKeys.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, -] - -ODBC_API.SQLProcedureColumns.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, -] - -ODBC_API.SQLProcedures.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, -] - - -ODBC_API.SQLSetConnectAttr.argtypes = [ - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_void_p, - ctypes.c_int, -] - -ODBC_API.SQLSetEnvAttr.argtypes = [ - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_void_p, - ctypes.c_int, -] - -ODBC_API.SQLStatistics.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_ushort, - ctypes.c_ushort, -] - -ODBC_API.SQLTables.argtypes = [ - ctypes.c_void_p, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, - ctypes.c_char_p, - ctypes.c_short, -] - -def to_wchar(argtypes): - if argtypes: # Under IronPython some argtypes are not declared - result = [] - for x in argtypes: - if x == ctypes.c_char_p: - result.append(wchar_type) - else: - result.append(x) - return result - else: - return argtypes - -ODBC_API.SQLColumnsW.argtypes = to_wchar(ODBC_API.SQLColumns.argtypes) -ODBC_API.SQLConnectW.argtypes = to_wchar(ODBC_API.SQLConnect.argtypes) -ODBC_API.SQLDataSourcesW.argtypes = to_wchar(ODBC_API.SQLDataSources.argtypes) -ODBC_API.SQLDescribeColW.argtypes = to_wchar(ODBC_API.SQLDescribeCol.argtypes) -ODBC_API.SQLDriverConnectW.argtypes = to_wchar(ODBC_API.SQLDriverConnect.argtypes) -ODBC_API.SQLExecDirectW.argtypes = to_wchar(ODBC_API.SQLExecDirect.argtypes) -ODBC_API.SQLForeignKeysW.argtypes = to_wchar(ODBC_API.SQLForeignKeys.argtypes) -ODBC_API.SQLPrepareW.argtypes = to_wchar(ODBC_API.SQLPrepare.argtypes) -ODBC_API.SQLPrimaryKeysW.argtypes = to_wchar(ODBC_API.SQLPrimaryKeys.argtypes) -ODBC_API.SQLProcedureColumnsW.argtypes = to_wchar(ODBC_API.SQLProcedureColumns.argtypes) -ODBC_API.SQLProceduresW.argtypes = to_wchar(ODBC_API.SQLProcedures.argtypes) -ODBC_API.SQLStatisticsW.argtypes = to_wchar(ODBC_API.SQLStatistics.argtypes) -ODBC_API.SQLTablesW.argtypes = to_wchar(ODBC_API.SQLTables.argtypes) - - -# Set the alias for the ctypes functions for beter code readbility or performance. -ADDR = ctypes.byref -SQLFetch = ODBC_API.SQLFetch -SQLExecute = ODBC_API.SQLExecute -SQLBindParameter = ODBC_API.SQLBindParameter - - - - - -def ctrl_err(ht, h, val_ret): - """Classify type of ODBC error from (type of handle, handle, return value) - , and raise with a list""" - state = create_buffer(5) - NativeError = ctypes.c_int() - Message = create_buffer(1024*10) - Buffer_len = ctypes.c_short() - err_list = [] - number_errors = 1 - - while 1: - ret = ODBC_API.SQLGetDiagRec(ht, h, number_errors, state, \ - NativeError, Message, len(Message), ADDR(Buffer_len)) - if ret == SQL_NO_DATA_FOUND: - #No more data, I can raise - if DEBUG: print err_list[0][1] - state = err_list[0][0] - err_text = '['+state+'] '+err_list[0][1] - if state[:2] in ('24','25','42'): - raise ProgrammingError(state,err_text) - elif state[:2] in ('22'): - raise DataError(state,err_text) - elif state[:2] in ('23') or state == '40002': - raise IntegrityError(state,err_text) - elif state == '0A000': - raise NotSupportedError(state,err_text) - elif state in ('HYT00','HYT01'): - raise OperationalError(state,err_text) - elif state[:2] in ('IM','HY'): - raise Error(state,err_text) - else: - raise DatabaseError(state,err_text) - break - elif ret == SQL_INVALID_HANDLE: - #The handle passed is an invalid handle - raise ProgrammingError('', 'SQL_INVALID_HANDLE') - elif ret == SQL_SUCCESS: - err_list.append((state.value, Message.value, NativeError.value)) - number_errors += 1 - - -def validate(ret, handle_type, handle): - """ Validate return value, if not success, raise exceptions based on the handle """ - if ret not in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA): - ctrl_err(handle_type, handle, ret) - - -def AllocateEnv(): - if pooling: - ret = ODBC_API.SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, SQL_CP_ONE_PER_HENV, SQL_IS_UINTEGER) - validate(ret, SQL_HANDLE_ENV, SQL_NULL_HANDLE) - - ''' - Allocate an ODBC environment by initializing the handle shared_env_h - ODBC enviroment needed to be created, so connections can be created under it - connections pooling can be shared under one environment - ''' - global shared_env_h - shared_env_h = ctypes.c_void_p() - ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, ADDR(shared_env_h)) - validate(ret, SQL_HANDLE_ENV, shared_env_h) - - # Set the ODBC environment's compatibil leve to ODBC 3.0 - ret = ODBC_API.SQLSetEnvAttr(shared_env_h, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0) - validate(ret, SQL_HANDLE_ENV, shared_env_h) - - -""" -Here, we have a few callables that determine how a result row is returned. - -A new one can be added by creating a callable that: -- accepts a cursor as its parameter. -- returns a callable that accepts an iterable containing the row values. -""" - -def TupleRow(cursor): - """Normal tuple with added attribute `cursor_description`, as in pyodbc. - - This is the default. - """ - class Row(tuple): - cursor_description = cursor.description - - return Row - - -def NamedTupleRow(cursor): - """Named tuple to allow attribute lookup by name. - - Requires py2.6 or above. - """ - from collections import namedtuple - - attr_names = [x[0] for x in cursor._ColBufferList] - - class Row(namedtuple('Row', attr_names, rename=True)): - cursor_description = cursor.description - - def __new__(cls, iterable): - return super(Row, cls).__new__(cls, *iterable) - - return Row - - -def MutableNamedTupleRow(cursor): - """Mutable named tuple to allow attribute to be replaced. This should be - compatible with pyodbc's Row type. - - Requires 3rd-party library "recordtype". - """ - from recordtype import recordtype - - attr_names = [x[0] for x in cursor._ColBufferList] - - class Row(recordtype('Row', attr_names, rename=True)): - cursor_description = cursor.description - - def __init__(self, iterable): - super(Row, self).__init__(*iterable) - - def __iter__(self): - for field_name in self.__slots__: - yield getattr(self, field_name) - - def __getitem__(self, index): - if isinstance(index, slice): - return tuple(getattr(self, x) for x in self.__slots__[index]) - return getattr(self, self.__slots__[index]) - - def __setitem__(self, index, value): - setattr(self, self.__slots__[index], value) - - return Row - - -# The get_type function is used to determine if parameters need to be re-binded -# against the changed parameter types -def get_type(v): - t = type(v) - if isinstance(v, str): - if len(v) >= 255: - t = 's' - elif isinstance(v, unicode): - if len(v) >= 255: - t = 'u' - elif isinstance(v, Decimal): - sv = str(v).replace('-','').strip('0').split('.') - if len(sv)>1: - t = (len(sv[0])+len(sv[1]),len(sv[1])) - else: - t = (len(sv[0]),0) - return t - - - -# The Cursor Class. -class Cursor: - def __init__(self, conx, row_type_callable=None): - """ Initialize self._stmt_h, which is the handle of a statement - A statement is actually the basis of a python"cursor" object - """ - self._stmt_h = ctypes.c_void_p() - self.connection = conx - self.row_type_callable = row_type_callable or TupleRow - self.statement = None - self._last_param_types = None - self._ParamBufferList = [] - self._ColBufferList = [] - self._row_type = None - self._buf_cvt_func = [] - self.rowcount = -1 - self.description = None - self.autocommit = None - self._ColTypeCodeList = [] - self._outputsize = {} - self._inputsizers = [] - self.arraysize = 1 - ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_STMT, self.connection.dbc_h, ADDR(self._stmt_h)) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - self.closed = False - - - def execute(self, query_string, params=None, many_mode=False, call_mode=False): - """ Execute the query string, with optional 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 - """ - - self._free_results('FREE_STATEMENT') - - if params: - # If parameters exist, first prepare the query then executed with parameters - if not type(params) in (tuple, list, set): - raise TypeError("Params must be in a list, tuple, or set") - - if not many_mode: - if query_string != self.statement: - # if the query is not same as last query, then it is not prepared - self.prepare(query_string) - - - param_types = map(get_type, params) - - if call_mode: - self._BindParams(param_types, self._pram_io_list) - else: - if param_types != self._last_param_types: - self._BindParams(param_types) - - - # With query prepared, now put parameters into buffers - col_num = 0 - for param_buffer, param_buffer_len, sql_type in self._ParamBufferList: - c_char_buf, c_buf_len = '', 0 - param_val = params[col_num] - if param_val is None: - c_buf_len = SQL_NULL_DATA - - elif isinstance(param_val, datetime.datetime): - max_len = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0] - datetime_str = param_val.strftime('%Y-%m-%d %H:%M:%S.%f') - c_char_buf = datetime_str[:max_len] - c_buf_len = len(c_char_buf) - # print c_buf_len, c_char_buf - - elif isinstance(param_val, datetime.date): - if self.connection.type_size_dic.has_key(SQL_TYPE_DATE): - max_len = self.connection.type_size_dic[SQL_TYPE_DATE][0] - else: - max_len = 10 - c_char_buf = param_val.isoformat()[:max_len] - c_buf_len = len(c_char_buf) - #print c_char_buf - - elif isinstance(param_val, datetime.time): - if self.connection.type_size_dic.has_key(SQL_TYPE_TIME): - max_len = self.connection.type_size_dic[SQL_TYPE_TIME][0] - c_char_buf = param_val.isoformat()[:max_len] - c_buf_len = len(c_char_buf) - elif self.connection.type_size_dic.has_key(SQL_SS_TIME2): - max_len = self.connection.type_size_dic[SQL_SS_TIME2][0] - c_char_buf = param_val.isoformat()[:max_len] - c_buf_len = len(c_char_buf) - else: - c_buf_len = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0] - time_str = param_val.isoformat() - if len(time_str) == 8: - time_str += '.000' - c_char_buf = '1900-01-01 '+time_str[0:c_buf_len - 11] - #print c_buf_len, c_char_buf - - elif isinstance(param_val, bool): - if param_val == True: - c_char_buf = '1' - else: - c_char_buf = '0' - c_buf_len = 1 - - elif isinstance(param_val, (int, long, float, Decimal)): - c_char_buf = str(param_val) - c_buf_len = len(c_char_buf) - - elif isinstance(param_val, str): - c_char_buf = param_val - c_buf_len = len(c_char_buf) - elif isinstance(param_val, unicode): - c_char_buf = to_unicode(param_val) - c_buf_len = len(c_char_buf) - elif isinstance(param_val, (bytearray, buffer)): - c_char_buf = str(param_val) - c_buf_len = len(c_char_buf) - - else: - c_char_buf = param_val - - - if isinstance(param_val, (bytearray, buffer)): - param_buffer.raw = c_char_buf - - else: - param_buffer.value = c_char_buf - #print param_buffer, param_buffer.value - - if isinstance(param_val, (unicode, str)): - #ODBC driver will find NUL in unicode and string to determine their length - param_buffer_len.value = SQL_NTS - else: - param_buffer_len.value = c_buf_len - - col_num += 1 - ret = SQLExecute(self._stmt_h) - if ret != SQL_SUCCESS: - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - - if not many_mode: - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - - else: - self.execdirect(query_string) - return (self) - - - def _SQLExecute(self): - ret = SQLExecute(self._stmt_h) - if ret != SQL_SUCCESS: - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - - - - def prepare(self, query_string): - """prepare a query""" - if type(query_string) == unicode: - c_query_string = wchar_type(to_unicode(query_string)) - ret = ODBC_API.SQLPrepareW(self._stmt_h, c_query_string, len(query_string)) - else: - c_query_string = ctypes.c_char_p(query_string) - ret = ODBC_API.SQLPrepare(self._stmt_h, c_query_string, len(query_string)) - if ret != SQL_SUCCESS: - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - self.statement = query_string - - - def execdirect(self, query_string): - """Execute a query directly""" - if type(query_string) == unicode: - c_query_string = wchar_type(to_unicode(query_string)) - ret = ODBC_API.SQLExecDirectW(self._stmt_h, c_query_string, len(query_string)) - else: - c_query_string = ctypes.c_char_p(query_string) - ret = ODBC_API.SQLExecDirect(self._stmt_h, c_query_string, len(query_string)) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - self.statement = None - return (self) - - - def callproc(self, procname, args): - 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)] - - print 'pram_io_list: '+str(self._pram_io_list) - - - - call_escape = '{CALL '+procname - if args: - call_escape += '(' + ','.join(['?' for params in args]) + ')' - call_escape += '}' - - self.execute(call_escape, args, call_mode = True) - - result = [] - - for buf, buf_len, sql_type in self._ParamBufferList: - if buf_len.value == -1: - result.append(None) - else: - result.append(self.connection.output_converter[sql_type](buf.value)) - return (result) - - - - def executemany(self, query_string, params_list = [None]): - self.prepare(query_string) - for params in params_list: - self.execute(query_string, params, many_mode = True) - self._NumOfRows() - self.rowcount = -1 - self._UpdateDesc() - #self._BindCols() - - - def _BindParams(self, param_types, pram_io_list = []): - """Create parameter buffers based on param types, and bind them to the statement""" - # Get the number of query parameters judged by database. - NumParams = ctypes.c_short() - ret = ODBC_API.SQLNumParams(self._stmt_h, ADDR(NumParams)) - if ret != SQL_SUCCESS: - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - if len(param_types) != NumParams.value: - # In case number of parameters provided do not same as number required - error_desc = "The SQL contains %d parameter markers, but %d parameters were supplied" \ - %(NumParams.value,len(param_types)) - raise ProgrammingError('HY000',error_desc) - - - # Every parameter needs to be binded to a buffer - ParamBufferList = [] - # Temporary holder since we can only call SQLDescribeParam before - # calling SQLBindParam. - temp_holder = [] - for col_num in range(NumParams.value): - col_size = 0 - buf_size = 512 - - if param_types[col_num] == type(None): - ParameterNumber = ctypes.c_ushort(col_num + 1) - DataType = ctypes.c_short() - ParameterSize = ctypes.c_size_t() - DecimalDigits = ctypes.c_short() - Nullable = ctypes.c_short() - ret = ODBC_API.SQLDescribeParam( - self._stmt_h, - ParameterNumber, - ADDR(DataType), - ADDR(ParameterSize), - ADDR(DecimalDigits), - ADDR(Nullable), - ) - if ret != SQL_SUCCESS: - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - sql_c_type = SQL_C_DEFAULT - sql_type = DataType.value - buf_size = 1 - ParameterBuffer = create_buffer(buf_size) - - elif param_types[col_num] == 'u': - sql_c_type = SQL_C_WCHAR - sql_type = SQL_WLONGVARCHAR - buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500 - ParameterBuffer = create_buffer_u(buf_size) - - elif param_types[col_num] == 's': - sql_c_type = SQL_C_CHAR - sql_type = SQL_LONGVARCHAR - buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500 - ParameterBuffer = create_buffer(buf_size) - - elif type(param_types[col_num]) == tuple: #Decimal - sql_c_type = SQL_C_CHAR - sql_type = SQL_NUMERIC - buf_size = param_types[col_num][0] - - ParameterBuffer = create_buffer(buf_size+4) - col_size = param_types[col_num][1] - if DEBUG: print param_types[col_num][0],param_types[col_num][1] - - # bool subclasses int, thus has to go first - elif issubclass(param_types[col_num], bool): - sql_c_type = SQL_C_CHAR - sql_type = SQL_BIT - buf_size = SQL_data_type_dict[sql_type][4] - ParameterBuffer = create_buffer(buf_size) - - elif issubclass(param_types[col_num], int): - sql_c_type = SQL_C_CHAR - sql_type = SQL_INTEGER - buf_size = SQL_data_type_dict[sql_type][4] - ParameterBuffer = create_buffer(buf_size) - - elif issubclass(param_types[col_num], long): - sql_c_type = SQL_C_CHAR - sql_type = SQL_BIGINT - buf_size = SQL_data_type_dict[sql_type][4] - ParameterBuffer = create_buffer(buf_size) - - - elif issubclass(param_types[col_num], float): - sql_c_type = SQL_C_CHAR - sql_type = SQL_DOUBLE - buf_size = SQL_data_type_dict[sql_type][4] - ParameterBuffer = create_buffer(buf_size) - - - # datetime subclasses date, thus has to go first - elif issubclass(param_types[col_num], datetime.datetime): - sql_c_type = SQL_C_CHAR - sql_type = SQL_TYPE_TIMESTAMP - buf_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0] - ParameterBuffer = create_buffer(buf_size) - col_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][1] - - - elif issubclass(param_types[col_num], datetime.date): - sql_c_type = SQL_C_CHAR - if self.connection.type_size_dic.has_key(SQL_TYPE_DATE): - if DEBUG: print 'conx.type_size_dic.has_key(SQL_TYPE_DATE)' - sql_type = SQL_TYPE_DATE - buf_size = self.connection.type_size_dic[SQL_TYPE_DATE][0] - - ParameterBuffer = create_buffer(buf_size) - col_size = self.connection.type_size_dic[SQL_TYPE_DATE][1] - - else: - # SQL Sever <2008 doesn't have a DATE type. - sql_type = SQL_TYPE_TIMESTAMP - buf_size = 10 - ParameterBuffer = create_buffer(buf_size) - - - elif issubclass(param_types[col_num], datetime.time): - sql_c_type = SQL_C_CHAR - if self.connection.type_size_dic.has_key(SQL_TYPE_TIME): - sql_type = SQL_TYPE_TIME - buf_size = self.connection.type_size_dic[SQL_TYPE_TIME][0] - ParameterBuffer = create_buffer(buf_size) - col_size = self.connection.type_size_dic[SQL_TYPE_TIME][1] - elif self.connection.type_size_dic.has_key(SQL_SS_TIME2): - # TIME type added in SQL Server 2008 - sql_type = SQL_SS_TIME2 - buf_size = self.connection.type_size_dic[SQL_SS_TIME2][0] - ParameterBuffer = create_buffer(buf_size) - col_size = self.connection.type_size_dic[SQL_SS_TIME2][1] - else: - # SQL Sever <2008 doesn't have a TIME type. - sql_type = SQL_TYPE_TIMESTAMP - buf_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0] - ParameterBuffer = create_buffer(buf_size) - col_size = 3 - - elif issubclass(param_types[col_num], unicode): - sql_c_type = SQL_C_WCHAR - sql_type = SQL_WVARCHAR - buf_size = 255 - ParameterBuffer = create_buffer_u(buf_size) - - elif issubclass(param_types[col_num], str): - sql_c_type = SQL_C_CHAR - sql_type = SQL_VARCHAR - buf_size = 255 - ParameterBuffer = create_buffer(buf_size) - - elif issubclass(param_types[col_num], (bytearray, buffer)): - sql_c_type = SQL_C_BINARY - sql_type = SQL_LONGVARBINARY - buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500 - ParameterBuffer = create_buffer(buf_size) - - - else: - sql_c_type = SQL_C_CHAR - sql_type = SQL_LONGVARCHAR - buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500 - ParameterBuffer = create_buffer(buf_size) - - temp_holder.append((sql_c_type, sql_type, buf_size, col_size, ParameterBuffer)) - - for col_num, (sql_c_type, sql_type, buf_size, col_size, ParameterBuffer) in enumerate(temp_holder): - BufferLen = ctypes.c_ssize_t(buf_size) - LenOrIndBuf = ctypes.c_ssize_t() - - - InputOutputType = SQL_PARAM_INPUT - if len(pram_io_list) > col_num: - InputOutputType = pram_io_list[col_num] - - ret = SQLBindParameter(self._stmt_h, col_num + 1, InputOutputType, sql_c_type, sql_type, buf_size,\ - col_size, ADDR(ParameterBuffer), BufferLen,ADDR(LenOrIndBuf)) - if ret != SQL_SUCCESS: - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - # Append the value buffer and the lenth buffer to the array - ParamBufferList.append((ParameterBuffer,LenOrIndBuf,sql_type)) - - self._last_param_types = param_types - self._ParamBufferList = ParamBufferList - - - - def _CreateColBuf(self): - NOC = self._NumOfCols() - self._ColBufferList = [] - self._row_type = None - for col_num in range(NOC): - col_name = self.description[col_num][0] - - col_sql_data_type = self._ColTypeCodeList[col_num] - - # set default size base on the column's sql data type - 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: - total_buf_len = self._outputsize.get(None,total_buf_len) - # over-write if there's preset size value for the "col_num" column - total_buf_len = self._outputsize.get(col_num, total_buf_len) - - - alloc_buffer = SQL_data_type_dict[col_sql_data_type][3](total_buf_len) - - used_buf_len = ctypes.c_ssize_t() - - target_type = SQL_data_type_dict[col_sql_data_type][2] - force_unicode = self.connection.unicode_results - - if force_unicode and col_sql_data_type in (SQL_CHAR,SQL_VARCHAR,SQL_LONGVARCHAR): - target_type = SQL_C_WCHAR - alloc_buffer = create_buffer_u(total_buf_len) - - buf_cvt_func = self.connection.output_converter[self._ColTypeCodeList[col_num]] - - self._ColBufferList.append([col_name, target_type, used_buf_len, alloc_buffer, total_buf_len, buf_cvt_func]) - - - def _GetData(self): - '''Bind buffers for the record set columns''' - - # Lazily create the row type on first fetch. - if self._row_type is None: - self._row_type = self.row_type_callable(self) - - value_list = [] - col_num = 0 - for col_name, target_type, used_buf_len, alloc_buffer, total_buf_len, buf_cvt_func in self._ColBufferList: - - blocks = [] - while True: - ret = ODBC_API.SQLGetData(self._stmt_h, col_num + 1, target_type, ADDR(alloc_buffer), total_buf_len,\ - ADDR(used_buf_len)) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - if ret == SQL_SUCCESS: - if used_buf_len.value == SQL_NULL_DATA: - blocks.append(None) - else: - if target_type == SQL_C_BINARY: - blocks.append(alloc_buffer.raw[:used_buf_len.value]) - elif target_type == SQL_C_WCHAR: - blocks.append(from_buffer_u(alloc_buffer)) - else: - #print col_name, target_type, alloc_buffer.value - blocks.append(alloc_buffer.value) - - break - - if ret == SQL_SUCCESS_WITH_INFO: - if target_type == SQL_C_BINARY: - blocks.append(alloc_buffer.raw) - else: - blocks.append(alloc_buffer.value) - - if ret == SQL_NO_DATA: - break - - - if len(blocks) == 1: - raw_value = blocks[0] - else: - raw_value = ''.join(blocks) - - if raw_value == None: - value_list.append(None) - else: - value_list.append(buf_cvt_func(raw_value)) - col_num += 1 - - return self._row_type(value_list) - - - def _UpdateDesc(self): - "Get the information of (name, type_code, display_size, internal_size, col_precision, scale, null_ok)" - Cname = create_buffer(1024) - Cname_ptr = ctypes.c_short() - Ctype_code = ctypes.c_short() - Csize = ctypes.c_size_t() - Cdisp_size = ctypes.c_ssize_t(0) - CDecimalDigits = ctypes.c_short() - Cnull_ok = ctypes.c_short() - ColDescr = [] - self._ColTypeCodeList = [] - NOC = self._NumOfCols() - for col in range(1, NOC+1): - ret = ODBC_API.SQLColAttribute(self._stmt_h, col, SQL_DESC_DISPLAY_SIZE, ADDR(create_buffer(10)), - 10, ADDR(ctypes.c_short()),ADDR(Cdisp_size)) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - ret = ODBC_API.SQLDescribeCol(self._stmt_h, col, Cname, len(Cname), ADDR(Cname_ptr),\ - ADDR(Ctype_code),ADDR(Csize),ADDR(CDecimalDigits), ADDR(Cnull_ok)) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - col_name = Cname.value - if lowercase: - col_name = str.lower(col_name) - #(name, type_code, display_size, - # internal_size, col_precision, scale, null_ok) - ColDescr.append((col_name, SQL_data_type_dict.get(Ctype_code.value,(Ctype_code.value))[0],Cdisp_size.value,\ - Csize.value, Csize.value,CDecimalDigits.value,Cnull_ok.value == 1 and True or False)) - self._ColTypeCodeList.append(Ctype_code.value) - - if len(ColDescr) > 0: - self.description = ColDescr - else: - self.description = None - self._CreateColBuf() - - - def _NumOfRows(self): - """Get the number of rows""" - NOR = ctypes.c_ssize_t() - ret = ODBC_API.SQLRowCount(self._stmt_h, ADDR(NOR)) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - self.rowcount = NOR.value - return self.rowcount - - - def _NumOfCols(self): - """Get the number of cols""" - NOC = ctypes.c_short() - ret = ODBC_API.SQLNumResultCols(self._stmt_h, ADDR(NOC)) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - return NOC.value - - - def fetchall(self): - rows = [] - while True: - row = self.fetchone() - if row == None: - break - rows.append(row) - return rows - - - def fetchmany(self, num = None): - if num == None: - num = self.arraysize - rows, row_num = [], 0 - - while row_num < num: - row = self.fetchone() - if row == None: - break - rows.append(row) - row_num += 1 - return rows - - - def fetchone(self): - ret = SQLFetch(self._stmt_h) - if ret == SQL_SUCCESS: - return self._GetData() - else: - if ret == SQL_NO_DATA_FOUND: - return None - else: - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - def next(self): - row = self.fetchone() - if row == None: - raise(StopIteration) - return row - - def __iter__(self): - return self - - - def skip(self, count = 0): - for i in xrange(count): - ret = ODBC_API.SQLFetchScroll(self._stmt_h, SQL_FETCH_NEXT, 0) - if ret != SQL_SUCCESS: - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - return None - - - - def nextset(self): - ret = ODBC_API.SQLMoreResults(self._stmt_h) - if ret not in (SQL_SUCCESS, SQL_NO_DATA): - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - if ret == SQL_NO_DATA: - self._free_results('FREE_STATEMENT') - return False - else: - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - return True - - - def _free_results(self, free_statement): - if not self.connection.connected: - raise ProgrammingError('HY000','Attempt to use a closed connection.') - - self.description = None - if free_statement == 'FREE_STATEMENT': - ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_CLOSE) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - else: - ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_UNBIND) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_RESET_PARAMS) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self.rowcount = -1 - - - - def getTypeInfo(self, sqlType = None): - if sqlType == None: - type = SQL_ALL_TYPES - else: - type = sqlType - ret = ODBC_API.SQLGetTypeInfo(self._stmt_h, type) - if ret in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO): - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - return self.fetchone() - - - def tables(self, table=None, catalog=None, schema=None, tableType=None): - """Return a list with all tables""" - l_catalog = l_schema = l_table = l_tableType = 0 - - if catalog != None: - l_catalog = len(catalog) - catalog = ctypes.c_char_p(catalog) - - if schema != None: - l_schema = len(schema) - schema = ctypes.c_char_p(schema) - - if table != None: - l_table = len(table) - table = ctypes.c_char_p(table) - - if tableType != None: - l_tableType = len(tableType) - tableType = ctypes.c_char_p(tableType) - - self._free_results('FREE_STATEMENT') - self.statement = None - ret = ODBC_API.SQLTables(self._stmt_h, - catalog, l_catalog, - schema, l_schema, - table, l_table, - tableType, l_tableType) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - return (self) - - - def columns(self, table=None, catalog=None, schema=None, column=None): - """Return a list with all columns""" - l_catalog = l_schema = l_table = l_column = 0 - if catalog != None: - l_catalog = len(catalog) - catalog = ctypes.c_char_p(catalog) - if schema != None: - l_schema = len(schema) - schema = ctypes.c_char_p(schema) - if table != None: - l_table = len(table) - table = ctypes.c_char_p(table) - if column != None: - l_column = len(column) - column = ctypes.c_char_p(column) - - self._free_results('FREE_STATEMENT') - self.statement = None - - ret = ODBC_API.SQLColumns(self._stmt_h, - catalog, l_catalog, - schema, l_schema, - table, l_table, - column, l_column) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - return (self) - - - def primaryKeys(self, table=None, catalog=None, schema=None): - l_catalog = l_schema = l_table = 0 - if catalog != None: - l_catalog = len(catalog) - catalog = ctypes.c_char_p(catalog) - - if schema != None: - l_schema = len(schema) - schema = ctypes.c_char_p(schema) - - if table != None: - l_table = len(table) - table = ctypes.c_char_p(table) - - self._free_results('FREE_STATEMENT') - self.statement = None - - ret = ODBC_API.SQLPrimaryKeys(self._stmt_h, - catalog, l_catalog, - schema, l_schema, - table, l_table) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - return (self) - - - def foreignKeys(self, table=None, catalog=None, schema=None, foreignTable=None, foreignCatalog=None, foreignSchema=None): - l_catalog = l_schema = l_table = l_foreignTable = l_foreignCatalog = l_foreignSchema = 0 - if catalog != None: - l_catalog = len(catalog) - catalog = ctypes.c_char_p(catalog) - if schema != None: - l_schema = len(schema) - schema = ctypes.c_char_p(schema) - if table != None: - l_table = len(table) - table = ctypes.c_char_p(table) - if foreignTable != None: - l_foreignTable = len(foreignTable) - foreignTable = ctypes.c_char_p(foreignTable) - if foreignCatalog != None: - l_foreignCatalog = len(foreignCatalog) - foreignCatalog = ctypes.c_char_p(foreignCatalog) - if foreignSchema != None: - l_foreignSchema = len(foreignSchema) - foreignSchema = ctypes.c_char_p(foreignSchema) - - self._free_results('FREE_STATEMENT') - self.statement = None - - ret = ODBC_API.SQLForeignKeys(self._stmt_h, - catalog, l_catalog, - schema, l_schema, - table, l_table, - foreignCatalog, l_foreignCatalog, - foreignSchema, l_foreignSchema, - foreignTable, l_foreignTable) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - return (self) - - - def procedurecolumns(self, procedure=None, catalog=None, schema=None, column=None): - l_catalog = l_schema = l_procedure = l_column = 0 - if catalog != None: - l_catalog = len(catalog) - catalog = ctypes.c_char_p(catalog) - if schema != None: - l_schema = len(schema) - schema = ctypes.c_char_p(schema) - if procedure != None: - l_procedure = len(procedure) - procedure = ctypes.c_char_p(procedure) - if column != None: - l_column = len(column) - column = ctypes.c_char_p(column) - - - self._free_results('FREE_STATEMENT') - self.statement = None - - ret = ODBC_API.SQLProcedureColumns(self._stmt_h, - catalog, l_catalog, - schema, l_schema, - procedure, l_procedure, - column, l_column) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self._NumOfRows() - self._UpdateDesc() - return (self) - - - def procedures(self, procedure=None, catalog=None, schema=None): - l_catalog = l_schema = l_procedure = 0 - if catalog != None: - l_catalog = len(catalog) - catalog = ctypes.c_char_p(catalog) - if schema != None: - l_schema = len(schema) - schema = ctypes.c_char_p(schema) - if procedure != None: - l_procedure = len(procedure) - procedure = ctypes.c_char_p(procedure) - - - self._free_results('FREE_STATEMENT') - self.statement = None - - ret = ODBC_API.SQLProcedures(self._stmt_h, - catalog, l_catalog, - schema, l_schema, - procedure, l_procedure) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self._NumOfRows() - self._UpdateDesc() - return (self) - - - def statistics(self, table, catalog=None, schema=None, unique=False, quick=True): - l_table = l_catalog = l_schema = 0 - - if catalog != None: - l_catalog = len(catalog) - catalog = ctypes.c_char_p(catalog) - if schema != None: - l_schema = len(schema) - schema = ctypes.c_char_p(schema) - if table != None: - l_table = len(table) - table = ctypes.c_char_p(table) - - if unique: - Unique = SQL_INDEX_UNIQUE - else: - Unique = SQL_INDEX_ALL - if quick: - Reserved = SQL_QUICK - else: - Reserved = SQL_ENSURE - - self._free_results('FREE_STATEMENT') - self.statement = None - - ret = ODBC_API.SQLStatistics(self._stmt_h, - catalog, l_catalog, - schema, l_schema, - table, l_table, - Unique, Reserved) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self._NumOfRows() - self._UpdateDesc() - #self._BindCols() - return (self) - - - def commit(self): - self.connection.commit() - - def rollback(self): - self.connection.rollback() - - def setoutputsize(self, size, column = None): - self._outputsize[column] = size - - def setinputsizes(self, sizes): - self._inputsizers = [size for size in sizes] - - - def close(self): - """ Call SQLCloseCursor API to free the statement handle""" -# ret = ODBC_API.SQLCloseCursor(self._stmt_h) -# validate(ret, SQL_HANDLE_STMT, self._stmt_h) -# - ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_CLOSE) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_UNBIND) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_RESET_PARAMS) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_STMT, self._stmt_h) - validate(ret, SQL_HANDLE_STMT, self._stmt_h) - - self.closed = True - - - - def __del__(self): - if not self.closed: - if DEBUG: print 'auto closing cursor: ', - try: - self.close() - except: - if DEBUG: print 'failed' - pass - else: - if DEBUG: print 'succeed' - pass - - def __exit__(self, type, value, traceback): - if value: - self.rollback() - else: - self.commit() - - self.close() - - - def __enter__(self): - return self - - -# This class implement a odbc connection. -# -# - -class Connection: - def __init__(self, connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False, **kargs): - """Init variables and connect to the engine""" - self.connected = 0 - self.type_size_dic = {} - self.unicode_results = False - self.dbc_h = ctypes.c_void_p() - self.autocommit = autocommit - self.readonly = False - self.timeout = 0 - - for key, value in kargs.items(): - connectString = connectString + key + '=' + value + ';' - self.connectString = connectString - - - self.clear_output_converters() - - try: - lock.acquire() - if shared_env_h == None: - #Initialize an enviroment if it is not created. - AllocateEnv() - finally: - lock.release() - - # Allocate an DBC handle self.dbc_h under the environment shared_env_h - # This DBC handle is actually the basis of a "connection" - # The handle of self.dbc_h will be used to connect to a certain source - # in the self.connect and self.ConnectByDSN method - - ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_DBC, shared_env_h, ADDR(self.dbc_h)) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - - self.connect(connectString, autocommit, ansi, timeout, unicode_results, readonly) - - - - def connect(self, connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False): - """Connect to odbc, using connect strings and set the connection's attributes like autocommit and timeout - by calling SQLSetConnectAttr - """ - - # Before we establish the connection by the connection string - # Set the connection's attribute of "timeout" (Actully LOGIN_TIMEOUT) - if timeout != 0: - ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_LOGIN_TIMEOUT, timeout, SQL_IS_UINTEGER); - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - - - # Create one connection with a connect string by calling SQLDriverConnect - # and make self.dbc_h the handle of this connection - - - # Convert the connetsytring to encoded string - # so it can be converted to a ctypes c_char array object - - - - if not ansi: - c_connectString = wchar_type(to_unicode(self.connectString)) - odbc_func = ODBC_API.SQLDriverConnectW - else: - c_connectString = ctypes.c_char_p(self.connectString) - odbc_func = ODBC_API.SQLDriverConnect - - # With unixODBC, SQLDriverConnect will intermittently fail with error: - # [01000] [unixODBC][Driver Manager]Can't open lib '/path/to/so' : file not found" - # or: - # [01000] [unixODBC][Driver Manager]Can't open lib '/path/to/so' : (null)" - # when called concurrently by more than one threads. So, we have to - # use a lock to serialize the calls. By the way, the error is much - # less likely to happen if ODBC Tracing is enabled, likely due to the - # implicit serialization caused by writing to trace file. - if ODBC_API._name != 'odbc32': - try: - lock.acquire() - ret = odbc_func(self.dbc_h, 0, c_connectString, len(self.connectString), None, 0, None, SQL_DRIVER_NOPROMPT) - finally: - lock.release() - else: - ret = odbc_func(self.dbc_h, 0, c_connectString, len(self.connectString), None, 0, None, SQL_DRIVER_NOPROMPT) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - - - # Set the connection's attribute of "autocommit" - # - self.autocommit = autocommit - - if self.autocommit == True: - ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER) - else: - ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER) - - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - - # Set the connection's attribute of "readonly" - # - self.readonly = readonly - - ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_ACCESS_MODE, self.readonly and SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE, SQL_IS_UINTEGER) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - - self.unicode_results = unicode_results - self.update_type_size_info() - self.connected = 1 - - def clear_output_converters(self): - self.output_converter = {} - for sqltype, profile in SQL_data_type_dict.items(): - self.output_converter[sqltype] = profile[1] - - - def add_output_converter(self, sqltype, func): - self.output_converter[sqltype] = func - - def settimeout(self, timeout): - ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_CONNECTION_TIMEOUT, timeout, SQL_IS_UINTEGER); - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - self.timeout = timeout - - - def ConnectByDSN(self, dsn, user, passwd = ''): - """Connect to odbc, we need dsn, user and optionally password""" - self.dsn = dsn - self.user = user - self.passwd = passwd - - sn = create_buffer(dsn) - un = create_buffer(user) - pw = create_buffer(passwd) - - ret = ODBC_API.SQLConnect(self.dbc_h, sn, len(sn), un, len(un), pw, len(pw)) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - - self.update_type_size_info() - self.connected = 1 - - - def cursor(self, row_type_callable=None): - #self.settimeout(self.timeout) - if not self.connected: - raise ProgrammingError('HY000','Attempt to use a closed connection.') - - - return Cursor(self, row_type_callable=row_type_callable) - - def update_type_size_info(self): - for sql_type in ( - SQL_TYPE_TIMESTAMP, - SQL_TYPE_DATE, - SQL_TYPE_TIME, - SQL_SS_TIME2, - ): - cur = Cursor(self) - info_tuple = cur.getTypeInfo(sql_type) - if info_tuple != None: - self.type_size_dic[sql_type] = info_tuple[2], info_tuple[14] - cur.close() - - - def commit(self): - if not self.connected: - raise ProgrammingError('HY000','Attempt to use a closed connection.') - - ret = ODBC_API.SQLEndTran(SQL_HANDLE_DBC, self.dbc_h, SQL_COMMIT); - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - - def rollback(self): - if not self.connected: - raise ProgrammingError('HY000','Attempt to use a closed connection.') - - ret = ODBC_API.SQLEndTran(SQL_HANDLE_DBC, self.dbc_h, SQL_ROLLBACK); - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - - - - def getinfo(self,infotype): - if infotype not in aInfoTypes.keys(): - raise ProgrammingError('HY000','Invalid getinfo value: '+str(infotype)) - - - if aInfoTypes[infotype] == 'GI_UINTEGER': - total_buf_len = 1000 - alloc_buffer = ctypes.c_ulong() - used_buf_len = ctypes.c_short() - ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\ - ADDR(used_buf_len)) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - result = alloc_buffer.value - - elif aInfoTypes[infotype] == 'GI_USMALLINT': - total_buf_len = 1000 - alloc_buffer = ctypes.c_ushort() - used_buf_len = ctypes.c_short() - ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\ - ADDR(used_buf_len)) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - result = alloc_buffer.value - - else: - total_buf_len = 1000 - alloc_buffer = create_buffer(total_buf_len) - used_buf_len = ctypes.c_short() - ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\ - ADDR(used_buf_len)) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - result = alloc_buffer.value - if aInfoTypes[infotype] == 'GI_YESNO': - if result[0] == 'Y': - result = True - else: - result = False - - return result - - def __exit__(self, type, value, traceback): - if value: - self.rollback() - else: - self.commit() - - if self.connected: - self.close() - - def __enter__(self): - return self - - def __del__(self): - if self.connected: - self.close() - - def close(self): - if not self.connected: - raise ProgrammingError('HY000','Attempt to close a closed connection.') - - - if self.connected: - if DEBUG: print 'disconnect' - if not self.autocommit: - self.rollback() - ret = ODBC_API.SQLDisconnect(self.dbc_h) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) - if DEBUG: print 'free dbc' - ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_DBC, self.dbc_h) - validate(ret, SQL_HANDLE_DBC, self.dbc_h) -# if shared_env_h.value: -# if DEBUG: print 'env' -# ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_ENV, shared_env_h) -# validate(ret, SQL_HANDLE_ENV, shared_env_h) - self.connected = 0 - -odbc = Connection -connect = odbc -''' -def connect(connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False, **kargs): - return odbc(connectString, autocommit, ansi, timeout, unicode_results, readonly, kargs) -''' - -def win_create_mdb(mdb_path, sort_order = "General\0\0"): - if sys.platform not in ('win32','cli'): - raise Exception('This function is available for use in Windows only.') - #CREATE_DB= - ctypes.windll.ODBCCP32.SQLConfigDataSource.argtypes = [ctypes.c_void_p,ctypes.c_ushort,ctypes.c_char_p,ctypes.c_char_p] - c_Path = "CREATE_DB=" + mdb_path + " " + sort_order - ODBC_ADD_SYS_DSN = 1 - ret = ctypes.windll.ODBCCP32.SQLConfigDataSource(None,ODBC_ADD_SYS_DSN,"Microsoft Access Driver (*.mdb)", c_Path) - if not ret: - raise Exception('Failed to create Access mdb file. Please check file path, permission and Access driver readiness.') - - -def win_compact_mdb(mdb_path, compacted_mdb_path, sort_order = "General\0\0"): - if sys.platform not in ('win32','cli'): - raise Exception('This function is available for use in Windows only.') - #COMPACT_DB= - c_Path = "COMPACT_DB=" + mdb_path + " " + compacted_mdb_path + " " + sort_order - ODBC_ADD_SYS_DSN = 1 - ctypes.windll.ODBCCP32.SQLConfigDataSource.argtypes = [ctypes.c_void_p,ctypes.c_ushort,ctypes.c_char_p,ctypes.c_char_p] - ret = ctypes.windll.ODBCCP32.SQLConfigDataSource(None,ODBC_ADD_SYS_DSN,"Microsoft Access Driver (*.mdb)", c_Path) - if not ret: - raise Exception('Failed to compact Access mdb file. Please check file path, permission and Access driver readiness.') - -def dataSources(): - """Return a list with [name, descrition]""" - dsn = create_buffer(1024) - desc = create_buffer(1024) - dsn_len = ctypes.c_short() - desc_len = ctypes.c_short() - dsn_list = {} - try: - lock.acquire() - if shared_env_h == None: - AllocateEnv() - finally: - lock.release() - while 1: - ret = ODBC_API.SQLDataSources(shared_env_h, SQL_FETCH_NEXT, \ - dsn, len(dsn), ADDR(dsn_len), desc, len(desc), ADDR(desc_len)) - if ret == SQL_NO_DATA_FOUND: - break - elif not ret in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO): - ctrl_err(SQL_HANDLE_ENV, shared_env_h, ret) - else: - dsn_list[dsn.value] = desc.value - return dsn_list +# -*- coding: utf-8 -*- + +# PyPyODBC is develped from RealPyODBC 0.1 beta released in 2004 by Michele Petrazzo. Thanks Michele. + +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +# documentation files (the "Software"), to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions +# of the Software. +# +# 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 +# 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 +# DEALINGS IN THE SOFTWARE. + +import sys, os, datetime, ctypes, threading +from decimal import Decimal + +try: + bytearray +except NameError: + # pre version 2.6 python does not have the bytearray type + bytearray = str + +if not hasattr(ctypes, 'c_ssize_t'): + if ctypes.sizeof(ctypes.c_uint) == ctypes.sizeof(ctypes.c_void_p): + ctypes.c_ssize_t = ctypes.c_int + elif ctypes.sizeof(ctypes.c_ulong) == ctypes.sizeof(ctypes.c_void_p): + ctypes.c_ssize_t = ctypes.c_long + elif ctypes.sizeof(ctypes.c_ulonglong) == ctypes.sizeof(ctypes.c_void_p): + ctypes.c_ssize_t = ctypes.c_longlong + +DEBUG = 0 +# Comment out all "if DEBUG:" statements like below for production +if DEBUG: print 'DEBUGGING' + +pooling = True +lock = threading.Lock() +shared_env_h = None +apilevel = '2.0' +paramstyle = 'qmark' +threadsafety = 1 +version = '0.9.3' +lowercase=True +SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_wchar) + +#determin the size of Py_UNICODE +#sys.maxunicode > 65536 and 'UCS4' or 'UCS2' +UNICODE_SIZE = sys.maxunicode > 65536 and 4 or 2 + + +# Define ODBC constants. They are widly used in ODBC documents and programs +# They are defined in cpp header files: sql.h sqlext.h sqltypes.h sqlucode.h +# and you can get these files from the mingw32-runtime_3.13-1_all.deb package +SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC2, SQL_OV_ODBC3 = 200, 2, 3 +SQL_DRIVER_NOPROMPT = 0 +SQL_ATTR_CONNECTION_POOLING = 201; SQL_CP_ONE_PER_HENV = 2 + +SQL_FETCH_NEXT, SQL_FETCH_FIRST, SQL_FETCH_LAST = 0x01, 0x02, 0x04 +SQL_NULL_HANDLE, SQL_HANDLE_ENV, SQL_HANDLE_DBC, SQL_HANDLE_STMT = 0, 1, 2, 3 +SQL_SUCCESS, SQL_SUCCESS_WITH_INFO = 0, 1 +SQL_NO_DATA = 100; SQL_NO_TOTAL = -4 +SQL_ATTR_ACCESS_MODE = SQL_ACCESS_MODE = 101 +SQL_ATTR_AUTOCOMMIT = SQL_AUTOCOMMIT = 102 + +SQL_MODE_DEFAULT = SQL_MODE_READ_WRITE = 0; SQL_MODE_READ_ONLY = 1 +SQL_AUTOCOMMIT_OFF, SQL_AUTOCOMMIT_ON = 0, 1 +SQL_IS_UINTEGER = -5 +SQL_ATTR_LOGIN_TIMEOUT = 103; SQL_ATTR_CONNECTION_TIMEOUT = 113 +SQL_COMMIT, SQL_ROLLBACK = 0, 1 + +SQL_INDEX_UNIQUE,SQL_INDEX_ALL = 0,1 +SQL_QUICK,SQL_ENSURE = 0,1 +SQL_FETCH_NEXT = 1 +SQL_COLUMN_DISPLAY_SIZE = 6 +SQL_INVALID_HANDLE = -2 +SQL_NO_DATA_FOUND = 100; SQL_NULL_DATA = -1; SQL_NTS = -3 +SQL_HANDLE_DESCR = 4 +SQL_TABLE_NAMES = 3 +SQL_PARAM_INPUT = 1; SQL_PARAM_INPUT_OUTPUT = 2 +SQL_PARAM_TYPE_UNKNOWN = 0 +SQL_RESULT_COL = 3 +SQL_PARAM_OUTPUT = 4 +SQL_RETURN_VALUE = 5 +SQL_PARAM_TYPE_DEFAULT = SQL_PARAM_INPUT_OUTPUT + +SQL_RESET_PARAMS = 3 +SQL_UNBIND = 2 +SQL_CLOSE = 0 + +SQL_TYPE_NULL = 0 +SQL_DECIMAL = 3 +SQL_FLOAT = 6 +SQL_DATE = 9 +SQL_TIME = 10 +SQL_TIMESTAMP = 11 +SQL_VARCHAR = 12 +SQL_LONGVARCHAR = -1 +SQL_VARBINARY = -3 +SQL_LONGVARBINARY = -4 +SQL_BIGINT = -5 +SQL_WVARCHAR = -9 +SQL_WLONGVARCHAR = -10 +SQL_ALL_TYPES = 0 +SQL_SIGNED_OFFSET = -20 + +SQL_C_CHAR = SQL_CHAR = 1 +SQL_C_NUMERIC = SQL_NUMERIC = 2 +SQL_C_LONG = SQL_INTEGER = 4 +SQL_C_SLONG = SQL_C_LONG + SQL_SIGNED_OFFSET +SQL_C_SHORT = SQL_SMALLINT = 5 +SQL_C_FLOAT = SQL_REAL = 7 +SQL_C_DOUBLE = SQL_DOUBLE = 8 +SQL_C_TYPE_DATE = SQL_TYPE_DATE = 91 +SQL_C_TYPE_TIME = SQL_TYPE_TIME = 92 +SQL_C_BINARY = SQL_BINARY = -2 +SQL_C_SBIGINT = SQL_BIGINT + SQL_SIGNED_OFFSET +SQL_C_TINYINT = SQL_TINYINT = -6 +SQL_C_BIT = SQL_BIT = -7 +SQL_C_WCHAR = SQL_WCHAR = -8 +SQL_C_GUID = SQL_GUID = -11 +SQL_C_TYPE_TIMESTAMP = SQL_TYPE_TIMESTAMP = 93 +SQL_C_DEFAULT = 99 + +SQL_SS_TIME2 = -154 + +SQL_DESC_DISPLAY_SIZE = SQL_COLUMN_DISPLAY_SIZE + + +def dttm_cvt(x): + 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'))) + +def tm_cvt(x): + if x == '': return None + else: return datetime.time(int(x[0:2]),int(x[3:5]),int(x[6:8]),int(x[9:].ljust(6,'0'))) + +def dt_cvt(x): + if x == '': return None + else: return datetime.date(int(x[0:4]),int(x[5:7]),int(x[8:10])) + + +# Below defines The constants for sqlgetinfo method, and their coresponding return types +SQL_QUALIFIER_LOCATION = 114 +SQL_QUALIFIER_NAME_SEPARATOR = 41 +SQL_QUALIFIER_TERM = 42 +SQL_QUALIFIER_USAGE = 92 +SQL_OWNER_TERM = 39 +SQL_OWNER_USAGE = 91 +SQL_ACCESSIBLE_PROCEDURES = 20 +SQL_ACCESSIBLE_TABLES = 19 +SQL_ACTIVE_ENVIRONMENTS = 116 +SQL_AGGREGATE_FUNCTIONS = 169 +SQL_ALTER_DOMAIN = 117 +SQL_ALTER_TABLE = 86 +SQL_ASYNC_MODE = 10021 +SQL_BATCH_ROW_COUNT = 120 +SQL_BATCH_SUPPORT = 121 +SQL_BOOKMARK_PERSISTENCE = 82 +SQL_CATALOG_LOCATION = SQL_QUALIFIER_LOCATION +SQL_CATALOG_NAME = 10003 +SQL_CATALOG_NAME_SEPARATOR = SQL_QUALIFIER_NAME_SEPARATOR +SQL_CATALOG_TERM = SQL_QUALIFIER_TERM +SQL_CATALOG_USAGE = SQL_QUALIFIER_USAGE +SQL_COLLATION_SEQ = 10004 +SQL_COLUMN_ALIAS = 87 +SQL_CONCAT_NULL_BEHAVIOR = 22 +SQL_CONVERT_FUNCTIONS = 48 +SQL_CONVERT_VARCHAR = 70 +SQL_CORRELATION_NAME = 74 +SQL_CREATE_ASSERTION = 127 +SQL_CREATE_CHARACTER_SET = 128 +SQL_CREATE_COLLATION = 129 +SQL_CREATE_DOMAIN = 130 +SQL_CREATE_SCHEMA = 131 +SQL_CREATE_TABLE = 132 +SQL_CREATE_TRANSLATION = 133 +SQL_CREATE_VIEW = 134 +SQL_CURSOR_COMMIT_BEHAVIOR = 23 +SQL_CURSOR_ROLLBACK_BEHAVIOR = 24 +SQL_DATABASE_NAME = 16 +SQL_DATA_SOURCE_NAME = 2 +SQL_DATA_SOURCE_READ_ONLY = 25 +SQL_DATETIME_LITERALS = 119 +SQL_DBMS_NAME = 17 +SQL_DBMS_VER = 18 +SQL_DDL_INDEX = 170 +SQL_DEFAULT_TXN_ISOLATION = 26 +SQL_DESCRIBE_PARAMETER = 10002 +SQL_DM_VER = 171 +SQL_DRIVER_NAME = 6 +SQL_DRIVER_ODBC_VER = 77 +SQL_DRIVER_VER = 7 +SQL_DROP_ASSERTION = 136 +SQL_DROP_CHARACTER_SET = 137 +SQL_DROP_COLLATION = 138 +SQL_DROP_DOMAIN = 139 +SQL_DROP_SCHEMA = 140 +SQL_DROP_TABLE = 141 +SQL_DROP_TRANSLATION = 142 +SQL_DROP_VIEW = 143 +SQL_DYNAMIC_CURSOR_ATTRIBUTES1 = 144 +SQL_DYNAMIC_CURSOR_ATTRIBUTES2 = 145 +SQL_EXPRESSIONS_IN_ORDERBY = 27 +SQL_FILE_USAGE = 84 +SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 = 146 +SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 = 147 +SQL_GETDATA_EXTENSIONS = 81 +SQL_GROUP_BY = 88 +SQL_IDENTIFIER_CASE = 28 +SQL_IDENTIFIER_QUOTE_CHAR = 29 +SQL_INDEX_KEYWORDS = 148 +SQL_INFO_SCHEMA_VIEWS = 149 +SQL_INSERT_STATEMENT = 172 +SQL_INTEGRITY = 73 +SQL_KEYSET_CURSOR_ATTRIBUTES1 = 150 +SQL_KEYSET_CURSOR_ATTRIBUTES2 = 151 +SQL_KEYWORDS = 89 +SQL_LIKE_ESCAPE_CLAUSE = 113 +SQL_MAX_ASYNC_CONCURRENT_STATEMENTS = 10022 +SQL_MAX_BINARY_LITERAL_LEN = 112 +SQL_MAX_CATALOG_NAME_LEN = 34 +SQL_MAX_CHAR_LITERAL_LEN = 108 +SQL_MAX_COLUMNS_IN_GROUP_BY = 97 +SQL_MAX_COLUMNS_IN_INDEX = 98 +SQL_MAX_COLUMNS_IN_ORDER_BY = 99 +SQL_MAX_COLUMNS_IN_SELECT = 100 +SQL_MAX_COLUMNS_IN_TABLE = 101 +SQL_MAX_COLUMN_NAME_LEN = 30 +SQL_MAX_CONCURRENT_ACTIVITIES = 1 +SQL_MAX_CURSOR_NAME_LEN = 31 +SQL_MAX_DRIVER_CONNECTIONS = 0 +SQL_MAX_IDENTIFIER_LEN = 10005 +SQL_MAX_INDEX_SIZE = 102 +SQL_MAX_PROCEDURE_NAME_LEN = 33 +SQL_MAX_ROW_SIZE = 104 +SQL_MAX_ROW_SIZE_INCLUDES_LONG = 103 +SQL_MAX_SCHEMA_NAME_LEN = 32 +SQL_MAX_STATEMENT_LEN = 105 +SQL_MAX_TABLES_IN_SELECT = 106 +SQL_MAX_TABLE_NAME_LEN = 35 +SQL_MAX_USER_NAME_LEN = 107 +SQL_MULTIPLE_ACTIVE_TXN = 37 +SQL_MULT_RESULT_SETS = 36 +SQL_NEED_LONG_DATA_LEN = 111 +SQL_NON_NULLABLE_COLUMNS = 75 +SQL_NULL_COLLATION = 85 +SQL_NUMERIC_FUNCTIONS = 49 +SQL_ODBC_INTERFACE_CONFORMANCE = 152 +SQL_ODBC_VER = 10 +SQL_OJ_CAPABILITIES = 65003 +SQL_ORDER_BY_COLUMNS_IN_SELECT = 90 +SQL_PARAM_ARRAY_ROW_COUNTS = 153 +SQL_PARAM_ARRAY_SELECTS = 154 +SQL_PROCEDURES = 21 +SQL_PROCEDURE_TERM = 40 +SQL_QUOTED_IDENTIFIER_CASE = 93 +SQL_ROW_UPDATES = 11 +SQL_SCHEMA_TERM = SQL_OWNER_TERM +SQL_SCHEMA_USAGE = SQL_OWNER_USAGE +SQL_SCROLL_OPTIONS = 44 +SQL_SEARCH_PATTERN_ESCAPE = 14 +SQL_SERVER_NAME = 13 +SQL_SPECIAL_CHARACTERS = 94 +SQL_SQL92_DATETIME_FUNCTIONS = 155 +SQL_SQL92_FOREIGN_KEY_DELETE_RULE = 156 +SQL_SQL92_FOREIGN_KEY_UPDATE_RULE = 157 +SQL_SQL92_GRANT = 158 +SQL_SQL92_NUMERIC_VALUE_FUNCTIONS = 159 +SQL_SQL92_PREDICATES = 160 +SQL_SQL92_RELATIONAL_JOIN_OPERATORS = 161 +SQL_SQL92_REVOKE = 162 +SQL_SQL92_ROW_VALUE_CONSTRUCTOR = 163 +SQL_SQL92_STRING_FUNCTIONS = 164 +SQL_SQL92_VALUE_EXPRESSIONS = 165 +SQL_SQL_CONFORMANCE = 118 +SQL_STANDARD_CLI_CONFORMANCE = 166 +SQL_STATIC_CURSOR_ATTRIBUTES1 = 167 +SQL_STATIC_CURSOR_ATTRIBUTES2 = 168 +SQL_STRING_FUNCTIONS = 50 +SQL_SUBQUERIES = 95 +SQL_SYSTEM_FUNCTIONS = 51 +SQL_TABLE_TERM = 45 +SQL_TIMEDATE_ADD_INTERVALS = 109 +SQL_TIMEDATE_DIFF_INTERVALS = 110 +SQL_TIMEDATE_FUNCTIONS = 52 +SQL_TXN_CAPABLE = 46 +SQL_TXN_ISOLATION_OPTION = 72 +SQL_UNION = 96 +SQL_USER_NAME = 47 +SQL_XOPEN_CLI_YEAR = 10000 + + +aInfoTypes = { +SQL_ACCESSIBLE_PROCEDURES : 'GI_YESNO',SQL_ACCESSIBLE_TABLES : 'GI_YESNO',SQL_ACTIVE_ENVIRONMENTS : 'GI_USMALLINT', +SQL_AGGREGATE_FUNCTIONS : 'GI_UINTEGER',SQL_ALTER_DOMAIN : 'GI_UINTEGER', +SQL_ALTER_TABLE : 'GI_UINTEGER',SQL_ASYNC_MODE : 'GI_UINTEGER',SQL_BATCH_ROW_COUNT : 'GI_UINTEGER', +SQL_BATCH_SUPPORT : 'GI_UINTEGER',SQL_BOOKMARK_PERSISTENCE : 'GI_UINTEGER',SQL_CATALOG_LOCATION : 'GI_USMALLINT', +SQL_CATALOG_NAME : 'GI_YESNO',SQL_CATALOG_NAME_SEPARATOR : 'GI_STRING',SQL_CATALOG_TERM : 'GI_STRING', +SQL_CATALOG_USAGE : 'GI_UINTEGER',SQL_COLLATION_SEQ : 'GI_STRING',SQL_COLUMN_ALIAS : 'GI_YESNO', +SQL_CONCAT_NULL_BEHAVIOR : 'GI_USMALLINT',SQL_CONVERT_FUNCTIONS : 'GI_UINTEGER', +SQL_CONVERT_VARCHAR : 'GI_UINTEGER',SQL_CORRELATION_NAME : 'GI_USMALLINT', +SQL_CREATE_ASSERTION : 'GI_UINTEGER',SQL_CREATE_CHARACTER_SET : 'GI_UINTEGER', +SQL_CREATE_COLLATION : 'GI_UINTEGER',SQL_CREATE_DOMAIN : 'GI_UINTEGER',SQL_CREATE_SCHEMA : 'GI_UINTEGER', +SQL_CREATE_TABLE : 'GI_UINTEGER',SQL_CREATE_TRANSLATION : 'GI_UINTEGER',SQL_CREATE_VIEW : 'GI_UINTEGER', +SQL_CURSOR_COMMIT_BEHAVIOR : 'GI_USMALLINT',SQL_CURSOR_ROLLBACK_BEHAVIOR : 'GI_USMALLINT',SQL_DATABASE_NAME : 'GI_STRING', +SQL_DATA_SOURCE_NAME : 'GI_STRING',SQL_DATA_SOURCE_READ_ONLY : 'GI_YESNO',SQL_DATETIME_LITERALS : 'GI_UINTEGER', +SQL_DBMS_NAME : 'GI_STRING',SQL_DBMS_VER : 'GI_STRING',SQL_DDL_INDEX : 'GI_UINTEGER', +SQL_DEFAULT_TXN_ISOLATION : 'GI_UINTEGER',SQL_DESCRIBE_PARAMETER : 'GI_YESNO',SQL_DM_VER : 'GI_STRING', +SQL_DRIVER_NAME : 'GI_STRING',SQL_DRIVER_ODBC_VER : 'GI_STRING',SQL_DRIVER_VER : 'GI_STRING', +SQL_DROP_ASSERTION : 'GI_UINTEGER',SQL_DROP_CHARACTER_SET : 'GI_UINTEGER', +SQL_DROP_COLLATION : 'GI_UINTEGER',SQL_DROP_DOMAIN : 'GI_UINTEGER', +SQL_DROP_SCHEMA : 'GI_UINTEGER',SQL_DROP_TABLE : 'GI_UINTEGER',SQL_DROP_TRANSLATION : 'GI_UINTEGER', +SQL_DROP_VIEW : 'GI_UINTEGER',SQL_DYNAMIC_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_DYNAMIC_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER', +SQL_EXPRESSIONS_IN_ORDERBY : 'GI_YESNO',SQL_FILE_USAGE : 'GI_USMALLINT', +SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER', +SQL_GETDATA_EXTENSIONS : 'GI_UINTEGER',SQL_GROUP_BY : 'GI_USMALLINT',SQL_IDENTIFIER_CASE : 'GI_USMALLINT', +SQL_IDENTIFIER_QUOTE_CHAR : 'GI_STRING',SQL_INDEX_KEYWORDS : 'GI_UINTEGER',SQL_INFO_SCHEMA_VIEWS : 'GI_UINTEGER', +SQL_INSERT_STATEMENT : 'GI_UINTEGER',SQL_INTEGRITY : 'GI_YESNO',SQL_KEYSET_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER', +SQL_KEYSET_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER',SQL_KEYWORDS : 'GI_STRING', +SQL_LIKE_ESCAPE_CLAUSE : 'GI_YESNO',SQL_MAX_ASYNC_CONCURRENT_STATEMENTS : 'GI_UINTEGER', +SQL_MAX_BINARY_LITERAL_LEN : 'GI_UINTEGER',SQL_MAX_CATALOG_NAME_LEN : 'GI_USMALLINT', +SQL_MAX_CHAR_LITERAL_LEN : 'GI_UINTEGER',SQL_MAX_COLUMNS_IN_GROUP_BY : 'GI_USMALLINT', +SQL_MAX_COLUMNS_IN_INDEX : 'GI_USMALLINT',SQL_MAX_COLUMNS_IN_ORDER_BY : 'GI_USMALLINT', +SQL_MAX_COLUMNS_IN_SELECT : 'GI_USMALLINT',SQL_MAX_COLUMNS_IN_TABLE : 'GI_USMALLINT', +SQL_MAX_COLUMN_NAME_LEN : 'GI_USMALLINT',SQL_MAX_CONCURRENT_ACTIVITIES : 'GI_USMALLINT', +SQL_MAX_CURSOR_NAME_LEN : 'GI_USMALLINT',SQL_MAX_DRIVER_CONNECTIONS : 'GI_USMALLINT', +SQL_MAX_IDENTIFIER_LEN : 'GI_USMALLINT',SQL_MAX_INDEX_SIZE : 'GI_UINTEGER', +SQL_MAX_PROCEDURE_NAME_LEN : 'GI_USMALLINT',SQL_MAX_ROW_SIZE : 'GI_UINTEGER', +SQL_MAX_ROW_SIZE_INCLUDES_LONG : 'GI_YESNO',SQL_MAX_SCHEMA_NAME_LEN : 'GI_USMALLINT', +SQL_MAX_STATEMENT_LEN : 'GI_UINTEGER',SQL_MAX_TABLES_IN_SELECT : 'GI_USMALLINT', +SQL_MAX_TABLE_NAME_LEN : 'GI_USMALLINT',SQL_MAX_USER_NAME_LEN : 'GI_USMALLINT', +SQL_MULTIPLE_ACTIVE_TXN : 'GI_YESNO',SQL_MULT_RESULT_SETS : 'GI_YESNO', +SQL_NEED_LONG_DATA_LEN : 'GI_YESNO',SQL_NON_NULLABLE_COLUMNS : 'GI_USMALLINT', +SQL_NULL_COLLATION : 'GI_USMALLINT',SQL_NUMERIC_FUNCTIONS : 'GI_UINTEGER', +SQL_ODBC_INTERFACE_CONFORMANCE : 'GI_UINTEGER',SQL_ODBC_VER : 'GI_STRING',SQL_OJ_CAPABILITIES : 'GI_UINTEGER', +SQL_ORDER_BY_COLUMNS_IN_SELECT : 'GI_YESNO',SQL_PARAM_ARRAY_ROW_COUNTS : 'GI_UINTEGER', +SQL_PARAM_ARRAY_SELECTS : 'GI_UINTEGER',SQL_PROCEDURES : 'GI_YESNO',SQL_PROCEDURE_TERM : 'GI_STRING', +SQL_QUOTED_IDENTIFIER_CASE : 'GI_USMALLINT',SQL_ROW_UPDATES : 'GI_YESNO',SQL_SCHEMA_TERM : 'GI_STRING', +SQL_SCHEMA_USAGE : 'GI_UINTEGER',SQL_SCROLL_OPTIONS : 'GI_UINTEGER',SQL_SEARCH_PATTERN_ESCAPE : 'GI_STRING', +SQL_SERVER_NAME : 'GI_STRING',SQL_SPECIAL_CHARACTERS : 'GI_STRING',SQL_SQL92_DATETIME_FUNCTIONS : 'GI_UINTEGER', +SQL_SQL92_FOREIGN_KEY_DELETE_RULE : 'GI_UINTEGER',SQL_SQL92_FOREIGN_KEY_UPDATE_RULE : 'GI_UINTEGER', +SQL_SQL92_GRANT : 'GI_UINTEGER',SQL_SQL92_NUMERIC_VALUE_FUNCTIONS : 'GI_UINTEGER', +SQL_SQL92_PREDICATES : 'GI_UINTEGER',SQL_SQL92_RELATIONAL_JOIN_OPERATORS : 'GI_UINTEGER', +SQL_SQL92_REVOKE : 'GI_UINTEGER',SQL_SQL92_ROW_VALUE_CONSTRUCTOR : 'GI_UINTEGER', +SQL_SQL92_STRING_FUNCTIONS : 'GI_UINTEGER',SQL_SQL92_VALUE_EXPRESSIONS : 'GI_UINTEGER', +SQL_SQL_CONFORMANCE : 'GI_UINTEGER',SQL_STANDARD_CLI_CONFORMANCE : 'GI_UINTEGER', +SQL_STATIC_CURSOR_ATTRIBUTES1 : 'GI_UINTEGER',SQL_STATIC_CURSOR_ATTRIBUTES2 : 'GI_UINTEGER', +SQL_STRING_FUNCTIONS : 'GI_UINTEGER',SQL_SUBQUERIES : 'GI_UINTEGER', +SQL_SYSTEM_FUNCTIONS : 'GI_UINTEGER',SQL_TABLE_TERM : 'GI_STRING',SQL_TIMEDATE_ADD_INTERVALS : 'GI_UINTEGER', +SQL_TIMEDATE_DIFF_INTERVALS : 'GI_UINTEGER',SQL_TIMEDATE_FUNCTIONS : 'GI_UINTEGER', +SQL_TXN_CAPABLE : 'GI_USMALLINT',SQL_TXN_ISOLATION_OPTION : 'GI_UINTEGER', +SQL_UNION : 'GI_UINTEGER',SQL_USER_NAME : 'GI_STRING',SQL_XOPEN_CLI_YEAR : 'GI_STRING', +} + +#Definations for types +BINARY = bytearray +Binary = bytearray +DATETIME = datetime.datetime +Date = datetime.date +Time = datetime.time +Timestamp = datetime.datetime +STRING = str +NUMBER = float +ROWID = int +DateFromTicks = datetime.date.fromtimestamp +TimeFromTicks = lambda x: datetime.datetime.fromtimestamp(x).time() +TimestampFromTicks = datetime.datetime.fromtimestamp + + +#Define exceptions +class OdbcNoLibrary(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) +class OdbcLibraryError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) +class OdbcInvalidHandle(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) +class OdbcGenericError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + + +class Warning(StandardError): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + + +class Error(StandardError): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + +class InterfaceError(Error): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + + +class DatabaseError(Error): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + + +class InternalError(DatabaseError): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + + +class ProgrammingError(DatabaseError): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + +class DataError(DatabaseError): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + +class IntegrityError(DatabaseError): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + +class NotSupportedError(Error): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + +class OperationalError(DatabaseError): + def __init__(self, error_code, error_desc): + self.value = (error_code, error_desc) + self.args = (error_code, error_desc) + + + +# Get the References of the platform's ODBC functions via ctypes +if sys.platform in ('win32','cli'): + ODBC_API = ctypes.windll.odbc32 + # On Windows, the size of SQLWCHAR is hardcoded to 2-bytes. + SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_ushort) +else: + # Set load the library on linux + try: + # First try direct loading libodbc.so + ODBC_API = ctypes.cdll.LoadLibrary('libodbc.so') + except: + # If direct loading libodbc.so failed + # We try finding the libodbc.so by using find_library + from ctypes.util import find_library + library = find_library('odbc') + if library is None: + # If find_library still can not find the library + # we try finding it manually from where libodbc.so usually appears + lib_paths = ("/usr/lib/libodbc.so","/usr/lib/i386-linux-gnu/libodbc.so","/usr/lib/x86_64-linux-gnu/libodbc.so") + lib_paths = [path for path in lib_paths if os.path.exists(path)] + if len(lib_paths) == 0 : + raise OdbcNoLibrary, 'ODBC Library is not found' + else: + library = lib_paths[0] + + # Then we try loading the found libodbc.so again + try: + ODBC_API = ctypes.cdll.LoadLibrary(library) + except: + # If still fail loading, abort. + raise OdbcLibraryError, 'Error while loading %s' % library + + # unixODBC defaults to 2-bytes SQLWCHAR, unless "-DSQL_WCHART_CONVERT" was + # added to CFLAGS, in which case it will be the size of wchar_t. + # Note that using 4-bytes SQLWCHAR will break most ODBC drivers, as driver + # development mostly targets the Windows platform. + import commands + status, output = commands.getstatusoutput('odbc_config --cflags') + if status == 0 and 'SQL_WCHART_CONVERT' in output: + SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_wchar) + else: + SQLWCHAR_SIZE = ctypes.sizeof(ctypes.c_ushort) + + +create_buffer_u = ctypes.create_unicode_buffer +create_buffer = ctypes.create_string_buffer +wchar_type = ctypes.c_wchar_p +to_unicode = lambda s: s +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 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 + wchar_type = ctypes.c_char_p + + def to_unicode(s): + return s.encode('UTF-16LE') + + def from_buffer_u(buffer): + i = 0 + uchars = [] + while True: + uchar = buffer.raw[i:i + 2].decode('UTF-16') + if uchar == u'\x00': + break + uchars.append(uchar) + i += 2 + return ''.join(uchars) + +# Exoteric case, don't really care. +elif UNICODE_SIZE < SQLWCHAR_SIZE: + raise OdbcLibraryError('Using narrow Python build with ODBC library ' + 'expecting wide unicode is not supported.') + + +# Below Datatype mappings referenced the document at +# 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 0.Python Data Type 1.Default Output Converter 2.Buffer Type 3.Buffer Allocator 4.Default Buffer Size +SQL_TYPE_NULL : (None, lambda x: None, SQL_C_CHAR, create_buffer, 2 ), +SQL_CHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ), +SQL_NUMERIC : (Decimal, Decimal, SQL_C_CHAR, create_buffer, 150 ), +SQL_DECIMAL : (Decimal, Decimal, SQL_C_CHAR, create_buffer, 150 ), +SQL_INTEGER : (int, int, SQL_C_CHAR, create_buffer, 150 ), +SQL_SMALLINT : (int, int, SQL_C_CHAR, create_buffer, 150 ), +SQL_FLOAT : (float, float, SQL_C_CHAR, create_buffer, 150 ), +SQL_REAL : (float, float, SQL_C_CHAR, create_buffer, 150 ), +SQL_DOUBLE : (float, float, SQL_C_CHAR, create_buffer, 200 ), +SQL_DATE : (datetime.date, dt_cvt, SQL_C_CHAR , create_buffer, 30 ), +SQL_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), +SQL_SS_TIME2 : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), +SQL_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ), +SQL_VARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 2048 ), +SQL_LONGVARCHAR : (str, lambda x: x, SQL_C_CHAR, create_buffer, 20500 ), +SQL_BINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 5120 ), +SQL_VARBINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 5120 ), +SQL_LONGVARBINARY : (bytearray, bytearray, SQL_C_BINARY, create_buffer, 20500 ), +SQL_BIGINT : (long, long, SQL_C_CHAR, create_buffer, 150 ), +SQL_TINYINT : (int, int, SQL_C_CHAR, create_buffer, 150 ), +SQL_BIT : (bool, lambda x:x=='1', SQL_C_CHAR, create_buffer, 2 ), +SQL_WCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ), +SQL_WVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 2048 ), +SQL_GUID : (str, str, SQL_C_CHAR, create_buffer, 50 ), +SQL_WLONGVARCHAR : (unicode, lambda x: x, SQL_C_WCHAR, create_buffer_u, 20500 ), +SQL_TYPE_DATE : (datetime.date, dt_cvt, SQL_C_CHAR, create_buffer, 30 ), +SQL_TYPE_TIME : (datetime.time, tm_cvt, SQL_C_CHAR, create_buffer, 20 ), +SQL_TYPE_TIMESTAMP : (datetime.datetime, dttm_cvt, SQL_C_CHAR, create_buffer, 30 ), +} + + +""" +Types mapping, applicable for 32-bit and 64-bit Linux / Windows / Mac OS X. + +SQLPointer -> ctypes.c_void_p +SQLCHAR * -> ctypes.c_char_p +SQLWCHAR * -> ctypes.c_wchar_p on Windows, ctypes.c_char_p with unixODBC +SQLINT -> ctypes.c_int +SQLSMALLINT -> ctypes.c_short +SQMUSMALLINT -> ctypes.c_ushort +SQLLEN -> ctypes.c_ssize_t +SQLULEN -> ctypes.c_size_t +SQLRETURN -> ctypes.c_short +""" + +# Define the python return type for ODBC functions with ret result. +funcs_with_ret = [ + "SQLAllocHandle", + "SQLBindParameter", + "SQLCloseCursor", + "SQLColAttribute", + "SQLColumns", + "SQLColumnsW", + "SQLConnect", + "SQLConnectW", + "SQLDataSources", + "SQLDataSourcesW", + "SQLDescribeCol", + "SQLDescribeColW", + "SQLDescribeParam", + "SQLDisconnect", + "SQLDriverConnect", + "SQLDriverConnectW", + "SQLEndTran", + "SQLExecDirect", + "SQLExecDirectW", + "SQLExecute", + "SQLFetch", + "SQLFetchScroll", + "SQLForeignKeys", + "SQLForeignKeysW", + "SQLFreeHandle", + "SQLFreeStmt", + "SQLGetData", + "SQLGetDiagRec", + "SQLGetInfo", + "SQLGetTypeInfo", + "SQLMoreResults", + "SQLNumParams", + "SQLNumResultCols", + "SQLPrepare", + "SQLPrepareW", + "SQLPrimaryKeys", + "SQLPrimaryKeysW", + "SQLProcedureColumns", + "SQLProcedureColumnsW", + "SQLProcedures", + "SQLProceduresW", + "SQLRowCount", + "SQLSetConnectAttr", + "SQLSetEnvAttr", + "SQLStatistics", + "SQLStatisticsW", + "SQLTables", + "SQLTablesW", +] + +for func_name in funcs_with_ret: + getattr(ODBC_API, func_name).restype = ctypes.c_short + +if sys.platform not in ('cli'): + #Seems like the IronPython can not declare ctypes.POINTER type arguments + ODBC_API.SQLAllocHandle.argtypes = [ + ctypes.c_short, + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_void_p), + ] + + ODBC_API.SQLBindParameter.argtypes = [ + ctypes.c_void_p, + ctypes.c_ushort, + ctypes.c_short, + ctypes.c_short, + ctypes.c_short, + ctypes.c_size_t, + ctypes.c_short, + ctypes.c_void_p, + ctypes.c_ssize_t, + ctypes.POINTER(ctypes.c_ssize_t), + ] + + ODBC_API.SQLColAttribute.argtypes = [ + ctypes.c_void_p, + ctypes.c_ushort, + ctypes.c_ushort, + ctypes.c_void_p, + ctypes.c_short, + ctypes.POINTER(ctypes.c_short), + ctypes.POINTER(ctypes.c_ssize_t), + ] + + ODBC_API.SQLDataSources.argtypes = [ + ctypes.c_void_p, + ctypes.c_ushort, + ctypes.c_char_p, + ctypes.c_short, + ctypes.POINTER(ctypes.c_short), + ctypes.c_char_p, + ctypes.c_short, + ctypes.POINTER(ctypes.c_short), + ] + + ODBC_API.SQLDescribeCol.argtypes = [ + ctypes.c_void_p, + ctypes.c_ushort, + ctypes.c_char_p, + ctypes.c_short, + ctypes.POINTER(ctypes.c_short), + ctypes.POINTER(ctypes.c_short), + ctypes.POINTER(ctypes.c_size_t), + ctypes.POINTER(ctypes.c_short), + ctypes.POINTER(ctypes.c_short), + ] + + ODBC_API.SQLDescribeParam.argtypes = [ + ctypes.c_void_p, + ctypes.c_ushort, + ctypes.POINTER(ctypes.c_short), + ctypes.POINTER(ctypes.c_size_t), + ctypes.POINTER(ctypes.c_short), + ctypes.POINTER(ctypes.c_short), + ] + + ODBC_API.SQLDriverConnect.argtypes = [ + ctypes.c_void_p, + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.POINTER(ctypes.c_short), + ctypes.c_ushort, + ] + + ODBC_API.SQLGetData.argtypes = [ + ctypes.c_void_p, + ctypes.c_ushort, + ctypes.c_short, + ctypes.c_void_p, + ctypes.c_ssize_t, + ctypes.POINTER(ctypes.c_ssize_t), + ] + + ODBC_API.SQLGetDiagRec.argtypes = [ + ctypes.c_short, + ctypes.c_void_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.POINTER(ctypes.c_int), + ctypes.c_char_p, + ctypes.c_short, + ctypes.POINTER(ctypes.c_short), + ] + + ODBC_API.SQLGetInfo.argtypes = [ + ctypes.c_void_p, + ctypes.c_ushort, + ctypes.c_void_p, + ctypes.c_short, + ctypes.POINTER(ctypes.c_short), + ] + + ODBC_API.SQLRowCount.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_ssize_t), + ] + + ODBC_API.SQLNumParams.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_short), + ] + + ODBC_API.SQLNumResultCols.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_short), + ] + + +ODBC_API.SQLCloseCursor.argtypes = [ctypes.c_void_p] + +ODBC_API.SQLColumns.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, +] + +ODBC_API.SQLConnect.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, +] + + + +ODBC_API.SQLDisconnect.argtypes = [ctypes.c_void_p] + + +ODBC_API.SQLEndTran.argtypes = [ + ctypes.c_short, + ctypes.c_void_p, + ctypes.c_short, +] + +ODBC_API.SQLExecute.argtypes = [ctypes.c_void_p] + +ODBC_API.SQLExecDirect.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_int, +] + +ODBC_API.SQLFetch.argtypes = [ctypes.c_void_p] + +ODBC_API.SQLFetchScroll.argtypes = [ + ctypes.c_void_p, + ctypes.c_short, + ctypes.c_ssize_t, +] + +ODBC_API.SQLForeignKeys.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, +] + +ODBC_API.SQLFreeHandle.argtypes = [ + ctypes.c_short, + ctypes.c_void_p, +] + +ODBC_API.SQLFreeStmt.argtypes = [ + ctypes.c_void_p, + ctypes.c_ushort, +] + + +ODBC_API.SQLGetTypeInfo.argtypes = [ + ctypes.c_void_p, + ctypes.c_short, +] + +ODBC_API.SQLMoreResults.argtypes = [ctypes.c_void_p] + + +ODBC_API.SQLPrepare.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_int, +] + +ODBC_API.SQLPrimaryKeys.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, +] + +ODBC_API.SQLProcedureColumns.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, +] + +ODBC_API.SQLProcedures.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, +] + + +ODBC_API.SQLSetConnectAttr.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, + ctypes.c_void_p, + ctypes.c_int, +] + +ODBC_API.SQLSetEnvAttr.argtypes = [ + ctypes.c_void_p, + ctypes.c_int, + ctypes.c_void_p, + ctypes.c_int, +] + +ODBC_API.SQLStatistics.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_ushort, + ctypes.c_ushort, +] + +ODBC_API.SQLTables.argtypes = [ + ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, + ctypes.c_char_p, + ctypes.c_short, +] + +def to_wchar(argtypes): + if argtypes: # Under IronPython some argtypes are not declared + result = [] + for x in argtypes: + if x == ctypes.c_char_p: + result.append(wchar_type) + else: + result.append(x) + return result + else: + return argtypes + +ODBC_API.SQLColumnsW.argtypes = to_wchar(ODBC_API.SQLColumns.argtypes) +ODBC_API.SQLConnectW.argtypes = to_wchar(ODBC_API.SQLConnect.argtypes) +ODBC_API.SQLDataSourcesW.argtypes = to_wchar(ODBC_API.SQLDataSources.argtypes) +ODBC_API.SQLDescribeColW.argtypes = to_wchar(ODBC_API.SQLDescribeCol.argtypes) +ODBC_API.SQLDriverConnectW.argtypes = to_wchar(ODBC_API.SQLDriverConnect.argtypes) +ODBC_API.SQLExecDirectW.argtypes = to_wchar(ODBC_API.SQLExecDirect.argtypes) +ODBC_API.SQLForeignKeysW.argtypes = to_wchar(ODBC_API.SQLForeignKeys.argtypes) +ODBC_API.SQLPrepareW.argtypes = to_wchar(ODBC_API.SQLPrepare.argtypes) +ODBC_API.SQLPrimaryKeysW.argtypes = to_wchar(ODBC_API.SQLPrimaryKeys.argtypes) +ODBC_API.SQLProcedureColumnsW.argtypes = to_wchar(ODBC_API.SQLProcedureColumns.argtypes) +ODBC_API.SQLProceduresW.argtypes = to_wchar(ODBC_API.SQLProcedures.argtypes) +ODBC_API.SQLStatisticsW.argtypes = to_wchar(ODBC_API.SQLStatistics.argtypes) +ODBC_API.SQLTablesW.argtypes = to_wchar(ODBC_API.SQLTables.argtypes) + + +# Set the alias for the ctypes functions for beter code readbility or performance. +ADDR = ctypes.byref +SQLFetch = ODBC_API.SQLFetch +SQLExecute = ODBC_API.SQLExecute +SQLBindParameter = ODBC_API.SQLBindParameter + + + + + +def ctrl_err(ht, h, val_ret): + """Classify type of ODBC error from (type of handle, handle, return value) + , and raise with a list""" + state = create_buffer(5) + NativeError = ctypes.c_int() + Message = create_buffer(1024*10) + Buffer_len = ctypes.c_short() + err_list = [] + number_errors = 1 + + while 1: + ret = ODBC_API.SQLGetDiagRec(ht, h, number_errors, state, \ + NativeError, Message, len(Message), ADDR(Buffer_len)) + if ret == SQL_NO_DATA_FOUND: + #No more data, I can raise + if DEBUG: print err_list[0][1] + state = err_list[0][0] + err_text = '['+state+'] '+err_list[0][1] + if state[:2] in ('24','25','42'): + raise ProgrammingError(state,err_text) + elif state[:2] in ('22'): + raise DataError(state,err_text) + elif state[:2] in ('23') or state == '40002': + raise IntegrityError(state,err_text) + elif state == '0A000': + raise NotSupportedError(state,err_text) + elif state in ('HYT00','HYT01'): + raise OperationalError(state,err_text) + elif state[:2] in ('IM','HY'): + raise Error(state,err_text) + else: + raise DatabaseError(state,err_text) + break + elif ret == SQL_INVALID_HANDLE: + #The handle passed is an invalid handle + raise ProgrammingError('', 'SQL_INVALID_HANDLE') + elif ret == SQL_SUCCESS: + err_list.append((state.value, Message.value, NativeError.value)) + number_errors += 1 + + +def validate(ret, handle_type, handle): + """ Validate return value, if not success, raise exceptions based on the handle """ + if ret not in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO, SQL_NO_DATA): + ctrl_err(handle_type, handle, ret) + + +def AllocateEnv(): + if pooling: + ret = ODBC_API.SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, SQL_CP_ONE_PER_HENV, SQL_IS_UINTEGER) + validate(ret, SQL_HANDLE_ENV, SQL_NULL_HANDLE) + + ''' + Allocate an ODBC environment by initializing the handle shared_env_h + ODBC enviroment needed to be created, so connections can be created under it + connections pooling can be shared under one environment + ''' + global shared_env_h + shared_env_h = ctypes.c_void_p() + ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, ADDR(shared_env_h)) + validate(ret, SQL_HANDLE_ENV, shared_env_h) + + # Set the ODBC environment's compatibil leve to ODBC 3.0 + ret = ODBC_API.SQLSetEnvAttr(shared_env_h, SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3, 0) + validate(ret, SQL_HANDLE_ENV, shared_env_h) + + +""" +Here, we have a few callables that determine how a result row is returned. + +A new one can be added by creating a callable that: +- accepts a cursor as its parameter. +- returns a callable that accepts an iterable containing the row values. +""" + +def TupleRow(cursor): + """Normal tuple with added attribute `cursor_description`, as in pyodbc. + + This is the default. + """ + class Row(tuple): + cursor_description = cursor.description + + return Row + + +def NamedTupleRow(cursor): + """Named tuple to allow attribute lookup by name. + + Requires py2.6 or above. + """ + from collections import namedtuple + + attr_names = [x[0] for x in cursor._ColBufferList] + + class Row(namedtuple('Row', attr_names, rename=True)): + cursor_description = cursor.description + + def __new__(cls, iterable): + return super(Row, cls).__new__(cls, *iterable) + + return Row + + +def MutableNamedTupleRow(cursor): + """Mutable named tuple to allow attribute to be replaced. This should be + compatible with pyodbc's Row type. + + Requires 3rd-party library "recordtype". + """ + from recordtype import recordtype + + attr_names = [x[0] for x in cursor._ColBufferList] + + class Row(recordtype('Row', attr_names, rename=True)): + cursor_description = cursor.description + + def __init__(self, iterable): + super(Row, self).__init__(*iterable) + + def __iter__(self): + for field_name in self.__slots__: + yield getattr(self, field_name) + + def __getitem__(self, index): + if isinstance(index, slice): + return tuple(getattr(self, x) for x in self.__slots__[index]) + return getattr(self, self.__slots__[index]) + + def __setitem__(self, index, value): + setattr(self, self.__slots__[index], value) + + return Row + + +# The get_type function is used to determine if parameters need to be re-binded +# against the changed parameter types +def get_type(v): + t = type(v) + if isinstance(v, str): + if len(v) >= 255: + t = 's' + elif isinstance(v, unicode): + if len(v) >= 255: + t = 'u' + elif isinstance(v, Decimal): + sv = str(v).replace('-','').strip('0').split('.') + if len(sv)>1: + t = (len(sv[0])+len(sv[1]),len(sv[1])) + else: + t = (len(sv[0]),0) + return t + + + +# The Cursor Class. +class Cursor: + def __init__(self, conx, row_type_callable=None): + """ Initialize self._stmt_h, which is the handle of a statement + A statement is actually the basis of a python"cursor" object + """ + self._stmt_h = ctypes.c_void_p() + self.connection = conx + self.row_type_callable = row_type_callable or TupleRow + self.statement = None + self._last_param_types = None + self._ParamBufferList = [] + self._ColBufferList = [] + self._row_type = None + self._buf_cvt_func = [] + self.rowcount = -1 + self.description = None + self.autocommit = None + self._ColTypeCodeList = [] + self._outputsize = {} + self._inputsizers = [] + self.arraysize = 1 + ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_STMT, self.connection.dbc_h, ADDR(self._stmt_h)) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + self.closed = False + + + def execute(self, query_string, params=None, many_mode=False, call_mode=False): + """ Execute the query string, with optional 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 + """ + + self._free_results('FREE_STATEMENT') + + if params: + # If parameters exist, first prepare the query then executed with parameters + if not type(params) in (tuple, list, set): + raise TypeError("Params must be in a list, tuple, or set") + + if not many_mode: + if query_string != self.statement: + # if the query is not same as last query, then it is not prepared + self.prepare(query_string) + + + param_types = map(get_type, params) + + if call_mode: + self._BindParams(param_types, self._pram_io_list) + else: + if param_types != self._last_param_types: + self._BindParams(param_types) + + + # With query prepared, now put parameters into buffers + col_num = 0 + for param_buffer, param_buffer_len, sql_type in self._ParamBufferList: + c_char_buf, c_buf_len = '', 0 + param_val = params[col_num] + if param_val is None: + c_buf_len = SQL_NULL_DATA + + elif isinstance(param_val, datetime.datetime): + max_len = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0] + datetime_str = param_val.strftime('%Y-%m-%d %H:%M:%S.%f') + c_char_buf = datetime_str[:max_len] + c_buf_len = len(c_char_buf) + # print c_buf_len, c_char_buf + + elif isinstance(param_val, datetime.date): + if self.connection.type_size_dic.has_key(SQL_TYPE_DATE): + max_len = self.connection.type_size_dic[SQL_TYPE_DATE][0] + else: + max_len = 10 + c_char_buf = param_val.isoformat()[:max_len] + c_buf_len = len(c_char_buf) + #print c_char_buf + + elif isinstance(param_val, datetime.time): + if self.connection.type_size_dic.has_key(SQL_TYPE_TIME): + max_len = self.connection.type_size_dic[SQL_TYPE_TIME][0] + c_char_buf = param_val.isoformat()[:max_len] + c_buf_len = len(c_char_buf) + elif self.connection.type_size_dic.has_key(SQL_SS_TIME2): + max_len = self.connection.type_size_dic[SQL_SS_TIME2][0] + c_char_buf = param_val.isoformat()[:max_len] + c_buf_len = len(c_char_buf) + else: + c_buf_len = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0] + time_str = param_val.isoformat() + if len(time_str) == 8: + time_str += '.000' + c_char_buf = '1900-01-01 '+time_str[0:c_buf_len - 11] + #print c_buf_len, c_char_buf + + elif isinstance(param_val, bool): + if param_val == True: + c_char_buf = '1' + else: + c_char_buf = '0' + c_buf_len = 1 + + elif isinstance(param_val, (int, long, float, Decimal)): + c_char_buf = str(param_val) + c_buf_len = len(c_char_buf) + + elif isinstance(param_val, str): + c_char_buf = param_val + c_buf_len = len(c_char_buf) + elif isinstance(param_val, unicode): + c_char_buf = to_unicode(param_val) + c_buf_len = len(c_char_buf) + elif isinstance(param_val, (bytearray, buffer)): + c_char_buf = str(param_val) + c_buf_len = len(c_char_buf) + + else: + c_char_buf = param_val + + + if isinstance(param_val, (bytearray, buffer)): + param_buffer.raw = c_char_buf + + else: + param_buffer.value = c_char_buf + #print param_buffer, param_buffer.value + + if isinstance(param_val, (unicode, str)): + #ODBC driver will find NUL in unicode and string to determine their length + param_buffer_len.value = SQL_NTS + else: + param_buffer_len.value = c_buf_len + + col_num += 1 + ret = SQLExecute(self._stmt_h) + if ret != SQL_SUCCESS: + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + + if not many_mode: + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + + else: + self.execdirect(query_string) + return (self) + + + def _SQLExecute(self): + ret = SQLExecute(self._stmt_h) + if ret != SQL_SUCCESS: + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + + + + def prepare(self, query_string): + """prepare a query""" + if type(query_string) == unicode: + c_query_string = wchar_type(to_unicode(query_string)) + ret = ODBC_API.SQLPrepareW(self._stmt_h, c_query_string, len(query_string)) + else: + c_query_string = ctypes.c_char_p(query_string) + ret = ODBC_API.SQLPrepare(self._stmt_h, c_query_string, len(query_string)) + if ret != SQL_SUCCESS: + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + self.statement = query_string + + + def execdirect(self, query_string): + """Execute a query directly""" + if type(query_string) == unicode: + c_query_string = wchar_type(to_unicode(query_string)) + ret = ODBC_API.SQLExecDirectW(self._stmt_h, c_query_string, len(query_string)) + else: + c_query_string = ctypes.c_char_p(query_string) + ret = ODBC_API.SQLExecDirect(self._stmt_h, c_query_string, len(query_string)) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + self.statement = None + return (self) + + + def callproc(self, procname, args): + 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)] + + print 'pram_io_list: '+str(self._pram_io_list) + + + + call_escape = '{CALL '+procname + if args: + call_escape += '(' + ','.join(['?' for params in args]) + ')' + call_escape += '}' + + self.execute(call_escape, args, call_mode = True) + + result = [] + + for buf, buf_len, sql_type in self._ParamBufferList: + if buf_len.value == -1: + result.append(None) + else: + result.append(self.connection.output_converter[sql_type](buf.value)) + return (result) + + + + def executemany(self, query_string, params_list = [None]): + self.prepare(query_string) + for params in params_list: + self.execute(query_string, params, many_mode = True) + self._NumOfRows() + self.rowcount = -1 + self._UpdateDesc() + #self._BindCols() + + + def _BindParams(self, param_types, pram_io_list = []): + """Create parameter buffers based on param types, and bind them to the statement""" + # Get the number of query parameters judged by database. + NumParams = ctypes.c_short() + ret = ODBC_API.SQLNumParams(self._stmt_h, ADDR(NumParams)) + if ret != SQL_SUCCESS: + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + if len(param_types) != NumParams.value: + # In case number of parameters provided do not same as number required + error_desc = "The SQL contains %d parameter markers, but %d parameters were supplied" \ + %(NumParams.value,len(param_types)) + raise ProgrammingError('HY000',error_desc) + + + # Every parameter needs to be binded to a buffer + ParamBufferList = [] + # Temporary holder since we can only call SQLDescribeParam before + # calling SQLBindParam. + temp_holder = [] + for col_num in range(NumParams.value): + col_size = 0 + buf_size = 512 + + if param_types[col_num] == type(None): + ParameterNumber = ctypes.c_ushort(col_num + 1) + DataType = ctypes.c_short() + ParameterSize = ctypes.c_size_t() + DecimalDigits = ctypes.c_short() + Nullable = ctypes.c_short() + ret = ODBC_API.SQLDescribeParam( + self._stmt_h, + ParameterNumber, + ADDR(DataType), + ADDR(ParameterSize), + ADDR(DecimalDigits), + ADDR(Nullable), + ) + if ret != SQL_SUCCESS: + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + sql_c_type = SQL_C_DEFAULT + sql_type = DataType.value + buf_size = 1 + ParameterBuffer = create_buffer(buf_size) + + elif param_types[col_num] == 'u': + sql_c_type = SQL_C_WCHAR + sql_type = SQL_WLONGVARCHAR + buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500 + ParameterBuffer = create_buffer_u(buf_size) + + elif param_types[col_num] == 's': + sql_c_type = SQL_C_CHAR + sql_type = SQL_LONGVARCHAR + buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500 + ParameterBuffer = create_buffer(buf_size) + + elif type(param_types[col_num]) == tuple: #Decimal + sql_c_type = SQL_C_CHAR + sql_type = SQL_NUMERIC + buf_size = param_types[col_num][0] + + ParameterBuffer = create_buffer(buf_size+4) + col_size = param_types[col_num][1] + if DEBUG: print param_types[col_num][0],param_types[col_num][1] + + # bool subclasses int, thus has to go first + elif issubclass(param_types[col_num], bool): + sql_c_type = SQL_C_CHAR + sql_type = SQL_BIT + buf_size = SQL_data_type_dict[sql_type][4] + ParameterBuffer = create_buffer(buf_size) + + elif issubclass(param_types[col_num], int): + sql_c_type = SQL_C_CHAR + sql_type = SQL_INTEGER + buf_size = SQL_data_type_dict[sql_type][4] + ParameterBuffer = create_buffer(buf_size) + + elif issubclass(param_types[col_num], long): + sql_c_type = SQL_C_CHAR + sql_type = SQL_BIGINT + buf_size = SQL_data_type_dict[sql_type][4] + ParameterBuffer = create_buffer(buf_size) + + + elif issubclass(param_types[col_num], float): + sql_c_type = SQL_C_CHAR + sql_type = SQL_DOUBLE + buf_size = SQL_data_type_dict[sql_type][4] + ParameterBuffer = create_buffer(buf_size) + + + # datetime subclasses date, thus has to go first + elif issubclass(param_types[col_num], datetime.datetime): + sql_c_type = SQL_C_CHAR + sql_type = SQL_TYPE_TIMESTAMP + buf_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0] + ParameterBuffer = create_buffer(buf_size) + col_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][1] + + + elif issubclass(param_types[col_num], datetime.date): + sql_c_type = SQL_C_CHAR + if self.connection.type_size_dic.has_key(SQL_TYPE_DATE): + if DEBUG: print 'conx.type_size_dic.has_key(SQL_TYPE_DATE)' + sql_type = SQL_TYPE_DATE + buf_size = self.connection.type_size_dic[SQL_TYPE_DATE][0] + + ParameterBuffer = create_buffer(buf_size) + col_size = self.connection.type_size_dic[SQL_TYPE_DATE][1] + + else: + # SQL Sever <2008 doesn't have a DATE type. + sql_type = SQL_TYPE_TIMESTAMP + buf_size = 10 + ParameterBuffer = create_buffer(buf_size) + + + elif issubclass(param_types[col_num], datetime.time): + sql_c_type = SQL_C_CHAR + if self.connection.type_size_dic.has_key(SQL_TYPE_TIME): + sql_type = SQL_TYPE_TIME + buf_size = self.connection.type_size_dic[SQL_TYPE_TIME][0] + ParameterBuffer = create_buffer(buf_size) + col_size = self.connection.type_size_dic[SQL_TYPE_TIME][1] + elif self.connection.type_size_dic.has_key(SQL_SS_TIME2): + # TIME type added in SQL Server 2008 + sql_type = SQL_SS_TIME2 + buf_size = self.connection.type_size_dic[SQL_SS_TIME2][0] + ParameterBuffer = create_buffer(buf_size) + col_size = self.connection.type_size_dic[SQL_SS_TIME2][1] + else: + # SQL Sever <2008 doesn't have a TIME type. + sql_type = SQL_TYPE_TIMESTAMP + buf_size = self.connection.type_size_dic[SQL_TYPE_TIMESTAMP][0] + ParameterBuffer = create_buffer(buf_size) + col_size = 3 + + elif issubclass(param_types[col_num], unicode): + sql_c_type = SQL_C_WCHAR + sql_type = SQL_WVARCHAR + buf_size = 255 + ParameterBuffer = create_buffer_u(buf_size) + + elif issubclass(param_types[col_num], str): + sql_c_type = SQL_C_CHAR + sql_type = SQL_VARCHAR + buf_size = 255 + ParameterBuffer = create_buffer(buf_size) + + elif issubclass(param_types[col_num], (bytearray, buffer)): + sql_c_type = SQL_C_BINARY + sql_type = SQL_LONGVARBINARY + buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500 + ParameterBuffer = create_buffer(buf_size) + + + else: + sql_c_type = SQL_C_CHAR + sql_type = SQL_LONGVARCHAR + buf_size = len(self._inputsizers)>col_num and self._inputsizers[col_num] or 20500 + ParameterBuffer = create_buffer(buf_size) + + temp_holder.append((sql_c_type, sql_type, buf_size, col_size, ParameterBuffer)) + + for col_num, (sql_c_type, sql_type, buf_size, col_size, ParameterBuffer) in enumerate(temp_holder): + BufferLen = ctypes.c_ssize_t(buf_size) + LenOrIndBuf = ctypes.c_ssize_t() + + + InputOutputType = SQL_PARAM_INPUT + if len(pram_io_list) > col_num: + InputOutputType = pram_io_list[col_num] + + ret = SQLBindParameter(self._stmt_h, col_num + 1, InputOutputType, sql_c_type, sql_type, buf_size,\ + col_size, ADDR(ParameterBuffer), BufferLen,ADDR(LenOrIndBuf)) + if ret != SQL_SUCCESS: + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + # Append the value buffer and the lenth buffer to the array + ParamBufferList.append((ParameterBuffer,LenOrIndBuf,sql_type)) + + self._last_param_types = param_types + self._ParamBufferList = ParamBufferList + + + + def _CreateColBuf(self): + NOC = self._NumOfCols() + self._ColBufferList = [] + self._row_type = None + for col_num in range(NOC): + col_name = self.description[col_num][0] + + col_sql_data_type = self._ColTypeCodeList[col_num] + + # set default size base on the column's sql data type + 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: + total_buf_len = self._outputsize.get(None,total_buf_len) + # over-write if there's preset size value for the "col_num" column + total_buf_len = self._outputsize.get(col_num, total_buf_len) + + + alloc_buffer = SQL_data_type_dict[col_sql_data_type][3](total_buf_len) + + used_buf_len = ctypes.c_ssize_t() + + target_type = SQL_data_type_dict[col_sql_data_type][2] + force_unicode = self.connection.unicode_results + + if force_unicode and col_sql_data_type in (SQL_CHAR,SQL_VARCHAR,SQL_LONGVARCHAR): + target_type = SQL_C_WCHAR + alloc_buffer = create_buffer_u(total_buf_len) + + buf_cvt_func = self.connection.output_converter[self._ColTypeCodeList[col_num]] + + self._ColBufferList.append([col_name, target_type, used_buf_len, alloc_buffer, total_buf_len, buf_cvt_func]) + + + def _GetData(self): + '''Bind buffers for the record set columns''' + + # Lazily create the row type on first fetch. + if self._row_type is None: + self._row_type = self.row_type_callable(self) + + value_list = [] + col_num = 0 + for col_name, target_type, used_buf_len, alloc_buffer, total_buf_len, buf_cvt_func in self._ColBufferList: + + blocks = [] + while True: + ret = ODBC_API.SQLGetData(self._stmt_h, col_num + 1, target_type, ADDR(alloc_buffer), total_buf_len,\ + ADDR(used_buf_len)) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + if ret == SQL_SUCCESS: + if used_buf_len.value == SQL_NULL_DATA: + blocks.append(None) + else: + if target_type == SQL_C_BINARY: + blocks.append(alloc_buffer.raw[:used_buf_len.value]) + elif target_type == SQL_C_WCHAR: + blocks.append(from_buffer_u(alloc_buffer)) + else: + #print col_name, target_type, alloc_buffer.value + blocks.append(alloc_buffer.value) + + break + + if ret == SQL_SUCCESS_WITH_INFO: + if target_type == SQL_C_BINARY: + blocks.append(alloc_buffer.raw) + else: + blocks.append(alloc_buffer.value) + + if ret == SQL_NO_DATA: + break + + + if len(blocks) == 1: + raw_value = blocks[0] + else: + raw_value = ''.join(blocks) + + if raw_value == None: + value_list.append(None) + else: + value_list.append(buf_cvt_func(raw_value)) + col_num += 1 + + return self._row_type(value_list) + + + def _UpdateDesc(self): + "Get the information of (name, type_code, display_size, internal_size, col_precision, scale, null_ok)" + Cname = create_buffer(1024) + Cname_ptr = ctypes.c_short() + Ctype_code = ctypes.c_short() + Csize = ctypes.c_size_t() + Cdisp_size = ctypes.c_ssize_t(0) + CDecimalDigits = ctypes.c_short() + Cnull_ok = ctypes.c_short() + ColDescr = [] + self._ColTypeCodeList = [] + NOC = self._NumOfCols() + for col in range(1, NOC+1): + ret = ODBC_API.SQLColAttribute(self._stmt_h, col, SQL_DESC_DISPLAY_SIZE, ADDR(create_buffer(10)), + 10, ADDR(ctypes.c_short()),ADDR(Cdisp_size)) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + ret = ODBC_API.SQLDescribeCol(self._stmt_h, col, Cname, len(Cname), ADDR(Cname_ptr),\ + ADDR(Ctype_code),ADDR(Csize),ADDR(CDecimalDigits), ADDR(Cnull_ok)) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + col_name = Cname.value + if lowercase: + col_name = str.lower(col_name) + #(name, type_code, display_size, + # internal_size, col_precision, scale, null_ok) + ColDescr.append((col_name, SQL_data_type_dict.get(Ctype_code.value,(Ctype_code.value))[0],Cdisp_size.value,\ + Csize.value, Csize.value,CDecimalDigits.value,Cnull_ok.value == 1 and True or False)) + self._ColTypeCodeList.append(Ctype_code.value) + + if len(ColDescr) > 0: + self.description = ColDescr + else: + self.description = None + self._CreateColBuf() + + + def _NumOfRows(self): + """Get the number of rows""" + NOR = ctypes.c_ssize_t() + ret = ODBC_API.SQLRowCount(self._stmt_h, ADDR(NOR)) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + self.rowcount = NOR.value + return self.rowcount + + + def _NumOfCols(self): + """Get the number of cols""" + NOC = ctypes.c_short() + ret = ODBC_API.SQLNumResultCols(self._stmt_h, ADDR(NOC)) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + return NOC.value + + + def fetchall(self): + rows = [] + while True: + row = self.fetchone() + if row == None: + break + rows.append(row) + return rows + + + def fetchmany(self, num = None): + if num == None: + num = self.arraysize + rows, row_num = [], 0 + + while row_num < num: + row = self.fetchone() + if row == None: + break + rows.append(row) + row_num += 1 + return rows + + + def fetchone(self): + ret = SQLFetch(self._stmt_h) + if ret == SQL_SUCCESS: + return self._GetData() + else: + if ret == SQL_NO_DATA_FOUND: + return None + else: + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + def next(self): + row = self.fetchone() + if row == None: + raise(StopIteration) + return row + + def __iter__(self): + return self + + + def skip(self, count = 0): + for i in xrange(count): + ret = ODBC_API.SQLFetchScroll(self._stmt_h, SQL_FETCH_NEXT, 0) + if ret != SQL_SUCCESS: + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + return None + + + + def nextset(self): + ret = ODBC_API.SQLMoreResults(self._stmt_h) + if ret not in (SQL_SUCCESS, SQL_NO_DATA): + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + if ret == SQL_NO_DATA: + self._free_results('FREE_STATEMENT') + return False + else: + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + return True + + + def _free_results(self, free_statement): + if not self.connection.connected: + raise ProgrammingError('HY000','Attempt to use a closed connection.') + + self.description = None + if free_statement == 'FREE_STATEMENT': + ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_CLOSE) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + else: + ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_UNBIND) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_RESET_PARAMS) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self.rowcount = -1 + + + + def getTypeInfo(self, sqlType = None): + if sqlType == None: + type = SQL_ALL_TYPES + else: + type = sqlType + ret = ODBC_API.SQLGetTypeInfo(self._stmt_h, type) + if ret in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO): + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + return self.fetchone() + + + def tables(self, table=None, catalog=None, schema=None, tableType=None): + """Return a list with all tables""" + l_catalog = l_schema = l_table = l_tableType = 0 + + if catalog != None: + l_catalog = len(catalog) + catalog = ctypes.c_char_p(catalog) + + if schema != None: + l_schema = len(schema) + schema = ctypes.c_char_p(schema) + + if table != None: + l_table = len(table) + table = ctypes.c_char_p(table) + + if tableType != None: + l_tableType = len(tableType) + tableType = ctypes.c_char_p(tableType) + + self._free_results('FREE_STATEMENT') + self.statement = None + ret = ODBC_API.SQLTables(self._stmt_h, + catalog, l_catalog, + schema, l_schema, + table, l_table, + tableType, l_tableType) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + return (self) + + + def columns(self, table=None, catalog=None, schema=None, column=None): + """Return a list with all columns""" + l_catalog = l_schema = l_table = l_column = 0 + if catalog != None: + l_catalog = len(catalog) + catalog = ctypes.c_char_p(catalog) + if schema != None: + l_schema = len(schema) + schema = ctypes.c_char_p(schema) + if table != None: + l_table = len(table) + table = ctypes.c_char_p(table) + if column != None: + l_column = len(column) + column = ctypes.c_char_p(column) + + self._free_results('FREE_STATEMENT') + self.statement = None + + ret = ODBC_API.SQLColumns(self._stmt_h, + catalog, l_catalog, + schema, l_schema, + table, l_table, + column, l_column) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + return (self) + + + def primaryKeys(self, table=None, catalog=None, schema=None): + l_catalog = l_schema = l_table = 0 + if catalog != None: + l_catalog = len(catalog) + catalog = ctypes.c_char_p(catalog) + + if schema != None: + l_schema = len(schema) + schema = ctypes.c_char_p(schema) + + if table != None: + l_table = len(table) + table = ctypes.c_char_p(table) + + self._free_results('FREE_STATEMENT') + self.statement = None + + ret = ODBC_API.SQLPrimaryKeys(self._stmt_h, + catalog, l_catalog, + schema, l_schema, + table, l_table) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + return (self) + + + def foreignKeys(self, table=None, catalog=None, schema=None, foreignTable=None, foreignCatalog=None, foreignSchema=None): + l_catalog = l_schema = l_table = l_foreignTable = l_foreignCatalog = l_foreignSchema = 0 + if catalog != None: + l_catalog = len(catalog) + catalog = ctypes.c_char_p(catalog) + if schema != None: + l_schema = len(schema) + schema = ctypes.c_char_p(schema) + if table != None: + l_table = len(table) + table = ctypes.c_char_p(table) + if foreignTable != None: + l_foreignTable = len(foreignTable) + foreignTable = ctypes.c_char_p(foreignTable) + if foreignCatalog != None: + l_foreignCatalog = len(foreignCatalog) + foreignCatalog = ctypes.c_char_p(foreignCatalog) + if foreignSchema != None: + l_foreignSchema = len(foreignSchema) + foreignSchema = ctypes.c_char_p(foreignSchema) + + self._free_results('FREE_STATEMENT') + self.statement = None + + ret = ODBC_API.SQLForeignKeys(self._stmt_h, + catalog, l_catalog, + schema, l_schema, + table, l_table, + foreignCatalog, l_foreignCatalog, + foreignSchema, l_foreignSchema, + foreignTable, l_foreignTable) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + return (self) + + + def procedurecolumns(self, procedure=None, catalog=None, schema=None, column=None): + l_catalog = l_schema = l_procedure = l_column = 0 + if catalog != None: + l_catalog = len(catalog) + catalog = ctypes.c_char_p(catalog) + if schema != None: + l_schema = len(schema) + schema = ctypes.c_char_p(schema) + if procedure != None: + l_procedure = len(procedure) + procedure = ctypes.c_char_p(procedure) + if column != None: + l_column = len(column) + column = ctypes.c_char_p(column) + + + self._free_results('FREE_STATEMENT') + self.statement = None + + ret = ODBC_API.SQLProcedureColumns(self._stmt_h, + catalog, l_catalog, + schema, l_schema, + procedure, l_procedure, + column, l_column) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self._NumOfRows() + self._UpdateDesc() + return (self) + + + def procedures(self, procedure=None, catalog=None, schema=None): + l_catalog = l_schema = l_procedure = 0 + if catalog != None: + l_catalog = len(catalog) + catalog = ctypes.c_char_p(catalog) + if schema != None: + l_schema = len(schema) + schema = ctypes.c_char_p(schema) + if procedure != None: + l_procedure = len(procedure) + procedure = ctypes.c_char_p(procedure) + + + self._free_results('FREE_STATEMENT') + self.statement = None + + ret = ODBC_API.SQLProcedures(self._stmt_h, + catalog, l_catalog, + schema, l_schema, + procedure, l_procedure) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self._NumOfRows() + self._UpdateDesc() + return (self) + + + def statistics(self, table, catalog=None, schema=None, unique=False, quick=True): + l_table = l_catalog = l_schema = 0 + + if catalog != None: + l_catalog = len(catalog) + catalog = ctypes.c_char_p(catalog) + if schema != None: + l_schema = len(schema) + schema = ctypes.c_char_p(schema) + if table != None: + l_table = len(table) + table = ctypes.c_char_p(table) + + if unique: + Unique = SQL_INDEX_UNIQUE + else: + Unique = SQL_INDEX_ALL + if quick: + Reserved = SQL_QUICK + else: + Reserved = SQL_ENSURE + + self._free_results('FREE_STATEMENT') + self.statement = None + + ret = ODBC_API.SQLStatistics(self._stmt_h, + catalog, l_catalog, + schema, l_schema, + table, l_table, + Unique, Reserved) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self._NumOfRows() + self._UpdateDesc() + #self._BindCols() + return (self) + + + def commit(self): + self.connection.commit() + + def rollback(self): + self.connection.rollback() + + def setoutputsize(self, size, column = None): + self._outputsize[column] = size + + def setinputsizes(self, sizes): + self._inputsizers = [size for size in sizes] + + + def close(self): + """ Call SQLCloseCursor API to free the statement handle""" +# ret = ODBC_API.SQLCloseCursor(self._stmt_h) +# validate(ret, SQL_HANDLE_STMT, self._stmt_h) +# + ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_CLOSE) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_UNBIND) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + ret = ODBC_API.SQLFreeStmt(self._stmt_h, SQL_RESET_PARAMS) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_STMT, self._stmt_h) + validate(ret, SQL_HANDLE_STMT, self._stmt_h) + + self.closed = True + + + + def __del__(self): + if not self.closed: + if DEBUG: print 'auto closing cursor: ', + try: + self.close() + except: + if DEBUG: print 'failed' + pass + else: + if DEBUG: print 'succeed' + pass + + def __exit__(self, type, value, traceback): + if value: + self.rollback() + else: + self.commit() + + self.close() + + + def __enter__(self): + return self + + +# This class implement a odbc connection. +# +# + +class Connection: + def __init__(self, connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False, **kargs): + """Init variables and connect to the engine""" + self.connected = 0 + self.type_size_dic = {} + self.unicode_results = False + self.dbc_h = ctypes.c_void_p() + self.autocommit = autocommit + self.readonly = False + self.timeout = 0 + + for key, value in kargs.items(): + connectString = connectString + key + '=' + value + ';' + self.connectString = connectString + + + self.clear_output_converters() + + try: + lock.acquire() + if shared_env_h == None: + #Initialize an enviroment if it is not created. + AllocateEnv() + finally: + lock.release() + + # Allocate an DBC handle self.dbc_h under the environment shared_env_h + # This DBC handle is actually the basis of a "connection" + # The handle of self.dbc_h will be used to connect to a certain source + # in the self.connect and self.ConnectByDSN method + + ret = ODBC_API.SQLAllocHandle(SQL_HANDLE_DBC, shared_env_h, ADDR(self.dbc_h)) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + + self.connect(connectString, autocommit, ansi, timeout, unicode_results, readonly) + + + + def connect(self, connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False): + """Connect to odbc, using connect strings and set the connection's attributes like autocommit and timeout + by calling SQLSetConnectAttr + """ + + # Before we establish the connection by the connection string + # Set the connection's attribute of "timeout" (Actully LOGIN_TIMEOUT) + if timeout != 0: + ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_LOGIN_TIMEOUT, timeout, SQL_IS_UINTEGER); + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + + + # Create one connection with a connect string by calling SQLDriverConnect + # and make self.dbc_h the handle of this connection + + + # Convert the connetsytring to encoded string + # so it can be converted to a ctypes c_char array object + + + + if not ansi: + c_connectString = wchar_type(to_unicode(self.connectString)) + odbc_func = ODBC_API.SQLDriverConnectW + else: + c_connectString = ctypes.c_char_p(self.connectString) + odbc_func = ODBC_API.SQLDriverConnect + + # With unixODBC, SQLDriverConnect will intermittently fail with error: + # [01000] [unixODBC][Driver Manager]Can't open lib '/path/to/so' : file not found" + # or: + # [01000] [unixODBC][Driver Manager]Can't open lib '/path/to/so' : (null)" + # when called concurrently by more than one threads. So, we have to + # use a lock to serialize the calls. By the way, the error is much + # less likely to happen if ODBC Tracing is enabled, likely due to the + # implicit serialization caused by writing to trace file. + if ODBC_API._name != 'odbc32': + try: + lock.acquire() + ret = odbc_func(self.dbc_h, 0, c_connectString, len(self.connectString), None, 0, None, SQL_DRIVER_NOPROMPT) + finally: + lock.release() + else: + ret = odbc_func(self.dbc_h, 0, c_connectString, len(self.connectString), None, 0, None, SQL_DRIVER_NOPROMPT) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + + + # Set the connection's attribute of "autocommit" + # + self.autocommit = autocommit + + if self.autocommit == True: + ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_ON, SQL_IS_UINTEGER) + else: + ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER) + + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + + # Set the connection's attribute of "readonly" + # + self.readonly = readonly + + ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_ACCESS_MODE, self.readonly and SQL_MODE_READ_ONLY or SQL_MODE_READ_WRITE, SQL_IS_UINTEGER) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + + self.unicode_results = unicode_results + self.update_type_size_info() + self.connected = 1 + + def clear_output_converters(self): + self.output_converter = {} + for sqltype, profile in SQL_data_type_dict.items(): + self.output_converter[sqltype] = profile[1] + + + def add_output_converter(self, sqltype, func): + self.output_converter[sqltype] = func + + def settimeout(self, timeout): + ret = ODBC_API.SQLSetConnectAttr(self.dbc_h, SQL_ATTR_CONNECTION_TIMEOUT, timeout, SQL_IS_UINTEGER); + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + self.timeout = timeout + + + def ConnectByDSN(self, dsn, user, passwd = ''): + """Connect to odbc, we need dsn, user and optionally password""" + self.dsn = dsn + self.user = user + self.passwd = passwd + + sn = create_buffer(dsn) + un = create_buffer(user) + pw = create_buffer(passwd) + + ret = ODBC_API.SQLConnect(self.dbc_h, sn, len(sn), un, len(un), pw, len(pw)) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + + self.update_type_size_info() + self.connected = 1 + + + def cursor(self, row_type_callable=None): + #self.settimeout(self.timeout) + if not self.connected: + raise ProgrammingError('HY000','Attempt to use a closed connection.') + + + return Cursor(self, row_type_callable=row_type_callable) + + def update_type_size_info(self): + for sql_type in ( + SQL_TYPE_TIMESTAMP, + SQL_TYPE_DATE, + SQL_TYPE_TIME, + SQL_SS_TIME2, + ): + cur = Cursor(self) + info_tuple = cur.getTypeInfo(sql_type) + if info_tuple != None: + self.type_size_dic[sql_type] = info_tuple[2], info_tuple[14] + cur.close() + + + def commit(self): + if not self.connected: + raise ProgrammingError('HY000','Attempt to use a closed connection.') + + ret = ODBC_API.SQLEndTran(SQL_HANDLE_DBC, self.dbc_h, SQL_COMMIT); + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + + def rollback(self): + if not self.connected: + raise ProgrammingError('HY000','Attempt to use a closed connection.') + + ret = ODBC_API.SQLEndTran(SQL_HANDLE_DBC, self.dbc_h, SQL_ROLLBACK); + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + + + + def getinfo(self,infotype): + if infotype not in aInfoTypes.keys(): + raise ProgrammingError('HY000','Invalid getinfo value: '+str(infotype)) + + + if aInfoTypes[infotype] == 'GI_UINTEGER': + total_buf_len = 1000 + alloc_buffer = ctypes.c_ulong() + used_buf_len = ctypes.c_short() + ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\ + ADDR(used_buf_len)) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + result = alloc_buffer.value + + elif aInfoTypes[infotype] == 'GI_USMALLINT': + total_buf_len = 1000 + alloc_buffer = ctypes.c_ushort() + used_buf_len = ctypes.c_short() + ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\ + ADDR(used_buf_len)) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + result = alloc_buffer.value + + else: + total_buf_len = 1000 + alloc_buffer = create_buffer(total_buf_len) + used_buf_len = ctypes.c_short() + ret = ODBC_API.SQLGetInfo(self.dbc_h,infotype,ADDR(alloc_buffer), total_buf_len,\ + ADDR(used_buf_len)) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + result = alloc_buffer.value + if aInfoTypes[infotype] == 'GI_YESNO': + if result[0] == 'Y': + result = True + else: + result = False + + return result + + def __exit__(self, type, value, traceback): + if value: + self.rollback() + else: + self.commit() + + if self.connected: + self.close() + + def __enter__(self): + return self + + def __del__(self): + if self.connected: + self.close() + + def close(self): + if not self.connected: + raise ProgrammingError('HY000','Attempt to close a closed connection.') + + + if self.connected: + if DEBUG: print 'disconnect' + if not self.autocommit: + self.rollback() + ret = ODBC_API.SQLDisconnect(self.dbc_h) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) + if DEBUG: print 'free dbc' + ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_DBC, self.dbc_h) + validate(ret, SQL_HANDLE_DBC, self.dbc_h) +# if shared_env_h.value: +# if DEBUG: print 'env' +# ret = ODBC_API.SQLFreeHandle(SQL_HANDLE_ENV, shared_env_h) +# validate(ret, SQL_HANDLE_ENV, shared_env_h) + self.connected = 0 + +odbc = Connection +connect = odbc +''' +def connect(connectString = '', autocommit = False, ansi = False, timeout = 0, unicode_results = False, readonly = False, **kargs): + return odbc(connectString, autocommit, ansi, timeout, unicode_results, readonly, kargs) +''' + +def win_create_mdb(mdb_path, sort_order = "General\0\0"): + if sys.platform not in ('win32','cli'): + raise Exception('This function is available for use in Windows only.') + #CREATE_DB= + ctypes.windll.ODBCCP32.SQLConfigDataSource.argtypes = [ctypes.c_void_p,ctypes.c_ushort,ctypes.c_char_p,ctypes.c_char_p] + c_Path = "CREATE_DB=" + mdb_path + " " + sort_order + ODBC_ADD_SYS_DSN = 1 + ret = ctypes.windll.ODBCCP32.SQLConfigDataSource(None,ODBC_ADD_SYS_DSN,"Microsoft Access Driver (*.mdb)", c_Path) + if not ret: + raise Exception('Failed to create Access mdb file. Please check file path, permission and Access driver readiness.') + + +def win_compact_mdb(mdb_path, compacted_mdb_path, sort_order = "General\0\0"): + if sys.platform not in ('win32','cli'): + raise Exception('This function is available for use in Windows only.') + #COMPACT_DB= + c_Path = "COMPACT_DB=" + mdb_path + " " + compacted_mdb_path + " " + sort_order + ODBC_ADD_SYS_DSN = 1 + ctypes.windll.ODBCCP32.SQLConfigDataSource.argtypes = [ctypes.c_void_p,ctypes.c_ushort,ctypes.c_char_p,ctypes.c_char_p] + ret = ctypes.windll.ODBCCP32.SQLConfigDataSource(None,ODBC_ADD_SYS_DSN,"Microsoft Access Driver (*.mdb)", c_Path) + if not ret: + raise Exception('Failed to compact Access mdb file. Please check file path, permission and Access driver readiness.') + +def dataSources(): + """Return a list with [name, descrition]""" + dsn = create_buffer(1024) + desc = create_buffer(1024) + dsn_len = ctypes.c_short() + desc_len = ctypes.c_short() + dsn_list = {} + try: + lock.acquire() + if shared_env_h == None: + AllocateEnv() + finally: + lock.release() + while 1: + ret = ODBC_API.SQLDataSources(shared_env_h, SQL_FETCH_NEXT, \ + dsn, len(dsn), ADDR(dsn_len), desc, len(desc), ADDR(desc_len)) + if ret == SQL_NO_DATA_FOUND: + break + elif not ret in (SQL_SUCCESS, SQL_SUCCESS_WITH_INFO): + ctrl_err(SQL_HANDLE_ENV, shared_env_h, ret) + else: + dsn_list[dsn.value] = desc.value + return dsn_list diff --git a/gluon/globals.py b/gluon/globals.py index 31e8813f..9ab524e0 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -695,7 +695,7 @@ class Session(Storage): def forget(self, response=None): self._close(response) - self._forget = True + self._forget = True def _try_store_in_cookie(self, request, response): if response.session_storage_type != 'cookie': diff --git a/gluon/languages.py b/gluon/languages.py index f19a0df4..fe968666 100644 --- a/gluon/languages.py +++ b/gluon/languages.py @@ -238,7 +238,7 @@ def read_possible_languages_aux(langdir): plurals = {} flist = oslistdir(langdir) if isdir(langdir) else [] - + # scan languages directory for plural dict files: for pname in flist: if regex_plural_file.match(pname): diff --git a/gluon/sanitizer.py b/gluon/sanitizer.py index 18fbfd3f..b91199f3 100644 --- a/gluon/sanitizer.py +++ b/gluon/sanitizer.py @@ -226,4 +226,3 @@ def sanitize(text, permitted_tags=[ return str(text) return XssCleaner(permitted_tags=permitted_tags, allowed_attributes=allowed_attributes).strip(text, escape) - diff --git a/gluon/serializers.py b/gluon/serializers.py index 388c8df1..de21ddbc 100644 --- a/gluon/serializers.py +++ b/gluon/serializers.py @@ -181,4 +181,3 @@ def loads_yaml(data): if have_yaml: return yamlib.load(data) else: raise ImportError("No YAML serializer available") - diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index ea382456..e36f423b 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -2573,7 +2573,7 @@ class SQLFORM(FORM): header = table._plural next = grid.create_form or grid.update_form or grid.view_form breadcrumbs.append(LI( - A(T(header), _class=trap_class(),_href=url()), + A(T(header), _class=trap_class(),_href=url()), SPAN(divider, _class='divider') if next else '', _class='active w2p_grid_breadcrumb_elem')) if grid.create_form: @@ -2584,7 +2584,7 @@ class SQLFORM(FORM): header = T('View %s' % format(table,grid.view_form.record)) if next: breadcrumbs.append(LI( - A(T(header), _class=trap_class(),_href=url()), + A(T(header), _class=trap_class(),_href=url()), _class='active w2p_grid_breadcrumb_elem')) grid.insert( 0, DIV(UL(*breadcrumbs, **{'_class': breadcrumbs_class}), diff --git a/gluon/tools.py b/gluon/tools.py index 29c5580e..f2234332 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -347,10 +347,10 @@ class Mail(object): mail.send_mail() method self.error: Exception message or None if above was successful """ - + # We don't want to use base64 encoding for unicode mail Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') - + def encode_header(key): if [c for c in key if 32 > ord(c) or ord(c) > 127]: return Header.Header(key.encode('utf-8'), 'utf-8') @@ -1429,7 +1429,7 @@ class Auth(object): def represent(id, record=None, s=settings): try: user = s.table_user(id) - return '%s %s' % (user.get("first_name", user.get("email")), + return '%s %s' % (user.get("first_name", user.get("email")), user.get("last_name", '')) except: return id @@ -1769,14 +1769,14 @@ class Auth(object): reads current.request.env.http_authorization and returns basic_allowed,basic_accepted,user. - + if basic_auth_realm is defined is a callable it's return value is used to set the basic authentication realm, if it's a string its content is used instead. Otherwise basic authentication realm is set to the application name. If basic_auth_realm is None or False (the default) the behavior is to skip sending any challenge. - + """ if not self.settings.allow_basic_login: return (False, False, False) @@ -1788,7 +1788,7 @@ class Auth(object): basic_realm = unicode(basic_auth_realm) elif basic_auth_realm is True: basic_realm = u'' + current.request.application - http_401 = HTTP(401, u'Not Authorized', + http_401 = HTTP(401, u'Not Authorized', **{'WWW-Authenticate': u'Basic realm="' + basic_realm + '"'}) if not basic or not basic[:6].lower() == 'basic ': if basic_auth_realm: @@ -4330,7 +4330,7 @@ class Service(object): # jsonrpc 2.0 error types. records the following structure {code: (message,meaning)} jsonrpc_errors = { - -32700: ("Parse error. Invalid JSON was received by the server.", "An error occurred on the server while parsing the JSON text."), + -32700: ("Parse error. Invalid JSON was received by the server.", "An error occurred on the server while parsing the JSON text."), -32600: ("Invalid Request", "The JSON sent is not a valid Request object."), -32601: ("Method not found", "The method does not exist / is not available."), -32602: ("Invalid params", "Invalid method parameter(s)."), @@ -4385,7 +4385,7 @@ class Service(object): return return_error(id, 100, 'Exception %s: %s' % (etype, eval)) def serve_jsonrpc2(self, data=None, batch_element=False): - + def return_response(id, result): if not must_respond: return None @@ -4416,9 +4416,9 @@ class Service(object): :returns: - True -- if successful - False -- if no error should be reported (i.e. data is missing 'id' member) - + :raises: JsonRPCException - + """ iparms = set(data.keys()) @@ -4434,8 +4434,8 @@ class Service(object): return True - - + + request = current.request response = current.response if not data: @@ -4444,7 +4444,7 @@ class Service(object): data = json_parser.loads(request.body.read()) except ValueError: # decoding error in json lib return return_error(None, -32700) - except json_parser.JSONDecodeError: # decoding error in simplejson lib + except json_parser.JSONDecodeError: # decoding error in simplejson lib return return_error(None, -32700) # Batch handling @@ -4460,12 +4460,12 @@ class Service(object): return "[" + ','.join(retlist) + "]" methods = self.jsonrpc2_procedures methods.update(self.jsonrpc_procedures) - + try: must_respond = validate(data) except Service.JsonRpcException, e: return return_error(None, e.code, e.info) - + id, method, params = data.get('id'), data['method'], data.get('params', '') if not method in methods: return return_error(id, -32601, data='Method "%s" does not exist' % method) @@ -4492,7 +4492,7 @@ class Service(object): except: etype, eval, etb = sys.exc_info() return return_error(id, -32099, data='Exception %s: %s' % (etype, eval)) - + def serve_xmlrpc(self): request = current.request @@ -5309,7 +5309,7 @@ class Wiki(object): slugs=db(db.wiki_page.id>0).select(db.wiki_page.id,db.wiki_page.slug) options=[OPTION(row.slug,_value=row.id) for row in slugs] options.insert(0, OPTION('',_value='')) - fields = [Field("slug", default=current.request.args(1) or + fields = [Field("slug", default=current.request.args(1) or self.settings.force_prefix, requires=(IS_SLUG(), IS_NOT_IN_DB(db,db.wiki_page.slug))),] if self.settings.templates: @@ -5342,7 +5342,7 @@ class Wiki(object): content = SQLFORM.grid( wiki_table, fields = [wiki_table.slug, - wiki_table.title, wiki_table.tags, + wiki_table.title, wiki_table.tags, wiki_table.can_read, wiki_table.can_edit], links=[ lambda row: