| """adodbapi.remote - A python DB API 2.0 (PEP 249) interface to Microsoft ADO |
| |
| Copyright (C) 2002 Henrik Ekelund, version 2.1 by Vernon Cole |
| * http://sourceforge.net/projects/pywin32 |
| * http://sourceforge.net/projects/adodbapi |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
| django adaptations and refactoring thanks to Adam Vandenberg |
| |
| DB-API 2.0 specification: http://www.python.org/dev/peps/pep-0249/ |
| |
| This module source should run correctly in CPython versions 2.5 and later, |
| or IronPython version 2.7 and later, |
| or, after running through 2to3.py, CPython 3.0 or later. |
| """ |
|
|
| __version__ = "2.6.0.4" |
| version = "adodbapi.remote v" + __version__ |
|
|
| import os |
| import sys |
| import array |
| import time |
| import datetime |
|
|
| |
| try: |
| import Pyro4 |
| except ImportError: |
| print('* * * Sorry, server operation requires Pyro4. Please "pip import" it.') |
| exit(11) |
|
|
| import adodbapi |
| import adodbapi.apibase as api |
| import adodbapi.process_connect_string |
| from adodbapi.apibase import ProgrammingError |
|
|
| _BaseException = api._BaseException |
|
|
| sys.excepthook = Pyro4.util.excepthook |
| Pyro4.config.PREFER_IP_VERSION = 0 |
| Pyro4.config.COMMTIMEOUT = 40.0 |
| Pyro4.config.SERIALIZER = "pickle" |
|
|
| try: |
| verbose = int(os.environ["ADODBAPI_VERBOSE"]) |
| except: |
| verbose = False |
| if verbose: |
| print(version) |
|
|
| |
| unicodeType = str |
| longType = int |
| StringTypes = str |
| makeByteBuffer = bytes |
| memoryViewType = memoryview |
|
|
| |
| |
| Binary = makeByteBuffer |
|
|
|
|
| def Date(year, month, day): |
| return datetime.date(year, month, day) |
|
|
|
|
| def Time(hour, minute, second): |
| return datetime.time(hour, minute, second) |
|
|
|
|
| def Timestamp(year, month, day, hour, minute, second): |
| return datetime.datetime(year, month, day, hour, minute, second) |
|
|
|
|
| def DateFromTicks(ticks): |
| return Date(*time.gmtime(ticks)[:3]) |
|
|
|
|
| def TimeFromTicks(ticks): |
| return Time(*time.gmtime(ticks)[3:6]) |
|
|
|
|
| def TimestampFromTicks(ticks): |
| return Timestamp(*time.gmtime(ticks)[:6]) |
|
|
|
|
| def connect(*args, **kwargs): |
| """Create and open a remote db-api database connection object""" |
| |
| kwargs = adodbapi.process_connect_string.process(args, kwargs) |
| |
| kwargs.setdefault( |
| "pyro_connection", "PYRO:ado.connection@%(proxy_host)s:%(proxy_port)s" |
| ) |
| if not "proxy_port" in kwargs: |
| try: |
| pport = os.environ["PROXY_PORT"] |
| except KeyError: |
| pport = 9099 |
| kwargs["proxy_port"] = pport |
| if not "proxy_host" in kwargs or not kwargs["proxy_host"]: |
| try: |
| phost = os.environ["PROXY_HOST"] |
| except KeyError: |
| phost = "[::1]" |
| kwargs["proxy_host"] = phost |
| ado_uri = kwargs["pyro_connection"] % kwargs |
| |
| auto_retry = 3 |
| while auto_retry: |
| try: |
| dispatcher = Pyro4.Proxy(ado_uri) |
| if "comm_timeout" in kwargs: |
| dispatcher._pyroTimeout = float(kwargs["comm_timeout"]) |
| uri = dispatcher.make_connection() |
| break |
| except Pyro4.core.errors.PyroError: |
| auto_retry -= 1 |
| if auto_retry: |
| time.sleep(1) |
| else: |
| raise api.DatabaseError("Cannot create connection to=%s" % ado_uri) |
|
|
| conn_uri = fix_uri(uri, kwargs) |
| while auto_retry: |
| try: |
| host_conn = Pyro4.Proxy( |
| conn_uri |
| ) |
| break |
| except Pyro4.core.errors.PyroError: |
| auto_retry -= 1 |
| if auto_retry: |
| time.sleep(1) |
| else: |
| raise api.DatabaseError( |
| "Cannot create ADO connection object using=%s" % conn_uri |
| ) |
| if "comm_timeout" in kwargs: |
| host_conn._pyroTimeout = float(kwargs["comm_timeout"]) |
| |
| myConn = Connection() |
| while auto_retry: |
| try: |
| myConn.connect( |
| kwargs, host_conn |
| ) |
| break |
| except Pyro4.core.errors.PyroError: |
| auto_retry -= 1 |
| if auto_retry: |
| time.sleep(1) |
| else: |
| raise api.DatabaseError( |
| "Pyro error creating connection to/thru=%s" % repr(kwargs) |
| ) |
| except _BaseException as e: |
| raise api.DatabaseError( |
| "Error creating remote connection to=%s, e=%s, %s" |
| % (repr(kwargs), repr(e), sys.exc_info()[2]) |
| ) |
| return myConn |
|
|
|
|
| def fix_uri(uri, kwargs): |
| """convert a generic pyro uri with '0.0.0.0' into the address we actually called""" |
| u = uri.asString() |
| s = u.split("[::0]") |
| if len(s) == 1: |
| s = u.split("0.0.0.0") |
| if len(s) > 1: |
| return kwargs["proxy_host"].join(s) |
| return uri |
|
|
|
|
| |
| class Connection(object): |
| |
| Warning = api.Warning |
| Error = api.Error |
| InterfaceError = api.InterfaceError |
| DataError = api.DataError |
| DatabaseError = api.DatabaseError |
| OperationalError = api.OperationalError |
| IntegrityError = api.IntegrityError |
| InternalError = api.InternalError |
| NotSupportedError = api.NotSupportedError |
| ProgrammingError = api.ProgrammingError |
| |
| paramstyle = api.paramstyle |
|
|
| @property |
| def dbapi(self): |
| "Return a reference to the DBAPI module for this Connection." |
| return api |
|
|
| def __init__(self): |
| self.proxy = None |
| self.kwargs = {} |
| self.errorhandler = None |
| self.supportsTransactions = False |
| self.paramstyle = api.paramstyle |
| self.timeout = 30 |
| self.cursors = {} |
|
|
| def connect(self, kwargs, connection_maker): |
| self.kwargs = kwargs |
| if verbose: |
| print('%s attempting: "%s"' % (version, repr(kwargs))) |
| self.proxy = connection_maker |
| |
| ret = self.proxy.connect(kwargs) |
| |
| |
| if ret is not True: |
| self._raiseConnectionError( |
| api.OperationalError, "Proxy returns error message=%s" % repr(ret) |
| ) |
|
|
| self.supportsTransactions = self.getIndexedValue("supportsTransactions") |
| self.paramstyle = self.getIndexedValue("paramstyle") |
| self.timeout = self.getIndexedValue("timeout") |
| if verbose: |
| print("adodbapi.remote New connection at %X" % id(self)) |
|
|
| def _raiseConnectionError(self, errorclass, errorvalue): |
| eh = self.errorhandler |
| if eh is None: |
| eh = api.standardErrorHandler |
| eh(self, None, errorclass, errorvalue) |
|
|
| def close(self): |
| """Close the connection now (rather than whenever __del__ is called). |
| |
| The connection will be unusable from this point forward; |
| an Error (or subclass) exception will be raised if any operation is attempted with the connection. |
| The same applies to all cursor objects trying to use the connection. |
| """ |
| for crsr in list(self.cursors.values())[ |
| : |
| ]: |
| crsr.close() |
| try: |
| """close the underlying remote Connection object""" |
| self.proxy.close() |
| if verbose: |
| print("adodbapi.remote Closed connection at %X" % id(self)) |
| object.__delattr__( |
| self, "proxy" |
| ) |
| except Exception: |
| pass |
|
|
| def __del__(self): |
| try: |
| self.proxy.close() |
| except: |
| pass |
|
|
| def commit(self): |
| """Commit any pending transaction to the database. |
| |
| Note that if the database supports an auto-commit feature, |
| this must be initially off. An interface method may be provided to turn it back on. |
| Database modules that do not support transactions should implement this method with void functionality. |
| """ |
| if not self.supportsTransactions: |
| return |
| result = self.proxy.commit() |
| if result: |
| self._raiseConnectionError( |
| api.OperationalError, "Error during commit: %s" % result |
| ) |
|
|
| def _rollback(self): |
| """In case a database does provide transactions this method causes the the database to roll back to |
| the start of any pending transaction. Closing a connection without committing the changes first will |
| cause an implicit rollback to be performed. |
| """ |
| result = self.proxy.rollback() |
| if result: |
| self._raiseConnectionError( |
| api.OperationalError, "Error during rollback: %s" % result |
| ) |
|
|
| def __setattr__(self, name, value): |
| if name in ("paramstyle", "timeout", "autocommit"): |
| if self.proxy: |
| self.proxy.send_attribute_to_host(name, value) |
| object.__setattr__(self, name, value) |
|
|
| def __getattr__(self, item): |
| if ( |
| item == "rollback" |
| ): |
| if self.supportsTransactions: |
| return ( |
| self._rollback |
| ) |
| else: |
| raise self.ProgrammingError( |
| "this data provider does not support Rollback" |
| ) |
| elif item in ( |
| "dbms_name", |
| "dbms_version", |
| "connection_string", |
| "autocommit", |
| ): |
| return self.getIndexedValue(item) |
| elif item == "proxy": |
| raise self.ProgrammingError("Attempting to use closed connection") |
| else: |
| raise self.ProgrammingError('No remote access for attribute="%s"' % item) |
|
|
| def getIndexedValue(self, index): |
| r = self.proxy.get_attribute_for_remote(index) |
| return r |
|
|
| def cursor(self): |
| "Return a new Cursor Object using the connection." |
| myCursor = Cursor(self) |
| return myCursor |
|
|
| def _i_am_here(self, crsr): |
| "message from a new cursor proclaiming its existence" |
| self.cursors[crsr.id] = crsr |
|
|
| def _i_am_closing(self, crsr): |
| "message from a cursor giving connection a chance to clean up" |
| try: |
| del self.cursors[crsr.id] |
| except: |
| pass |
|
|
| def __enter__(self): |
| return self |
|
|
| def __exit__(self, exc_type, exc_val, exc_tb): |
| if exc_type: |
| self._rollback() |
| else: |
| self.commit() |
|
|
| def get_table_names(self): |
| return self.proxy.get_table_names() |
|
|
|
|
| def fixpickle(x): |
| """pickle barfs on buffer(x) so we pass as array.array(x) then restore to original form for .execute()""" |
| if x is None: |
| return None |
| if isinstance(x, dict): |
| |
| newargs = {} |
| for arg, val in list(x.items()): |
| if isinstance(val, memoryViewType): |
| newval = array.array("B") |
| newval.fromstring(val) |
| newargs[arg] = newval |
| else: |
| newargs[arg] = val |
| return newargs |
| |
| newargs = [] |
| for arg in x: |
| if isinstance(arg, memoryViewType): |
| newarg = array.array("B") |
| newarg.fromstring(arg) |
| newargs.append(newarg) |
| else: |
| newargs.append(arg) |
| return newargs |
|
|
|
|
| class Cursor(object): |
| def __init__(self, connection): |
| self.command = None |
| self.errorhandler = None |
| self.connection = connection |
| self.proxy = self.connection.proxy |
| self.rs = None |
| self.converters = NotImplemented |
| self.id = connection.proxy.build_cursor() |
| connection._i_am_here(self) |
| self.recordset_format = api.RS_REMOTE |
| if verbose: |
| print( |
| "%s New cursor at %X on conn %X" |
| % (version, id(self), id(self.connection)) |
| ) |
|
|
| def prepare(self, operation): |
| self.command = operation |
| try: |
| del self.description |
| except AttributeError: |
| pass |
| self.proxy.crsr_prepare(self.id, operation) |
|
|
| def __iter__(self): |
| return iter(self.fetchone, None) |
|
|
| def __next__(self): |
| r = self.fetchone() |
| if r: |
| return r |
| raise StopIteration |
|
|
| def __enter__(self): |
| "Allow database cursors to be used with context managers." |
| return self |
|
|
| def __exit__(self, exc_type, exc_val, exc_tb): |
| "Allow database cursors to be used with context managers." |
| self.close() |
|
|
| def __getattr__(self, key): |
| if key == "numberOfColumns": |
| try: |
| return len(self.rs[0]) |
| except: |
| return 0 |
| if key == "description": |
| try: |
| self.description = self.proxy.crsr_get_description(self.id)[:] |
| return self.description |
| except TypeError: |
| return None |
| if key == "columnNames": |
| try: |
| r = dict( |
| self.proxy.crsr_get_columnNames(self.id) |
| ) |
|
|
| except TypeError: |
| r = {} |
| self.columnNames = r |
| return r |
|
|
| if key == "remote_cursor": |
| raise api.OperationalError |
| try: |
| return self.proxy.crsr_get_attribute_for_remote(self.id, key) |
| except AttributeError: |
| raise api.InternalError( |
| 'Failure getting attribute "%s" from proxy cursor.' % key |
| ) |
|
|
| def __setattr__(self, key, value): |
| if key == "arraysize": |
| self.proxy.crsr_set_arraysize(self.id, value) |
| if key == "paramstyle": |
| if value in api.accepted_paramstyles: |
| self.proxy.crsr_set_paramstyle(self.id, value) |
| else: |
| self._raiseCursorError( |
| api.ProgrammingError, 'invalid paramstyle ="%s"' % value |
| ) |
| object.__setattr__(self, key, value) |
|
|
| def _raiseCursorError(self, errorclass, errorvalue): |
| eh = self.errorhandler |
| if eh is None: |
| eh = api.standardErrorHandler |
| eh(self.connection, self, errorclass, errorvalue) |
|
|
| def execute(self, operation, parameters=None): |
| if self.connection is None: |
| self._raiseCursorError( |
| ProgrammingError, "Attempted operation on closed cursor" |
| ) |
| self.command = operation |
| try: |
| del self.description |
| except AttributeError: |
| pass |
| try: |
| del self.columnNames |
| except AttributeError: |
| pass |
| fp = fixpickle(parameters) |
| if verbose > 2: |
| print( |
| ( |
| '%s executing "%s" with params=%s' |
| % (version, operation, repr(parameters)) |
| ) |
| ) |
| result = self.proxy.crsr_execute(self.id, operation, fp) |
| if result: |
| self._raiseCursorError(result[0], result[1]) |
|
|
| def executemany(self, operation, seq_of_parameters): |
| if self.connection is None: |
| self._raiseCursorError( |
| ProgrammingError, "Attempted operation on closed cursor" |
| ) |
| self.command = operation |
| try: |
| del self.description |
| except AttributeError: |
| pass |
| try: |
| del self.columnNames |
| except AttributeError: |
| pass |
| sq = [fixpickle(x) for x in seq_of_parameters] |
| if verbose > 2: |
| print( |
| ( |
| '%s executemany "%s" with params=%s' |
| % (version, operation, repr(seq_of_parameters)) |
| ) |
| ) |
| self.proxy.crsr_executemany(self.id, operation, sq) |
|
|
| def nextset(self): |
| try: |
| del self.description |
| except AttributeError: |
| pass |
| try: |
| del self.columnNames |
| except AttributeError: |
| pass |
| if verbose > 2: |
| print(("%s nextset" % version)) |
| return self.proxy.crsr_nextset(self.id) |
|
|
| def callproc(self, procname, parameters=None): |
| if self.connection is None: |
| self._raiseCursorError( |
| ProgrammingError, "Attempted operation on closed cursor" |
| ) |
| self.command = procname |
| try: |
| del self.description |
| except AttributeError: |
| pass |
| try: |
| del self.columnNames |
| except AttributeError: |
| pass |
| fp = fixpickle(parameters) |
| if verbose > 2: |
| print( |
| ( |
| '%s callproc "%s" with params=%s' |
| % (version, procname, repr(parameters)) |
| ) |
| ) |
| return self.proxy.crsr_callproc(self.id, procname, fp) |
|
|
| def fetchone(self): |
| try: |
| f1 = self.proxy.crsr_fetchone(self.id) |
| except _BaseException as e: |
| self._raiseCursorError(api.DatabaseError, e) |
| else: |
| if f1 is None: |
| return None |
| self.rs = [f1] |
| return api.SQLrows(self.rs, 1, self)[ |
| 0 |
| ] |
|
|
| def fetchmany(self, size=None): |
| try: |
| self.rs = self.proxy.crsr_fetchmany(self.id, size) |
| if not self.rs: |
| return [] |
| r = api.SQLrows(self.rs, len(self.rs), self) |
| return r |
| except Exception as e: |
| self._raiseCursorError(api.DatabaseError, e) |
|
|
| def fetchall(self): |
| try: |
| self.rs = self.proxy.crsr_fetchall(self.id) |
| if not self.rs: |
| return [] |
| return api.SQLrows(self.rs, len(self.rs), self) |
| except Exception as e: |
| self._raiseCursorError(api.DatabaseError, e) |
|
|
| def close(self): |
| if self.connection is None: |
| return |
| self.connection._i_am_closing(self) |
| try: |
| self.proxy.crsr_close(self.id) |
| except: |
| pass |
| try: |
| del self.description |
| except: |
| pass |
| try: |
| del self.rs |
| except: |
| pass |
| self.connection = ( |
| None |
| ) |
| self.proxy = None |
| if verbose: |
| print("adodbapi.remote Closed cursor at %X" % id(self)) |
|
|
| def __del__(self): |
| try: |
| self.close() |
| except: |
| pass |
|
|
| def setinputsizes(self, sizes): |
| pass |
|
|
| def setoutputsize(self, size, column=None): |
| pass |
|
|