|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794 |
- """adodbapi.apibase - 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
- """
-
- import datetime
- import decimal
- import numbers
- import sys
- import time
-
- # noinspection PyUnresolvedReferences
- from . import ado_consts as adc
-
- verbose = False # debugging flag
-
- onIronPython = sys.platform == "cli"
- if onIronPython: # we need type definitions for odd data we may need to convert
- # noinspection PyUnresolvedReferences
- from System import DateTime, DBNull
-
- NullTypes = (type(None), DBNull)
- else:
- DateTime = type(NotImplemented) # should never be seen on win32
- NullTypes = type(None)
-
- # --- define objects to smooth out Python3 <-> Python 2.x differences
- unicodeType = str
- longType = int
- StringTypes = str
- makeByteBuffer = bytes
- memoryViewType = memoryview
- _BaseException = Exception
-
- try: # jdhardy -- handle bytes under IronPython & Py3
- bytes
- except NameError:
- bytes = str # define it for old Pythons
-
-
- # ------- Error handlers ------
- def standardErrorHandler(connection, cursor, errorclass, errorvalue):
- err = (errorclass, errorvalue)
- try:
- connection.messages.append(err)
- except:
- pass
- if cursor is not None:
- try:
- cursor.messages.append(err)
- except:
- pass
- raise errorclass(errorvalue)
-
-
- # Note: _BaseException is defined differently between Python 2.x and 3.x
- class Error(_BaseException):
- pass # Exception that is the base class of all other error
- # exceptions. You can use this to catch all errors with one
- # single 'except' statement. Warnings are not considered
- # errors and thus should not use this class as base. It must
- # be a subclass of the Python StandardError (defined in the
- # module exceptions).
-
-
- class Warning(_BaseException):
- pass
-
-
- class InterfaceError(Error):
- pass
-
-
- class DatabaseError(Error):
- pass
-
-
- class InternalError(DatabaseError):
- pass
-
-
- class OperationalError(DatabaseError):
- pass
-
-
- class ProgrammingError(DatabaseError):
- pass
-
-
- class IntegrityError(DatabaseError):
- pass
-
-
- class DataError(DatabaseError):
- pass
-
-
- class NotSupportedError(DatabaseError):
- pass
-
-
- class FetchFailedError(OperationalError):
- """
- Error is used by RawStoredProcedureQuerySet to determine when a fetch
- failed due to a connection being closed or there is no record set
- returned. (Non-standard, added especially for django)
- """
-
- pass
-
-
- # # # # # ----- Type Objects and Constructors ----- # # # # #
- # Many databases need to have the input in a particular format for binding to an operation's input parameters.
- # For example, if an input is destined for a DATE column, then it must be bound to the database in a particular
- # string format. Similar problems exist for "Row ID" columns or large binary items (e.g. blobs or RAW columns).
- # This presents problems for Python since the parameters to the executeXXX() method are untyped.
- # When the database module sees a Python string object, it doesn't know if it should be bound as a simple CHAR
- # column, as a raw BINARY item, or as a DATE.
- #
- # To overcome this problem, a module must provide the constructors defined below to create objects that can
- # hold special values. When passed to the cursor methods, the module can then detect the proper type of
- # the input parameter and bind it accordingly.
-
- # A Cursor Object's description attribute returns information about each of the result columns of a query.
- # The type_code must compare equal to one of Type Objects defined below. Type Objects may be equal to more than
- # one type code (e.g. DATETIME could be equal to the type codes for date, time and timestamp columns;
- # see the Implementation Hints below for details).
-
- # SQL NULL values are represented by the Python None singleton on input and output.
-
- # Note: Usage of Unix ticks for database interfacing can cause troubles because of the limited date range they cover.
-
-
- # def Date(year,month,day):
- # "This function constructs an object holding a date value. "
- # return dateconverter.date(year,month,day) #dateconverter.Date(year,month,day)
- #
- # def Time(hour,minute,second):
- # "This function constructs an object holding a time value. "
- # return dateconverter.time(hour, minute, second) # dateconverter.Time(hour,minute,second)
- #
- # def Timestamp(year,month,day,hour,minute,second):
- # "This function constructs an object holding a time stamp value. "
- # return dateconverter.datetime(year,month,day,hour,minute,second)
- #
- # def DateFromTicks(ticks):
- # """This function constructs an object holding a date value from the given ticks value
- # (number of seconds since the epoch; see the documentation of the standard Python time module for details). """
- # return Date(*time.gmtime(ticks)[:3])
- #
- # def TimeFromTicks(ticks):
- # """This function constructs an object holding a time value from the given ticks value
- # (number of seconds since the epoch; see the documentation of the standard Python time module for details). """
- # return Time(*time.gmtime(ticks)[3:6])
- #
- # def TimestampFromTicks(ticks):
- # """This function constructs an object holding a time stamp value from the given
- # ticks value (number of seconds since the epoch;
- # see the documentation of the standard Python time module for details). """
- # return Timestamp(*time.gmtime(ticks)[:6])
- #
- # def Binary(aString):
- # """This function constructs an object capable of holding a binary (long) string value. """
- # b = makeByteBuffer(aString)
- # return b
- # ----- Time converters ----------------------------------------------
- class TimeConverter(object): # this is a generic time converter skeleton
- def __init__(self): # the details will be filled in by instances
- self._ordinal_1899_12_31 = datetime.date(1899, 12, 31).toordinal() - 1
- # Use cls.types to compare if an input parameter is a datetime
- self.types = {
- type(self.Date(2000, 1, 1)),
- type(self.Time(12, 1, 1)),
- type(self.Timestamp(2000, 1, 1, 12, 1, 1)),
- datetime.datetime,
- datetime.time,
- datetime.date,
- }
-
- def COMDate(self, obj):
- """Returns a ComDate from a date-time"""
- try: # most likely a datetime
- tt = obj.timetuple()
-
- try:
- ms = obj.microsecond
- except:
- ms = 0
- return self.ComDateFromTuple(tt, ms)
- except: # might be a tuple
- try:
- return self.ComDateFromTuple(obj)
- except: # try an mxdate
- try:
- return obj.COMDate()
- except:
- raise ValueError('Cannot convert "%s" to COMdate.' % repr(obj))
-
- def ComDateFromTuple(self, t, microseconds=0):
- d = datetime.date(t[0], t[1], t[2])
- integerPart = d.toordinal() - self._ordinal_1899_12_31
- ms = (t[3] * 3600 + t[4] * 60 + t[5]) * 1000000 + microseconds
- fractPart = float(ms) / 86400000000.0
- return integerPart + fractPart
-
- def DateObjectFromCOMDate(self, comDate):
- "Returns an object of the wanted type from a ComDate"
- raise NotImplementedError # "Abstract class"
-
- def Date(self, year, month, day):
- "This function constructs an object holding a date value."
- raise NotImplementedError # "Abstract class"
-
- def Time(self, hour, minute, second):
- "This function constructs an object holding a time value."
- raise NotImplementedError # "Abstract class"
-
- def Timestamp(self, year, month, day, hour, minute, second):
- "This function constructs an object holding a time stamp value."
- raise NotImplementedError # "Abstract class"
- # all purpose date to ISO format converter
-
- def DateObjectToIsoFormatString(self, obj):
- "This function should return a string in the format 'YYYY-MM-dd HH:MM:SS:ms' (ms optional)"
- try: # most likely, a datetime.datetime
- s = obj.isoformat(" ")
- except (TypeError, AttributeError):
- if isinstance(obj, datetime.date):
- s = obj.isoformat() + " 00:00:00" # return exact midnight
- else:
- try: # maybe it has a strftime method, like mx
- s = obj.strftime("%Y-%m-%d %H:%M:%S")
- except AttributeError:
- try: # but may be time.struct_time
- s = time.strftime("%Y-%m-%d %H:%M:%S", obj)
- except:
- raise ValueError('Cannot convert "%s" to isoformat' % repr(obj))
- return s
-
-
- # -- Optional: if mx extensions are installed you may use mxDateTime ----
- try:
- import mx.DateTime
-
- mxDateTime = True
- except:
- mxDateTime = False
- if mxDateTime:
-
- class mxDateTimeConverter(TimeConverter): # used optionally if installed
- def __init__(self):
- TimeConverter.__init__(self)
- self.types.add(type(mx.DateTime))
-
- def DateObjectFromCOMDate(self, comDate):
- return mx.DateTime.DateTimeFromCOMDate(comDate)
-
- def Date(self, year, month, day):
- return mx.DateTime.Date(year, month, day)
-
- def Time(self, hour, minute, second):
- return mx.DateTime.Time(hour, minute, second)
-
- def Timestamp(self, year, month, day, hour, minute, second):
- return mx.DateTime.Timestamp(year, month, day, hour, minute, second)
-
- else:
-
- class mxDateTimeConverter(TimeConverter):
- pass # if no mx is installed
-
-
- class pythonDateTimeConverter(TimeConverter): # standard since Python 2.3
- def __init__(self):
- TimeConverter.__init__(self)
-
- def DateObjectFromCOMDate(self, comDate):
- if isinstance(comDate, datetime.datetime):
- odn = comDate.toordinal()
- tim = comDate.time()
- new = datetime.datetime.combine(datetime.datetime.fromordinal(odn), tim)
- return new
- # return comDate.replace(tzinfo=None) # make non aware
- elif isinstance(comDate, DateTime):
- fComDate = comDate.ToOADate() # ironPython clr Date/Time
- else:
- fComDate = float(comDate) # ComDate is number of days since 1899-12-31
- integerPart = int(fComDate)
- floatpart = fComDate - integerPart
- ##if floatpart == 0.0:
- ## return datetime.date.fromordinal(integerPart + self._ordinal_1899_12_31)
- dte = datetime.datetime.fromordinal(
- integerPart + self._ordinal_1899_12_31
- ) + datetime.timedelta(milliseconds=floatpart * 86400000)
- # millisecondsperday=86400000 # 24*60*60*1000
- return dte
-
- def Date(self, year, month, day):
- return datetime.date(year, month, day)
-
- def Time(self, hour, minute, second):
- return datetime.time(hour, minute, second)
-
- def Timestamp(self, year, month, day, hour, minute, second):
- return datetime.datetime(year, month, day, hour, minute, second)
-
-
- class pythonTimeConverter(TimeConverter): # the old, ?nix type date and time
- def __init__(self): # caution: this Class gets confised by timezones and DST
- TimeConverter.__init__(self)
- self.types.add(time.struct_time)
-
- def DateObjectFromCOMDate(self, comDate):
- "Returns ticks since 1970"
- if isinstance(comDate, datetime.datetime):
- return comDate.timetuple()
- elif isinstance(comDate, DateTime): # ironPython clr date/time
- fcomDate = comDate.ToOADate()
- else:
- fcomDate = float(comDate)
- secondsperday = 86400 # 24*60*60
- # ComDate is number of days since 1899-12-31, gmtime epoch is 1970-1-1 = 25569 days
- t = time.gmtime(secondsperday * (fcomDate - 25569.0))
- return t # year,month,day,hour,minute,second,weekday,julianday,daylightsaving=t
-
- def Date(self, year, month, day):
- return self.Timestamp(year, month, day, 0, 0, 0)
-
- def Time(self, hour, minute, second):
- return time.gmtime((hour * 60 + minute) * 60 + second)
-
- def Timestamp(self, year, month, day, hour, minute, second):
- return time.localtime(
- time.mktime((year, month, day, hour, minute, second, 0, 0, -1))
- )
-
-
- base_dateconverter = pythonDateTimeConverter()
-
- # ------ DB API required module attributes ---------------------
- threadsafety = 1 # TODO -- find out whether this module is actually BETTER than 1.
-
- apilevel = "2.0" # String constant stating the supported DB API level.
-
- paramstyle = "qmark" # the default parameter style
-
- # ------ control for an extension which may become part of DB API 3.0 ---
- accepted_paramstyles = ("qmark", "named", "format", "pyformat", "dynamic")
-
- # ------------------------------------------------------------------------------------------
- # define similar types for generic conversion routines
- adoIntegerTypes = (
- adc.adInteger,
- adc.adSmallInt,
- adc.adTinyInt,
- adc.adUnsignedInt,
- adc.adUnsignedSmallInt,
- adc.adUnsignedTinyInt,
- adc.adBoolean,
- adc.adError,
- ) # max 32 bits
- adoRowIdTypes = (adc.adChapter,) # v2.1 Rose
- adoLongTypes = (adc.adBigInt, adc.adFileTime, adc.adUnsignedBigInt)
- adoExactNumericTypes = (
- adc.adDecimal,
- adc.adNumeric,
- adc.adVarNumeric,
- adc.adCurrency,
- ) # v2.3 Cole
- adoApproximateNumericTypes = (adc.adDouble, adc.adSingle) # v2.1 Cole
- adoStringTypes = (
- adc.adBSTR,
- adc.adChar,
- adc.adLongVarChar,
- adc.adLongVarWChar,
- adc.adVarChar,
- adc.adVarWChar,
- adc.adWChar,
- )
- adoBinaryTypes = (adc.adBinary, adc.adLongVarBinary, adc.adVarBinary)
- adoDateTimeTypes = (adc.adDBTime, adc.adDBTimeStamp, adc.adDate, adc.adDBDate)
- adoRemainingTypes = (
- adc.adEmpty,
- adc.adIDispatch,
- adc.adIUnknown,
- adc.adPropVariant,
- adc.adArray,
- adc.adUserDefined,
- adc.adVariant,
- adc.adGUID,
- )
-
-
- # this class is a trick to determine whether a type is a member of a related group of types. see PEP notes
- class DBAPITypeObject(object):
- def __init__(self, valuesTuple):
- self.values = frozenset(valuesTuple)
-
- def __eq__(self, other):
- return other in self.values
-
- def __ne__(self, other):
- return other not in self.values
-
-
- """This type object is used to describe columns in a database that are string-based (e.g. CHAR). """
- STRING = DBAPITypeObject(adoStringTypes)
-
- """This type object is used to describe (long) binary columns in a database (e.g. LONG, RAW, BLOBs). """
- BINARY = DBAPITypeObject(adoBinaryTypes)
-
- """This type object is used to describe numeric columns in a database. """
- NUMBER = DBAPITypeObject(
- adoIntegerTypes + adoLongTypes + adoExactNumericTypes + adoApproximateNumericTypes
- )
-
- """This type object is used to describe date/time columns in a database. """
-
- DATETIME = DBAPITypeObject(adoDateTimeTypes)
- """This type object is used to describe the "Row ID" column in a database. """
- ROWID = DBAPITypeObject(adoRowIdTypes)
-
- OTHER = DBAPITypeObject(adoRemainingTypes)
-
- # ------- utilities for translating python data types to ADO data types ---------------------------------
- typeMap = {
- memoryViewType: adc.adVarBinary,
- float: adc.adDouble,
- type(None): adc.adEmpty,
- str: adc.adBSTR,
- bool: adc.adBoolean, # v2.1 Cole
- decimal.Decimal: adc.adDecimal,
- int: adc.adBigInt,
- bytes: adc.adVarBinary,
- }
-
-
- def pyTypeToADOType(d):
- tp = type(d)
- try:
- return typeMap[tp]
- except KeyError: # The type was not defined in the pre-computed Type table
- from . import dateconverter
-
- if (
- tp in dateconverter.types
- ): # maybe it is one of our supported Date/Time types
- return adc.adDate
- # otherwise, attempt to discern the type by probing the data object itself -- to handle duck typing
- if isinstance(d, StringTypes):
- return adc.adBSTR
- if isinstance(d, numbers.Integral):
- return adc.adBigInt
- if isinstance(d, numbers.Real):
- return adc.adDouble
- raise DataError('cannot convert "%s" (type=%s) to ADO' % (repr(d), tp))
-
-
- # # # # # # # # # # # # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- # functions to convert database values to Python objects
- # ------------------------------------------------------------------------
- # variant type : function converting variant to Python value
- def variantConvertDate(v):
- from . import dateconverter # this function only called when adodbapi is running
-
- return dateconverter.DateObjectFromCOMDate(v)
-
-
- def cvtString(variant): # use to get old action of adodbapi v1 if desired
- if onIronPython:
- try:
- return variant.ToString()
- except:
- pass
- return str(variant)
-
-
- def cvtDecimal(variant): # better name
- return _convertNumberWithCulture(variant, decimal.Decimal)
-
-
- def cvtNumeric(variant): # older name - don't break old code
- return cvtDecimal(variant)
-
-
- def cvtFloat(variant):
- return _convertNumberWithCulture(variant, float)
-
-
- def _convertNumberWithCulture(variant, f):
- try:
- return f(variant)
- except (ValueError, TypeError, decimal.InvalidOperation):
- try:
- europeVsUS = str(variant).replace(",", ".")
- return f(europeVsUS)
- except (ValueError, TypeError, decimal.InvalidOperation):
- pass
-
-
- def cvtInt(variant):
- return int(variant)
-
-
- def cvtLong(variant): # only important in old versions where long and int differ
- return int(variant)
-
-
- def cvtBuffer(variant):
- return bytes(variant)
-
-
- def cvtUnicode(variant):
- return str(variant)
-
-
- def identity(x):
- return x
-
-
- def cvtUnusual(variant):
- if verbose > 1:
- sys.stderr.write("Conversion called for Unusual data=%s\n" % repr(variant))
- if isinstance(variant, DateTime): # COMdate or System.Date
- from .adodbapi import ( # this will only be called when adodbapi is in use, and very rarely
- dateconverter,
- )
-
- return dateconverter.DateObjectFromCOMDate(variant)
- return variant # cannot find conversion function -- just give the data to the user
-
-
- def convert_to_python(variant, func): # convert DB value into Python value
- if isinstance(variant, NullTypes): # IronPython Null or None
- return None
- return func(variant) # call the appropriate conversion function
-
-
- class MultiMap(dict): # builds a dictionary from {(sequence,of,keys) : function}
- """A dictionary of ado.type : function -- but you can set multiple items by passing a sequence of keys"""
-
- # useful for defining conversion functions for groups of similar data types.
- def __init__(self, aDict):
- for k, v in list(aDict.items()):
- self[k] = v # we must call __setitem__
-
- def __setitem__(self, adoType, cvtFn):
- "set a single item, or a whole sequence of items"
- try: # user passed us a sequence, set them individually
- for type in adoType:
- dict.__setitem__(self, type, cvtFn)
- except TypeError: # a single value fails attempt to iterate
- dict.__setitem__(self, adoType, cvtFn)
-
-
- # initialize variantConversions dictionary used to convert SQL to Python
- # this is the dictionary of default conversion functions, built by the class above.
- # this becomes a class attribute for the Connection, and that attribute is used
- # to build the list of column conversion functions for the Cursor
- variantConversions = MultiMap(
- {
- adoDateTimeTypes: variantConvertDate,
- adoApproximateNumericTypes: cvtFloat,
- adoExactNumericTypes: cvtDecimal, # use to force decimal rather than unicode
- adoLongTypes: cvtLong,
- adoIntegerTypes: cvtInt,
- adoRowIdTypes: cvtInt,
- adoStringTypes: identity,
- adoBinaryTypes: cvtBuffer,
- adoRemainingTypes: cvtUnusual,
- }
- )
-
- # # # # # classes to emulate the result of cursor.fetchxxx() as a sequence of sequences # # # # #
- # "an ENUM of how my low level records are laid out"
- RS_WIN_32, RS_ARRAY, RS_REMOTE = list(range(1, 4))
-
-
- class SQLrow(object): # a single database row
- # class to emulate a sequence, so that a column may be retrieved by either number or name
- def __init__(self, rows, index): # "rows" is an _SQLrows object, index is which row
- self.rows = rows # parent 'fetch' container object
- self.index = index # my row number within parent
-
- def __getattr__(self, name): # used for row.columnName type of value access
- try:
- return self._getValue(self.rows.columnNames[name.lower()])
- except KeyError:
- raise AttributeError('Unknown column name "{}"'.format(name))
-
- def _getValue(self, key): # key must be an integer
- if (
- self.rows.recordset_format == RS_ARRAY
- ): # retrieve from two-dimensional array
- v = self.rows.ado_results[key, self.index]
- elif self.rows.recordset_format == RS_REMOTE:
- v = self.rows.ado_results[self.index][key]
- else: # pywin32 - retrieve from tuple of tuples
- v = self.rows.ado_results[key][self.index]
- if self.rows.converters is NotImplemented:
- return v
- return convert_to_python(v, self.rows.converters[key])
-
- def __len__(self):
- return self.rows.numberOfColumns
-
- def __getitem__(self, key): # used for row[key] type of value access
- if isinstance(key, int): # normal row[1] designation
- try:
- return self._getValue(key)
- except IndexError:
- raise
- if isinstance(key, slice):
- indices = key.indices(self.rows.numberOfColumns)
- vl = [self._getValue(i) for i in range(*indices)]
- return tuple(vl)
- try:
- return self._getValue(
- self.rows.columnNames[key.lower()]
- ) # extension row[columnName] designation
- except (KeyError, TypeError):
- er, st, tr = sys.exc_info()
- raise er(
- 'No such key as "%s" in %s' % (repr(key), self.__repr__())
- ).with_traceback(tr)
-
- def __iter__(self):
- return iter(self.__next__())
-
- def __next__(self):
- for n in range(self.rows.numberOfColumns):
- yield self._getValue(n)
-
- def __repr__(self): # create a human readable representation
- taglist = sorted(list(self.rows.columnNames.items()), key=lambda x: x[1])
- s = "<SQLrow={"
- for name, i in taglist:
- s += name + ":" + repr(self._getValue(i)) + ", "
- return s[:-2] + "}>"
-
- def __str__(self): # create a pretty human readable representation
- return str(
- tuple(str(self._getValue(i)) for i in range(self.rows.numberOfColumns))
- )
-
- # TO-DO implement pickling an SQLrow directly
- # def __getstate__(self): return self.__dict__
- # def __setstate__(self, d): self.__dict__.update(d)
- # which basically tell pickle to treat your class just like a normal one,
- # taking self.__dict__ as representing the whole of the instance state,
- # despite the existence of the __getattr__.
- # # # #
-
-
- class SQLrows(object):
- # class to emulate a sequence for multiple rows using a container object
- def __init__(self, ado_results, numberOfRows, cursor):
- self.ado_results = ado_results # raw result of SQL get
- try:
- self.recordset_format = cursor.recordset_format
- self.numberOfColumns = cursor.numberOfColumns
- self.converters = cursor.converters
- self.columnNames = cursor.columnNames
- except AttributeError:
- self.recordset_format = RS_ARRAY
- self.numberOfColumns = 0
- self.converters = []
- self.columnNames = {}
- self.numberOfRows = numberOfRows
-
- def __len__(self):
- return self.numberOfRows
-
- def __getitem__(self, item): # used for row or row,column access
- if not self.ado_results:
- return []
- if isinstance(item, slice): # will return a list of row objects
- indices = item.indices(self.numberOfRows)
- return [SQLrow(self, k) for k in range(*indices)]
- elif isinstance(item, tuple) and len(item) == 2:
- # d = some_rowsObject[i,j] will return a datum from a two-dimension address
- i, j = item
- if not isinstance(j, int):
- try:
- j = self.columnNames[j.lower()] # convert named column to numeric
- except KeyError:
- raise KeyError('adodbapi: no such column name as "%s"' % repr(j))
- if self.recordset_format == RS_ARRAY: # retrieve from two-dimensional array
- v = self.ado_results[j, i]
- elif self.recordset_format == RS_REMOTE:
- v = self.ado_results[i][j]
- else: # pywin32 - retrieve from tuple of tuples
- v = self.ado_results[j][i]
- if self.converters is NotImplemented:
- return v
- return convert_to_python(v, self.converters[j])
- else:
- row = SQLrow(self, item) # new row descriptor
- return row
-
- def __iter__(self):
- return iter(self.__next__())
-
- def __next__(self):
- for n in range(self.numberOfRows):
- row = SQLrow(self, n)
- yield row
- # # # # #
-
- # # # # # functions to re-format SQL requests to other paramstyle requirements # # # # # # # # # #
-
-
- def changeNamedToQmark(
- op,
- ): # convert from 'named' paramstyle to ADO required '?'mark parameters
- outOp = ""
- outparms = []
- chunks = op.split(
- "'"
- ) # quote all literals -- odd numbered list results are literals.
- inQuotes = False
- for chunk in chunks:
- if inQuotes: # this is inside a quote
- if chunk == "": # double apostrophe to quote one apostrophe
- outOp = outOp[:-1] # so take one away
- else:
- outOp += "'" + chunk + "'" # else pass the quoted string as is.
- else: # is SQL code -- look for a :namedParameter
- while chunk: # some SQL string remains
- sp = chunk.split(":", 1)
- outOp += sp[0] # concat the part up to the :
- s = ""
- try:
- chunk = sp[1]
- except IndexError:
- chunk = None
- if chunk: # there was a parameter - parse it out
- i = 0
- c = chunk[0]
- while c.isalnum() or c == "_":
- i += 1
- try:
- c = chunk[i]
- except IndexError:
- break
- s = chunk[:i]
- chunk = chunk[i:]
- if s:
- outparms.append(s) # list the parameters in order
- outOp += "?" # put in the Qmark
- inQuotes = not inQuotes
- return outOp, outparms
-
-
- def changeFormatToQmark(
- op,
- ): # convert from 'format' paramstyle to ADO required '?'mark parameters
- outOp = ""
- outparams = []
- chunks = op.split(
- "'"
- ) # quote all literals -- odd numbered list results are literals.
- inQuotes = False
- for chunk in chunks:
- if inQuotes:
- if (
- outOp != "" and chunk == ""
- ): # he used a double apostrophe to quote one apostrophe
- outOp = outOp[:-1] # so take one away
- else:
- outOp += "'" + chunk + "'" # else pass the quoted string as is.
- else: # is SQL code -- look for a %s parameter
- if "%(" in chunk: # ugh! pyformat!
- while chunk: # some SQL string remains
- sp = chunk.split("%(", 1)
- outOp += sp[0] # concat the part up to the %
- if len(sp) > 1:
- try:
- s, chunk = sp[1].split(")s", 1) # find the ')s'
- except ValueError:
- raise ProgrammingError(
- 'Pyformat SQL has incorrect format near "%s"' % chunk
- )
- outparams.append(s)
- outOp += "?" # put in the Qmark
- else:
- chunk = None
- else: # proper '%s' format
- sp = chunk.split("%s") # make each %s
- outOp += "?".join(sp) # into ?
- inQuotes = not inQuotes # every other chunk is a quoted string
- return outOp, outparams
|