| """adodbapi - A python DB API 2.0 (PEP 249) interface to Microsoft ADO |
| |
| Copyright (C) 2002 Henrik Ekelund, versions 2.1 and later by Vernon Cole |
| * http://sourceforge.net/projects/pywin32 |
| * https://github.com/mhammond/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 by 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.7 and later, |
| or IronPython version 2.7 and later, |
| or, after running through 2to3.py, CPython 3.4 or later. |
| """ |
|
|
| __version__ = "2.6.2.0" |
| version = "adodbapi v" + __version__ |
|
|
| import copy |
| import decimal |
| import os |
| import sys |
| import weakref |
|
|
| from . import ado_consts as adc, apibase as api, process_connect_string |
|
|
| try: |
| verbose = int(os.environ["ADODBAPI_VERBOSE"]) |
| except: |
| verbose = False |
| if verbose: |
| print(version) |
|
|
| |
| onWin32 = False |
| if api.onIronPython: |
| from clr import Reference |
| from System import ( |
| Activator, |
| Array, |
| Byte, |
| DateTime, |
| DBNull, |
| Decimal as SystemDecimal, |
| Type, |
| ) |
|
|
| def Dispatch(dispatch): |
| type = Type.GetTypeFromProgID(dispatch) |
| return Activator.CreateInstance(type) |
|
|
| def getIndexedValue(obj, index): |
| return obj.Item[index] |
|
|
| else: |
| try: |
| import pythoncom |
| import pywintypes |
| import win32com.client |
|
|
| onWin32 = True |
|
|
| def Dispatch(dispatch): |
| return win32com.client.Dispatch(dispatch) |
|
|
| except ImportError: |
| import warnings |
|
|
| warnings.warn( |
| "pywin32 package (or IronPython) required for adodbapi.", ImportWarning |
| ) |
|
|
| def getIndexedValue(obj, index): |
| return obj(index) |
|
|
|
|
| from collections.abc import Mapping |
|
|
| |
| unicodeType = str |
| longType = int |
| StringTypes = str |
| maxint = sys.maxsize |
|
|
|
|
| |
| def make_COM_connecter(): |
| try: |
| if onWin32: |
| pythoncom.CoInitialize() |
| c = Dispatch("ADODB.Connection") |
| except: |
| raise api.InterfaceError( |
| "Windows COM Error: Dispatch('ADODB.Connection') failed." |
| ) |
| return c |
|
|
|
|
| def connect(*args, **kwargs): |
| """Connect to a database. |
| |
| call using: |
| :connection_string -- An ADODB formatted connection string, see: |
| * http://www.connectionstrings.com |
| * http://www.asp101.com/articles/john/connstring/default.asp |
| :timeout -- A command timeout value, in seconds (default 30 seconds) |
| """ |
| co = Connection() |
|
|
| kwargs = process_connect_string.process(args, kwargs, True) |
|
|
| try: |
| co.connect(kwargs) |
| return co |
| except Exception as e: |
| message = 'Error opening connection to "%s"' % co.connection_string |
| raise api.OperationalError(e, message) |
|
|
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| defaultIsolationLevel = adc.adXactReadCommitted |
| |
| |
| |
| |
| |
| |
| |
| defaultCursorLocation = adc.adUseClient |
|
|
| dateconverter = api.pythonDateTimeConverter() |
|
|
|
|
| def format_parameters(ADOparameters, show_value=False): |
| """Format a collection of ADO Command Parameters. |
| |
| Used by error reporting in _execute_command. |
| """ |
| try: |
| if show_value: |
| desc = [ |
| 'Name: %s, Dir.: %s, Type: %s, Size: %s, Value: "%s", Precision: %s, NumericScale: %s' |
| % ( |
| p.Name, |
| adc.directions[p.Direction], |
| adc.adTypeNames.get(p.Type, str(p.Type) + " (unknown type)"), |
| p.Size, |
| p.Value, |
| p.Precision, |
| p.NumericScale, |
| ) |
| for p in ADOparameters |
| ] |
| else: |
| desc = [ |
| "Name: %s, Dir.: %s, Type: %s, Size: %s, Precision: %s, NumericScale: %s" |
| % ( |
| p.Name, |
| adc.directions[p.Direction], |
| adc.adTypeNames.get(p.Type, str(p.Type) + " (unknown type)"), |
| p.Size, |
| p.Precision, |
| p.NumericScale, |
| ) |
| for p in ADOparameters |
| ] |
| return "[" + "\n".join(desc) + "]" |
| except: |
| return "[]" |
|
|
|
|
| def _configure_parameter(p, value, adotype, settings_known): |
| """Configure the given ADO Parameter 'p' with the Python 'value'.""" |
|
|
| if adotype in api.adoBinaryTypes: |
| p.Size = len(value) |
| p.AppendChunk(value) |
|
|
| elif isinstance(value, StringTypes): |
| L = len(value) |
| if adotype in api.adoStringTypes: |
| if settings_known: |
| L = min(L, p.Size) |
| p.Value = value[:L] |
| else: |
| p.Value = value |
| if L > 0: |
| p.Size = L |
|
|
| elif isinstance(value, decimal.Decimal): |
| if api.onIronPython: |
| s = str(value) |
| p.Value = s |
| p.Size = len(s) |
| else: |
| p.Value = value |
| exponent = value.as_tuple()[2] |
| digit_count = len(value.as_tuple()[1]) |
| p.Precision = digit_count |
| if exponent == 0: |
| p.NumericScale = 0 |
| elif exponent < 0: |
| p.NumericScale = -exponent |
| if p.Precision < p.NumericScale: |
| p.Precision = p.NumericScale |
| else: |
| p.NumericScale = 0 |
| p.Precision = digit_count + exponent |
|
|
| elif type(value) in dateconverter.types: |
| if settings_known and adotype in api.adoDateTimeTypes: |
| p.Value = dateconverter.COMDate(value) |
| else: |
| |
| s = dateconverter.DateObjectToIsoFormatString(value) |
| p.Value = s |
| p.Size = len(s) |
|
|
| elif api.onIronPython and isinstance(value, longType): |
| s = str(value) |
| p.Value = s |
|
|
| elif adotype == adc.adEmpty: |
| p.Type = ( |
| adc.adInteger |
| ) |
| p.Value = None |
|
|
| |
| else: |
| p.Value = value |
|
|
|
|
| |
| 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 |
| FetchFailedError = api.FetchFailedError |
| |
| verbose = api.verbose |
|
|
| @property |
| def dbapi(self): |
| "Return a reference to the DBAPI module for this Connection." |
| return api |
|
|
| def __init__(self): |
| self.connector = None |
| self.paramstyle = api.paramstyle |
| self.supportsTransactions = False |
| self.connection_string = "" |
| self.cursors = weakref.WeakValueDictionary() |
| self.dbms_name = "" |
| self.dbms_version = "" |
| self.errorhandler = None |
| self.transaction_level = 0 |
| self._autocommit = False |
|
|
| def connect(self, kwargs, connection_maker=make_COM_connecter): |
| if verbose > 9: |
| print("kwargs=", repr(kwargs)) |
| try: |
| self.connection_string = ( |
| kwargs["connection_string"] % kwargs |
| ) |
| except Exception as e: |
| self._raiseConnectionError( |
| KeyError, "Python string format error in connection string->" |
| ) |
| self.timeout = kwargs.get("timeout", 30) |
| self.mode = kwargs.get("mode", adc.adModeUnknown) |
| self.kwargs = kwargs |
| if verbose: |
| print('%s attempting: "%s"' % (version, self.connection_string)) |
| self.connector = connection_maker() |
| self.connector.ConnectionTimeout = self.timeout |
| self.connector.ConnectionString = self.connection_string |
| self.connector.Mode = self.mode |
|
|
| try: |
| self.connector.Open() |
| except api.Error: |
| self._raiseConnectionError( |
| api.DatabaseError, |
| "ADO error trying to Open=%s" % self.connection_string, |
| ) |
|
|
| try: |
| if getIndexedValue(self.connector.Properties, "Transaction DDL").Value != 0: |
| self.supportsTransactions = True |
| except pywintypes.com_error: |
| pass |
| self.dbms_name = getIndexedValue(self.connector.Properties, "DBMS Name").Value |
| try: |
| self.dbms_version = getIndexedValue( |
| self.connector.Properties, "DBMS Version" |
| ).Value |
| except pywintypes.com_error: |
| pass |
| self.connector.CursorLocation = defaultCursorLocation |
| if self.supportsTransactions: |
| self.connector.IsolationLevel = defaultIsolationLevel |
| self._autocommit = bool(kwargs.get("autocommit", False)) |
| if not self._autocommit: |
| self.transaction_level = ( |
| self.connector.BeginTrans() |
| ) |
| else: |
| self._autocommit = True |
| if "paramstyle" in kwargs: |
| self.paramstyle = kwargs["paramstyle"] |
| self.messages = [] |
| if verbose: |
| print("adodbapi 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 _closeAdoConnection(self): |
| """close the underlying ADO Connection object, |
| rolling it back first if it supports transactions.""" |
| if self.connector is None: |
| return |
| if not self._autocommit: |
| if self.transaction_level: |
| try: |
| self.connector.RollbackTrans() |
| except: |
| pass |
| self.connector.Close() |
| if verbose: |
| print("adodbapi Closed connection at %X" % id(self)) |
|
|
| 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(dont_tell_me=True) |
| self.messages = [] |
| try: |
| self._closeAdoConnection() |
| except Exception as e: |
| self._raiseConnectionError(sys.exc_info()[0], sys.exc_info()[1]) |
|
|
| self.connector = None |
| |
| |
|
|
| 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. |
| """ |
| self.messages = [] |
| if not self.supportsTransactions: |
| return |
|
|
| try: |
| self.transaction_level = self.connector.CommitTrans() |
| if verbose > 1: |
| print("commit done on connection at %X" % id(self)) |
| if not ( |
| self._autocommit |
| or (self.connector.Attributes & adc.adXactAbortRetaining) |
| ): |
| |
| |
| |
| self.transaction_level = self.connector.BeginTrans() |
| except Exception as e: |
| self._raiseConnectionError(api.ProgrammingError, e) |
|
|
| 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. |
| |
| If the database does not support the functionality required by the method, the interface should |
| throw an exception in case the method is used. |
| The preferred approach is to not implement the method and thus have Python generate |
| an AttributeError in case the method is requested. This allows the programmer to check for database |
| capabilities using the standard hasattr() function. |
| |
| For some dynamically configured interfaces it may not be appropriate to require dynamically making |
| the method available. These interfaces should then raise a NotSupportedError to indicate the |
| non-ability to perform the roll back when the method is invoked. |
| """ |
| self.messages = [] |
| if ( |
| self.transaction_level |
| ): |
| try: |
| self.transaction_level = self.connector.RollbackTrans() |
| if verbose > 1: |
| print("rollback done on connection at %X" % id(self)) |
| if not self._autocommit and not ( |
| self.connector.Attributes & adc.adXactAbortRetaining |
| ): |
| |
| |
| |
| if ( |
| not self.transaction_level |
| ): |
| self.transaction_level = self.connector.BeginTrans() |
| except Exception as e: |
| self._raiseConnectionError(api.ProgrammingError, e) |
|
|
| def __setattr__(self, name, value): |
| if name == "autocommit": |
| if self.supportsTransactions: |
| object.__setattr__(self, "_autocommit", bool(value)) |
| try: |
| self._rollback() |
| except: |
| pass |
| return |
| elif name == "paramstyle": |
| if value not in api.accepted_paramstyles: |
| self._raiseConnectionError( |
| api.NotSupportedError, |
| 'paramstyle="%s" not in:%s' |
| % (value, repr(api.accepted_paramstyles)), |
| ) |
| elif name == "variantConversions": |
| value = copy.copy( |
| value |
| ) |
| object.__setattr__(self, name, value) |
|
|
| def __getattr__(self, item): |
| if ( |
| item == "rollback" |
| ): |
| if self.supportsTransactions: |
| return ( |
| self._rollback |
| ) |
| else: |
| raise AttributeError("this data provider does not support Rollback") |
| elif item == "autocommit": |
| return self._autocommit |
| else: |
| raise AttributeError( |
| 'no such attribute in ADO connection object as="%s"' % item |
| ) |
|
|
| def cursor(self): |
| "Return a new Cursor Object using the connection." |
| self.messages = [] |
| c = Cursor(self) |
| return c |
|
|
| def _i_am_here(self, crsr): |
| "message from a new cursor proclaiming its existence" |
| oid = id(crsr) |
| self.cursors[oid] = crsr |
|
|
| def _i_am_closing(self, crsr): |
| "message from a cursor giving connection a chance to clean up" |
| try: |
| del self.cursors[id(crsr)] |
| except: |
| pass |
|
|
| def printADOerrors(self): |
| j = self.connector.Errors.Count |
| if j: |
| print("ADO Errors:(%i)" % j) |
| for e in self.connector.Errors: |
| print("Description: %s" % e.Description) |
| print("Error: %s %s " % (e.Number, adc.adoErrors.get(e.Number, "unknown"))) |
| if e.Number == adc.ado_error_TIMEOUT: |
| print( |
| "Timeout Error: Try using adodbpi.connect(constr,timeout=Nseconds)" |
| ) |
| print("Source: %s" % e.Source) |
| print("NativeError: %s" % e.NativeError) |
| print("SQL State: %s" % e.SQLState) |
|
|
| def _suggest_error_class(self): |
| """Introspect the current ADO Errors and determine an appropriate error class. |
| |
| Error.SQLState is a SQL-defined error condition, per the SQL specification: |
| http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt |
| |
| The 23000 class of errors are integrity errors. |
| Error 40002 is a transactional integrity error. |
| """ |
| if self.connector is not None: |
| for e in self.connector.Errors: |
| state = str(e.SQLState) |
| if state.startswith("23") or state == "40002": |
| return api.IntegrityError |
| return api.DatabaseError |
|
|
| def __del__(self): |
| try: |
| self._closeAdoConnection() |
| except: |
| pass |
| self.connector = None |
|
|
| 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): |
| schema = self.connector.OpenSchema(20) |
|
|
| tables = [] |
| while not schema.EOF: |
| name = getIndexedValue(schema.Fields, "TABLE_NAME").Value |
| tables.append(name) |
| schema.MoveNext() |
| del schema |
| return tables |
|
|
|
|
| |
| class Cursor(object): |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| def __init__(self, connection): |
| self.command = None |
| self._ado_prepared = False |
| self.messages = [] |
| self.connection = connection |
| self.paramstyle = connection.paramstyle |
| self._parameter_names = [] |
| self.recordset_is_remote = False |
| self.rs = None |
| self.converters = [] |
| self.columnNames = {} |
| self.numberOfColumns = 0 |
| self._description = None |
| self.rowcount = -1 |
| self.errorhandler = connection.errorhandler |
| self.arraysize = 1 |
| connection._i_am_here(self) |
| if verbose: |
| print( |
| "%s New cursor at %X on conn %X" |
| % (version, id(self), id(self.connection)) |
| ) |
|
|
| def __iter__(self): |
| return iter(self.fetchone, None) |
|
|
| def prepare(self, operation): |
| self.command = operation |
| self._description = None |
| self._ado_prepared = "setup" |
|
|
| 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 _raiseCursorError(self, errorclass, errorvalue): |
| eh = self.errorhandler |
| if eh is None: |
| eh = api.standardErrorHandler |
| eh(self.connection, self, errorclass, errorvalue) |
|
|
| def build_column_info(self, recordset): |
| self.converters = [] |
| self.columnNames = {} |
| self._description = None |
|
|
| |
| if (recordset is None) or (recordset.State == adc.adStateClosed): |
| self.rs = None |
| self.numberOfColumns = 0 |
| return |
| self.rs = recordset |
| self.recordset_format = api.RS_ARRAY if api.onIronPython else api.RS_WIN_32 |
| self.numberOfColumns = recordset.Fields.Count |
| try: |
| varCon = self.connection.variantConversions |
| except AttributeError: |
| varCon = api.variantConversions |
| for i in range(self.numberOfColumns): |
| f = getIndexedValue(self.rs.Fields, i) |
| try: |
| self.converters.append( |
| varCon[f.Type] |
| ) |
| except KeyError: |
| self._raiseCursorError( |
| api.InternalError, "Data column of Unknown ADO type=%s" % f.Type |
| ) |
| self.columnNames[f.Name.lower()] = i |
|
|
| def _makeDescriptionFromRS(self): |
| |
| if self.rs is None: |
| self._description = None |
| return |
| desc = [] |
| for i in range(self.numberOfColumns): |
| f = getIndexedValue(self.rs.Fields, i) |
| if self.rs.EOF or self.rs.BOF: |
| display_size = None |
| else: |
| display_size = ( |
| f.ActualSize |
| ) |
| null_ok = bool(f.Attributes & adc.adFldMayBeNull) |
| desc.append( |
| ( |
| f.Name, |
| f.Type, |
| display_size, |
| f.DefinedSize, |
| f.Precision, |
| f.NumericScale, |
| null_ok, |
| ) |
| ) |
| self._description = desc |
|
|
| def get_description(self): |
| if not self._description: |
| self._makeDescriptionFromRS() |
| return self._description |
|
|
| def __getattr__(self, item): |
| if item == "description": |
| return self.get_description() |
| object.__getattribute__( |
| self, item |
| ) |
|
|
| def format_description(self, d): |
| """Format db_api description tuple for printing.""" |
| if self.description is None: |
| self._makeDescriptionFromRS() |
| if isinstance(d, int): |
| d = self.description[d] |
| desc = ( |
| "Name= %s, Type= %s, DispSize= %s, IntSize= %s, Precision= %s, Scale= %s NullOK=%s" |
| % ( |
| d[0], |
| adc.adTypeNames.get(d[1], str(d[1]) + " (unknown type)"), |
| d[2], |
| d[3], |
| d[4], |
| d[5], |
| d[6], |
| ) |
| ) |
| return desc |
|
|
| def close(self, dont_tell_me=False): |
| """Close the cursor now (rather than whenever __del__ is called). |
| The cursor will be unusable from this point forward; an Error (or subclass) |
| exception will be raised if any operation is attempted with the cursor. |
| """ |
| if self.connection is None: |
| return |
| self.messages = [] |
| if ( |
| self.rs and self.rs.State != adc.adStateClosed |
| ): |
| self.rs.Close() |
| self.rs = None |
| if not dont_tell_me: |
| self.connection._i_am_closing( |
| self |
| ) |
| self.connection = ( |
| None |
| ) |
| if verbose: |
| print("adodbapi Closed cursor at %X" % id(self)) |
|
|
| def __del__(self): |
| try: |
| self.close() |
| except: |
| pass |
|
|
| def _new_command(self, command_type=adc.adCmdText): |
| self.cmd = None |
| self.messages = [] |
|
|
| if self.connection is None: |
| self._raiseCursorError(api.InterfaceError, None) |
| return |
| try: |
| self.cmd = Dispatch("ADODB.Command") |
| self.cmd.ActiveConnection = self.connection.connector |
| self.cmd.CommandTimeout = self.connection.timeout |
| self.cmd.CommandType = command_type |
| self.cmd.CommandText = self.commandText |
| self.cmd.Prepared = bool(self._ado_prepared) |
| except: |
| self._raiseCursorError( |
| api.DatabaseError, |
| 'Error creating new ADODB.Command object for "%s"' |
| % repr(self.commandText), |
| ) |
|
|
| def _execute_command(self): |
| |
| self.return_value = None |
| recordset = None |
| count = -1 |
| if verbose: |
| print('Executing command="%s"' % self.commandText) |
| try: |
| |
| if api.onIronPython: |
| ra = Reference[int]() |
| recordset = self.cmd.Execute(ra) |
| count = ra.Value |
| else: |
| recordset, count = self.cmd.Execute() |
| |
| except Exception as e: |
| _message = "" |
| if hasattr(e, "args"): |
| _message += str(e.args) + "\n" |
| _message += "Command:\n%s\nParameters:\n%s" % ( |
| self.commandText, |
| format_parameters(self.cmd.Parameters, True), |
| ) |
| klass = self.connection._suggest_error_class() |
| self._raiseCursorError(klass, _message) |
| try: |
| self.rowcount = recordset.RecordCount |
| except: |
| self.rowcount = count |
| self.build_column_info(recordset) |
|
|
| |
| |
| |
| |
| |
| |
|
|
| def get_rowcount(self): |
| return self.rowcount |
|
|
| def get_returned_parameters(self): |
| """with some providers, returned parameters and the .return_value are not available until |
| after the last recordset has been read. In that case, you must coll nextset() until it |
| returns None, then call this method to get your returned information.""" |
|
|
| retLst = ( |
| [] |
| ) |
| for p in tuple(self.cmd.Parameters): |
| if verbose > 2: |
| print( |
| 'Returned=Name: %s, Dir.: %s, Type: %s, Size: %s, Value: "%s",' |
| " Precision: %s, NumericScale: %s" |
| % ( |
| p.Name, |
| adc.directions[p.Direction], |
| adc.adTypeNames.get(p.Type, str(p.Type) + " (unknown type)"), |
| p.Size, |
| p.Value, |
| p.Precision, |
| p.NumericScale, |
| ) |
| ) |
| pyObject = api.convert_to_python(p.Value, api.variantConversions[p.Type]) |
| if p.Direction == adc.adParamReturnValue: |
| self.returnValue = ( |
| pyObject |
| ) |
| self.return_value = pyObject |
| else: |
| retLst.append(pyObject) |
| return retLst |
|
|
| def callproc(self, procname, parameters=None): |
| """Call a stored database procedure with the given name. |
| The sequence of parameters must contain one entry for each |
| argument that the sproc expects. The result of the |
| call is returned as modified copy of the input |
| sequence. Input parameters are left untouched, output and |
| input/output parameters replaced with possibly new values. |
| |
| The sproc may also provide a result set as output, |
| which is available through the standard .fetch*() methods. |
| Extension: A "return_value" property may be set on the |
| cursor if the sproc defines an integer return value. |
| """ |
| self._parameter_names = [] |
| self.commandText = procname |
| self._new_command(command_type=adc.adCmdStoredProc) |
| self._buildADOparameterList(parameters, sproc=True) |
| if verbose > 2: |
| print( |
| "Calling Stored Proc with Params=", |
| format_parameters(self.cmd.Parameters, True), |
| ) |
| self._execute_command() |
| return self.get_returned_parameters() |
|
|
| def _reformat_operation(self, operation, parameters): |
| if self.paramstyle in ("format", "pyformat"): |
| operation, self._parameter_names = api.changeFormatToQmark(operation) |
| elif self.paramstyle == "named" or ( |
| self.paramstyle == "dynamic" and isinstance(parameters, Mapping) |
| ): |
| operation, self._parameter_names = api.changeNamedToQmark( |
| operation |
| ) |
| return operation |
|
|
| def _buildADOparameterList(self, parameters, sproc=False): |
| self.parameters = parameters |
| if parameters is None: |
| parameters = [] |
|
|
| |
| parameters_known = False |
| if sproc: |
| try: |
| self.cmd.Parameters.Refresh() |
| if verbose > 2: |
| print( |
| "ADO detected Params=", |
| format_parameters(self.cmd.Parameters, True), |
| ) |
| print("Program Parameters=", repr(parameters)) |
| parameters_known = True |
| except api.Error: |
| if verbose: |
| print("ADO Parameter Refresh failed") |
| pass |
| else: |
| if len(parameters) != self.cmd.Parameters.Count - 1: |
| raise api.ProgrammingError( |
| "You must supply %d parameters for this stored procedure" |
| % (self.cmd.Parameters.Count - 1) |
| ) |
| if sproc or parameters != []: |
| i = 0 |
| if parameters_known: |
| if self._parameter_names: |
| for i, pm_name in enumerate(self._parameter_names): |
| p = getIndexedValue(self.cmd.Parameters, i) |
| try: |
| _configure_parameter( |
| p, parameters[pm_name], p.Type, parameters_known |
| ) |
| except Exception as e: |
| _message = ( |
| "Error Converting Parameter %s: %s, %s <- %s\n" |
| % ( |
| p.Name, |
| adc.ado_type_name(p.Type), |
| p.Value, |
| repr(parameters[pm_name]), |
| ) |
| ) |
| self._raiseCursorError( |
| api.DataError, _message + "->" + repr(e.args) |
| ) |
| else: |
| for value in parameters: |
| p = getIndexedValue(self.cmd.Parameters, i) |
| if ( |
| p.Direction == adc.adParamReturnValue |
| ): |
| i += 1 |
| p = getIndexedValue(self.cmd.Parameters, i) |
| try: |
| _configure_parameter(p, value, p.Type, parameters_known) |
| except Exception as e: |
| _message = ( |
| "Error Converting Parameter %s: %s, %s <- %s\n" |
| % ( |
| p.Name, |
| adc.ado_type_name(p.Type), |
| p.Value, |
| repr(value), |
| ) |
| ) |
| self._raiseCursorError( |
| api.DataError, _message + "->" + repr(e.args) |
| ) |
| i += 1 |
| else: |
| if ( |
| self._parameter_names |
| ): |
| for parm_name in self._parameter_names: |
| elem = parameters[parm_name] |
| adotype = api.pyTypeToADOType(elem) |
| p = self.cmd.CreateParameter( |
| parm_name, adotype, adc.adParamInput |
| ) |
| _configure_parameter(p, elem, adotype, parameters_known) |
| try: |
| self.cmd.Parameters.Append(p) |
| except Exception as e: |
| _message = "Error Building Parameter %s: %s, %s <- %s\n" % ( |
| p.Name, |
| adc.ado_type_name(p.Type), |
| p.Value, |
| repr(elem), |
| ) |
| self._raiseCursorError( |
| api.DataError, _message + "->" + repr(e.args) |
| ) |
| else: |
| if sproc: |
| p = self.cmd.CreateParameter( |
| "@RETURN_VALUE", adc.adInteger, adc.adParamReturnValue |
| ) |
| self.cmd.Parameters.Append(p) |
|
|
| for elem in parameters: |
| name = "p%i" % i |
| adotype = api.pyTypeToADOType(elem) |
| p = self.cmd.CreateParameter( |
| name, adotype, adc.adParamInput |
| ) |
| _configure_parameter(p, elem, adotype, parameters_known) |
| try: |
| self.cmd.Parameters.Append(p) |
| except Exception as e: |
| _message = "Error Building Parameter %s: %s, %s <- %s\n" % ( |
| p.Name, |
| adc.ado_type_name(p.Type), |
| p.Value, |
| repr(elem), |
| ) |
| self._raiseCursorError( |
| api.DataError, _message + "->" + repr(e.args) |
| ) |
| i += 1 |
| if self._ado_prepared == "setup": |
| self._ado_prepared = ( |
| True |
| ) |
|
|
| def execute(self, operation, parameters=None): |
| """Prepare and execute a database operation (query or command). |
| |
| Parameters may be provided as sequence or mapping and will be bound to variables in the operation. |
| Variables are specified in a database-specific notation |
| (see the module's paramstyle attribute for details). [5] |
| A reference to the operation will be retained by the cursor. |
| If the same operation object is passed in again, then the cursor |
| can optimize its behavior. This is most effective for algorithms |
| where the same operation is used, but different parameters are bound to it (many times). |
| |
| For maximum efficiency when reusing an operation, it is best to use |
| the setinputsizes() method to specify the parameter types and sizes ahead of time. |
| It is legal for a parameter to not match the predefined information; |
| the implementation should compensate, possibly with a loss of efficiency. |
| |
| The parameters may also be specified as list of tuples to e.g. insert multiple rows in |
| a single operation, but this kind of usage is depreciated: executemany() should be used instead. |
| |
| Return value is not defined. |
| |
| [5] The module will use the __getitem__ method of the parameters object to map either positions |
| (integers) or names (strings) to parameter values. This allows for both sequences and mappings |
| to be used as input. |
| The term "bound" refers to the process of binding an input value to a database execution buffer. |
| In practical terms, this means that the input value is directly used as a value in the operation. |
| The client should not be required to "escape" the value so that it can be used -- the value |
| should be equal to the actual database value.""" |
| if ( |
| self.command is not operation |
| or self._ado_prepared == "setup" |
| or not hasattr(self, "commandText") |
| ): |
| if self.command is not operation: |
| self._ado_prepared = False |
| self.command = operation |
| self._parameter_names = [] |
| self.commandText = ( |
| operation |
| if (self.paramstyle == "qmark" or not parameters) |
| else self._reformat_operation(operation, parameters) |
| ) |
| self._new_command() |
| self._buildADOparameterList(parameters) |
| if verbose > 3: |
| print("Params=", format_parameters(self.cmd.Parameters, True)) |
| self._execute_command() |
|
|
| def executemany(self, operation, seq_of_parameters): |
| """Prepare a database operation (query or command) |
| and then execute it against all parameter sequences or mappings found in the sequence seq_of_parameters. |
| |
| Return values are not defined. |
| """ |
| self.messages = list() |
| total_recordcount = 0 |
|
|
| self.prepare(operation) |
| for params in seq_of_parameters: |
| self.execute(self.command, params) |
| if self.rowcount == -1: |
| total_recordcount = -1 |
| if total_recordcount != -1: |
| total_recordcount += self.rowcount |
| self.rowcount = total_recordcount |
|
|
| def _fetch(self, limit=None): |
| """Fetch rows from the current recordset. |
| |
| limit -- Number of rows to fetch, or None (default) to fetch all rows. |
| """ |
| if self.connection is None or self.rs is None: |
| self._raiseCursorError( |
| api.FetchFailedError, "fetch() on closed connection or empty query set" |
| ) |
| return |
|
|
| if self.rs.State == adc.adStateClosed or self.rs.BOF or self.rs.EOF: |
| return list() |
| if limit: |
| ado_results = self.rs.GetRows(limit) |
| else: |
| ado_results = self.rs.GetRows() |
| if ( |
| self.recordset_format == api.RS_ARRAY |
| ): |
| length = ( |
| len(ado_results) // self.numberOfColumns |
| ) |
| else: |
| length = len(ado_results[0]) |
| fetchObject = api.SQLrows( |
| ado_results, length, self |
| ) |
| return fetchObject |
|
|
| def fetchone(self): |
| """Fetch the next row of a query result set, returning a single sequence, |
| or None when no more data is available. |
| |
| An Error (or subclass) exception is raised if the previous call to executeXXX() |
| did not produce any result set or no call was issued yet. |
| """ |
| self.messages = [] |
| result = self._fetch(1) |
| if result: |
| return result[0] |
| return None |
|
|
| def fetchmany(self, size=None): |
| """Fetch the next set of rows of a query result, returning a list of tuples. An empty sequence is returned when no more rows are available. |
| |
| The number of rows to fetch per call is specified by the parameter. |
| If it is not given, the cursor's arraysize determines the number of rows to be fetched. |
| The method should try to fetch as many rows as indicated by the size parameter. |
| If this is not possible due to the specified number of rows not being available, |
| fewer rows may be returned. |
| |
| An Error (or subclass) exception is raised if the previous call to executeXXX() |
| did not produce any result set or no call was issued yet. |
| |
| Note there are performance considerations involved with the size parameter. |
| For optimal performance, it is usually best to use the arraysize attribute. |
| If the size parameter is used, then it is best for it to retain the same value from |
| one fetchmany() call to the next. |
| """ |
| self.messages = [] |
| if size is None: |
| size = self.arraysize |
| return self._fetch(size) |
|
|
| def fetchall(self): |
| """Fetch all (remaining) rows of a query result, returning them as a sequence of sequences (e.g. a list of tuples). |
| |
| Note that the cursor's arraysize attribute |
| can affect the performance of this operation. |
| An Error (or subclass) exception is raised if the previous call to executeXXX() |
| did not produce any result set or no call was issued yet. |
| """ |
| self.messages = [] |
| return self._fetch() |
|
|
| def nextset(self): |
| """Skip to the next available recordset, discarding any remaining rows from the current recordset. |
| |
| If there are no more sets, the method returns None. Otherwise, it returns a true |
| value and subsequent calls to the fetch methods will return rows from the next result set. |
| |
| An Error (or subclass) exception is raised if the previous call to executeXXX() |
| did not produce any result set or no call was issued yet. |
| """ |
| self.messages = [] |
| if self.connection is None or self.rs is None: |
| self._raiseCursorError( |
| api.OperationalError, |
| ("nextset() on closed connection or empty query set"), |
| ) |
| return None |
|
|
| if api.onIronPython: |
| try: |
| recordset = self.rs.NextRecordset() |
| except TypeError: |
| recordset = None |
| except api.Error as exc: |
| self._raiseCursorError(api.NotSupportedError, exc.args) |
| else: |
| try: |
| rsTuple = self.rs.NextRecordset() |
| except pywintypes.com_error as exc: |
| self._raiseCursorError( |
| api.NotSupportedError, exc.args |
| ) |
| recordset = rsTuple[0] |
| if recordset is None: |
| return None |
| self.build_column_info(recordset) |
| return True |
|
|
| def setinputsizes(self, sizes): |
| pass |
|
|
| def setoutputsize(self, size, column=None): |
| pass |
|
|
| def _last_query(self): |
| try: |
| if self.parameters == None: |
| ret = self.commandText |
| else: |
| ret = "%s,parameters=%s" % (self.commandText, repr(self.parameters)) |
| except: |
| ret = None |
| return ret |
|
|
| query = property(_last_query, None, None, "returns the last query executed") |
|
|
|
|
| if __name__ == "__main__": |
| raise api.ProgrammingError(version + " cannot be run as a main program.") |
|
|