|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 |
- """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 array
- import datetime
- import os
- import sys
- import time
-
- # Pyro4 is required for server and remote operation --> https://pypi.python.org/pypi/Pyro4/
- 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 # allow system to prefer IPv6
- Pyro4.config.COMMTIMEOUT = 40.0 # a bit longer than the default SQL server Gtimeout
- Pyro4.config.SERIALIZER = "pickle"
-
- try:
- verbose = int(os.environ["ADODBAPI_VERBOSE"])
- except:
- verbose = False
- if verbose:
- print(version)
-
- # --- define objects to smooth out Python3 <-> Python 2.x differences
- unicodeType = str # this line will be altered by 2to3.py to '= str'
- longType = int # this line will be altered by 2to3.py to '= int'
- StringTypes = str
- makeByteBuffer = bytes
- memoryViewType = memoryview
-
- # -----------------------------------------------------------
- # conversion functions mandated by PEP 249
- Binary = makeByteBuffer # override the function from apibase.py
-
-
- def Date(year, month, day):
- return datetime.date(year, month, day) # dateconverter.Date(year,month,day)
-
-
- def Time(hour, minute, second):
- return datetime.time(hour, minute, second) # dateconverter.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): # --> a remote db-api connection object
- """Create and open a remote db-api database connection object"""
- # process the argument list the programmer gave us
- kwargs = adodbapi.process_connect_string.process(args, kwargs)
- # the "proxy_xxx" keys tell us where to find the PyRO proxy server
- 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]" # '127.0.0.1'
- kwargs["proxy_host"] = phost
- ado_uri = kwargs["pyro_connection"] % kwargs
- # ask PyRO make us a remote connection object
- 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) # get a host connection from the proxy server
- while auto_retry:
- try:
- host_conn = Pyro4.Proxy(
- conn_uri
- ) # bring up an exclusive Pyro connection for my ADO connection
- 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"])
- # make a local clone
- myConn = Connection()
- while auto_retry:
- try:
- myConn.connect(
- kwargs, host_conn
- ) # call my connect method -- hand him the host connection
- 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]") # IPv6 generic address
- if len(s) == 1: # did not find one
- s = u.split("0.0.0.0") # IPv4 generic address
- if len(s) > 1: # found a generic
- return kwargs["proxy_host"].join(s) # fill in our address for the host
- return uri
-
-
- # # # # # ----- the Class that defines a connection ----- # # # # #
- class Connection(object):
- # include connection attributes required by api definition.
- 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
- # set up some class attributes
- paramstyle = api.paramstyle
-
- @property
- def dbapi(self): # a proposed db-api version 3 extension.
- "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
- ##try:
- ret = self.proxy.connect(kwargs) # ask the server to hook us up
- ##except ImportError, e: # Pyro is trying to import pywinTypes.comerrer
- ## self._raiseConnectionError(api.DatabaseError, 'Proxy cannot connect using=%s' % repr(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())[
- :
- ]: # copy the list, then close each one
- 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"
- ) # future attempts to use closed cursor will be caught by __getattr__
- 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) # store attribute locally (too)
-
- def __getattr__(self, item):
- if (
- item == "rollback"
- ): # the rollback method only appears if the database supports transactions
- if self.supportsTransactions:
- return (
- self._rollback
- ) # return the rollback method so the caller can execute it.
- else:
- raise self.ProgrammingError(
- "this data provider does not support Rollback"
- )
- elif item in (
- "dbms_name",
- "dbms_version",
- "connection_string",
- "autocommit",
- ): # 'messages' ):
- 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): # Connections are context managers
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- if exc_type:
- self._rollback() # automatic rollback on errors
- 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):
- # for 'named' paramstyle user will pass a mapping
- 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
- # if not a mapping, then a sequence
- 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 ## was: connection.errorhandler
- self.connection = connection
- self.proxy = self.connection.proxy
- self.rs = None # the fetchable data for this cursor
- 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): # [2.1 Zamarev]
- return iter(self.fetchone, None) # [2.1 Zamarev]
-
- 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)
- ) # copy the remote columns
-
- 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: # an exception was triggered
- 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
- ] # new object to hold the results of the fetch
-
- 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) # take me off the connection's cursors list
- try:
- self.proxy.crsr_close(self.id)
- except:
- pass
- try:
- del self.description
- except:
- pass
- try:
- del self.rs # let go of the recordset
- except:
- pass
- self.connection = (
- None # this will make all future method calls on me throw an exception
- )
- 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
|