Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
apibase.py795 linesDownload Raw Back to adodbapi
1"""adodbapi.apibase - A python DB API 2.0 (PEP 249) interface to Microsoft ADO
2
3Copyright (C) 2002 Henrik Ekelund, version 2.1 by Vernon Cole
4* http://sourceforge.net/projects/pywin32
5* http://sourceforge.net/projects/adodbapi
6"""
7
8import datetime
9import decimal
10import numbers
11import sys
12import time
13
14# noinspection PyUnresolvedReferences
15from . import ado_consts as adc
16
17verbose = False  # debugging flag
18
19onIronPython = sys.platform == "cli"
20if onIronPython:  # we need type definitions for odd data we may need to convert
21    # noinspection PyUnresolvedReferences
22    from System import DateTime, DBNull
23
24    NullTypes = (type(None), DBNull)
25else:
26    DateTime = type(NotImplemented)  # should never be seen on win32
27    NullTypes = type(None)
28
29# --- define objects to smooth out Python3 <-> Python 2.x differences
30unicodeType = str
31longType = int
32StringTypes = str
33makeByteBuffer = bytes
34memoryViewType = memoryview
35_BaseException = Exception
36
37try:  # jdhardy -- handle bytes under IronPython & Py3
38    bytes
39except NameError:
40    bytes = str  # define it for old Pythons
41
42
43# ------- Error handlers ------
44def standardErrorHandler(connection, cursor, errorclass, errorvalue):
45    err = (errorclass, errorvalue)
46    try:
47        connection.messages.append(err)
48    except:
49        pass
50    if cursor is not None:
51        try:
52            cursor.messages.append(err)
53        except:
54            pass
55    raise errorclass(errorvalue)
56
57
58# Note: _BaseException is defined differently between Python 2.x and 3.x
59class Error(_BaseException):
60    pass  # Exception that is the base class of all other error
61    # exceptions. You can use this to catch all errors with one
62    # single 'except' statement. Warnings are not considered
63    # errors and thus should not use this class as base. It must
64    # be a subclass of the Python StandardError (defined in the
65    # module exceptions).
66
67
68class Warning(_BaseException):
69    pass
70
71
72class InterfaceError(Error):
73    pass
74
75
76class DatabaseError(Error):
77    pass
78
79
80class InternalError(DatabaseError):
81    pass
82
83
84class OperationalError(DatabaseError):
85    pass
86
87
88class ProgrammingError(DatabaseError):
89    pass
90
91
92class IntegrityError(DatabaseError):
93    pass
94
95
96class DataError(DatabaseError):
97    pass
98
99
100class NotSupportedError(DatabaseError):
101    pass
102
103
104class FetchFailedError(OperationalError):
105    """
106    Error is used by RawStoredProcedureQuerySet to determine when a fetch
107    failed due to a connection being closed or there is no record set
108    returned. (Non-standard, added especially for django)
109    """
110
111    pass
112
113
114# # # # # ----- Type Objects and Constructors ----- # # # # #
115# Many databases need to have the input in a particular format for binding to an operation's input parameters.
116# For example, if an input is destined for a DATE column, then it must be bound to the database in a particular
117# string format. Similar problems exist for "Row ID" columns or large binary items (e.g. blobs or RAW columns).
118# This presents problems for Python since the parameters to the executeXXX() method are untyped.
119# When the database module sees a Python string object, it doesn't know if it should be bound as a simple CHAR
120# column, as a raw BINARY item, or as a DATE.
121#
122# To overcome this problem, a module must provide the constructors defined below to create objects that can
123# hold special values. When passed to the cursor methods, the module can then detect the proper type of
124# the input parameter and bind it accordingly.
125
126# A Cursor Object's description attribute returns information about each of the result columns of a query.
127# The type_code must compare equal to one of Type Objects defined below. Type Objects may be equal to more than
128# one type code (e.g. DATETIME could be equal to the type codes for date, time and timestamp columns;
129# see the Implementation Hints below for details).
130
131# SQL NULL values are represented by the Python None singleton on input and output.
132
133# Note: Usage of Unix ticks for database interfacing can cause troubles because of the limited date range they cover.
134
135
136# def Date(year,month,day):
137#     "This function constructs an object holding a date value. "
138#     return dateconverter.date(year,month,day)  #dateconverter.Date(year,month,day)
139#
140# def Time(hour,minute,second):
141#     "This function constructs an object holding a time value. "
142#     return dateconverter.time(hour, minute, second) # dateconverter.Time(hour,minute,second)
143#
144# def Timestamp(year,month,day,hour,minute,second):
145#     "This function constructs an object holding a time stamp value. "
146#     return dateconverter.datetime(year,month,day,hour,minute,second)
147#
148# def DateFromTicks(ticks):
149#     """This function constructs an object holding a date value from the given ticks value
150#     (number of seconds since the epoch; see the documentation of the standard Python time module for details). """
151#     return Date(*time.gmtime(ticks)[:3])
152#
153# def TimeFromTicks(ticks):
154#     """This function constructs an object holding a time value from the given ticks value
155#     (number of seconds since the epoch; see the documentation of the standard Python time module for details). """
156#     return Time(*time.gmtime(ticks)[3:6])
157#
158# def TimestampFromTicks(ticks):
159#     """This function constructs an object holding a time stamp value from the given
160#     ticks value (number of seconds since the epoch;
161#     see the documentation of the standard Python time module for details). """
162#     return Timestamp(*time.gmtime(ticks)[:6])
163#
164# def Binary(aString):
165#     """This function constructs an object capable of holding a binary (long) string value. """
166#     b = makeByteBuffer(aString)
167#     return b
168# -----     Time converters ----------------------------------------------
169class TimeConverter(object):  # this is a generic time converter skeleton
170    def __init__(self):  # the details will be filled in by instances
171        self._ordinal_1899_12_31 = datetime.date(1899, 12, 31).toordinal() - 1
172        # Use cls.types to compare if an input parameter is a datetime
173        self.types = {
174            type(self.Date(2000, 1, 1)),
175            type(self.Time(12, 1, 1)),
176            type(self.Timestamp(2000, 1, 1, 12, 1, 1)),
177            datetime.datetime,
178            datetime.time,
179            datetime.date,
180        }
181
182    def COMDate(self, obj):
183        """Returns a ComDate from a date-time"""
184        try:  # most likely a datetime
185            tt = obj.timetuple()
186
187            try:
188                ms = obj.microsecond
189            except:
190                ms = 0
191            return self.ComDateFromTuple(tt, ms)
192        except:  # might be a tuple
193            try:
194                return self.ComDateFromTuple(obj)
195            except:  # try an mxdate
196                try:
197                    return obj.COMDate()
198                except:
199                    raise ValueError('Cannot convert "%s" to COMdate.' % repr(obj))
200
201    def ComDateFromTuple(self, t, microseconds=0):
202        d = datetime.date(t[0], t[1], t[2])
203        integerPart = d.toordinal() - self._ordinal_1899_12_31
204        ms = (t[3] * 3600 + t[4] * 60 + t[5]) * 1000000 + microseconds
205        fractPart = float(ms) / 86400000000.0
206        return integerPart + fractPart
207
208    def DateObjectFromCOMDate(self, comDate):
209        "Returns an object of the wanted type from a ComDate"
210        raise NotImplementedError  # "Abstract class"
211
212    def Date(self, year, month, day):
213        "This function constructs an object holding a date value."
214        raise NotImplementedError  # "Abstract class"
215
216    def Time(self, hour, minute, second):
217        "This function constructs an object holding a time value."
218        raise NotImplementedError  # "Abstract class"
219
220    def Timestamp(self, year, month, day, hour, minute, second):
221        "This function constructs an object holding a time stamp value."
222        raise NotImplementedError  # "Abstract class"
223        # all purpose date to ISO format converter
224
225    def DateObjectToIsoFormatString(self, obj):
226        "This function should return a string in the format 'YYYY-MM-dd HH:MM:SS:ms' (ms optional)"
227        try:  # most likely, a datetime.datetime
228            s = obj.isoformat(" ")
229        except (TypeError, AttributeError):
230            if isinstance(obj, datetime.date):
231                s = obj.isoformat() + " 00:00:00"  # return exact midnight
232            else:
233                try:  # maybe it has a strftime method, like mx
234                    s = obj.strftime("%Y-%m-%d %H:%M:%S")
235                except AttributeError:
236                    try:  # but may be time.struct_time
237                        s = time.strftime("%Y-%m-%d %H:%M:%S", obj)
238                    except:
239                        raise ValueError('Cannot convert "%s" to isoformat' % repr(obj))
240        return s
241
242
243# -- Optional: if mx extensions are installed you may use mxDateTime ----
244try:
245    import mx.DateTime
246
247    mxDateTime = True
248except:
249    mxDateTime = False
250if mxDateTime:
251
252    class mxDateTimeConverter(TimeConverter):  # used optionally if installed
253        def __init__(self):
254            TimeConverter.__init__(self)
255            self.types.add(type(mx.DateTime))
256
257        def DateObjectFromCOMDate(self, comDate):
258            return mx.DateTime.DateTimeFromCOMDate(comDate)
259
260        def Date(self, year, month, day):
261            return mx.DateTime.Date(year, month, day)
262
263        def Time(self, hour, minute, second):
264            return mx.DateTime.Time(hour, minute, second)
265
266        def Timestamp(self, year, month, day, hour, minute, second):
267            return mx.DateTime.Timestamp(year, month, day, hour, minute, second)
268
269else:
270
271    class mxDateTimeConverter(TimeConverter):
272        pass  # if no mx is installed
273
274
275class pythonDateTimeConverter(TimeConverter):  # standard since Python 2.3
276    def __init__(self):
277        TimeConverter.__init__(self)
278
279    def DateObjectFromCOMDate(self, comDate):
280        if isinstance(comDate, datetime.datetime):
281            odn = comDate.toordinal()
282            tim = comDate.time()
283            new = datetime.datetime.combine(datetime.datetime.fromordinal(odn), tim)
284            return new
285            # return comDate.replace(tzinfo=None) # make non aware
286        elif isinstance(comDate, DateTime):
287            fComDate = comDate.ToOADate()  # ironPython clr Date/Time
288        else:
289            fComDate = float(comDate)  # ComDate is number of days since 1899-12-31
290        integerPart = int(fComDate)
291        floatpart = fComDate - integerPart
292        ##if floatpart == 0.0:
293        ##    return datetime.date.fromordinal(integerPart + self._ordinal_1899_12_31)
294        dte = datetime.datetime.fromordinal(
295            integerPart + self._ordinal_1899_12_31
296        ) + datetime.timedelta(milliseconds=floatpart * 86400000)
297        # millisecondsperday=86400000 # 24*60*60*1000
298        return dte
299
300    def Date(self, year, month, day):
301        return datetime.date(year, month, day)
302
303    def Time(self, hour, minute, second):
304        return datetime.time(hour, minute, second)
305
306    def Timestamp(self, year, month, day, hour, minute, second):
307        return datetime.datetime(year, month, day, hour, minute, second)
308
309
310class pythonTimeConverter(TimeConverter):  # the old, ?nix type date and time
311    def __init__(self):  # caution: this Class gets confised by timezones and DST
312        TimeConverter.__init__(self)
313        self.types.add(time.struct_time)
314
315    def DateObjectFromCOMDate(self, comDate):
316        "Returns ticks since 1970"
317        if isinstance(comDate, datetime.datetime):
318            return comDate.timetuple()
319        elif isinstance(comDate, DateTime):  # ironPython clr date/time
320            fcomDate = comDate.ToOADate()
321        else:
322            fcomDate = float(comDate)
323        secondsperday = 86400  # 24*60*60
324        # ComDate is number of days since 1899-12-31, gmtime epoch is 1970-1-1 = 25569 days
325        t = time.gmtime(secondsperday * (fcomDate - 25569.0))
326        return t  # year,month,day,hour,minute,second,weekday,julianday,daylightsaving=t
327
328    def Date(self, year, month, day):
329        return self.Timestamp(year, month, day, 0, 0, 0)
330
331    def Time(self, hour, minute, second):
332        return time.gmtime((hour * 60 + minute) * 60 + second)
333
334    def Timestamp(self, year, month, day, hour, minute, second):
335        return time.localtime(
336            time.mktime((year, month, day, hour, minute, second, 0, 0, -1))
337        )
338
339
340base_dateconverter = pythonDateTimeConverter()
341
342# ------ DB API required module attributes ---------------------
343threadsafety = 1  # TODO -- find out whether this module is actually BETTER than 1.
344
345apilevel = "2.0"  # String constant stating the supported DB API level.
346
347paramstyle = "qmark"  # the default parameter style
348
349# ------ control for an extension which may become part of DB API 3.0 ---
350accepted_paramstyles = ("qmark", "named", "format", "pyformat", "dynamic")
351
352# ------------------------------------------------------------------------------------------
353# define similar types for generic conversion routines
354adoIntegerTypes = (
355    adc.adInteger,
356    adc.adSmallInt,
357    adc.adTinyInt,
358    adc.adUnsignedInt,
359    adc.adUnsignedSmallInt,
360    adc.adUnsignedTinyInt,
361    adc.adBoolean,
362    adc.adError,
363)  # max 32 bits
364adoRowIdTypes = (adc.adChapter,)  # v2.1 Rose
365adoLongTypes = (adc.adBigInt, adc.adFileTime, adc.adUnsignedBigInt)
366adoExactNumericTypes = (
367    adc.adDecimal,
368    adc.adNumeric,
369    adc.adVarNumeric,
370    adc.adCurrency,
371)  # v2.3 Cole
372adoApproximateNumericTypes = (adc.adDouble, adc.adSingle)  # v2.1 Cole
373adoStringTypes = (
374    adc.adBSTR,
375    adc.adChar,
376    adc.adLongVarChar,
377    adc.adLongVarWChar,
378    adc.adVarChar,
379    adc.adVarWChar,
380    adc.adWChar,
381)
382adoBinaryTypes = (adc.adBinary, adc.adLongVarBinary, adc.adVarBinary)
383adoDateTimeTypes = (adc.adDBTime, adc.adDBTimeStamp, adc.adDate, adc.adDBDate)
384adoRemainingTypes = (
385    adc.adEmpty,
386    adc.adIDispatch,
387    adc.adIUnknown,
388    adc.adPropVariant,
389    adc.adArray,
390    adc.adUserDefined,
391    adc.adVariant,
392    adc.adGUID,
393)
394
395
396# this class is a trick to determine whether a type is a member of a related group of types. see PEP notes
397class DBAPITypeObject(object):
398    def __init__(self, valuesTuple):
399        self.values = frozenset(valuesTuple)
400
401    def __eq__(self, other):
402        return other in self.values
403
404    def __ne__(self, other):
405        return other not in self.values
406
407
408"""This type object is used to describe columns in a database that are string-based (e.g. CHAR). """
409STRING = DBAPITypeObject(adoStringTypes)
410
411"""This type object is used to describe (long) binary columns in a database (e.g. LONG, RAW, BLOBs). """
412BINARY = DBAPITypeObject(adoBinaryTypes)
413
414"""This type object is used to describe numeric columns in a database. """
415NUMBER = DBAPITypeObject(
416    adoIntegerTypes + adoLongTypes + adoExactNumericTypes + adoApproximateNumericTypes
417)
418
419"""This type object is used to describe date/time columns in a database. """
420
421DATETIME = DBAPITypeObject(adoDateTimeTypes)
422"""This type object is used to describe the "Row ID" column in a database. """
423ROWID = DBAPITypeObject(adoRowIdTypes)
424
425OTHER = DBAPITypeObject(adoRemainingTypes)
426
427# ------- utilities for translating python data types to ADO data types ---------------------------------
428typeMap = {
429    memoryViewType: adc.adVarBinary,
430    float: adc.adDouble,
431    type(None): adc.adEmpty,
432    str: adc.adBSTR,
433    bool: adc.adBoolean,  # v2.1 Cole
434    decimal.Decimal: adc.adDecimal,
435    int: adc.adBigInt,
436    bytes: adc.adVarBinary,
437}
438
439
440def pyTypeToADOType(d):
441    tp = type(d)
442    try:
443        return typeMap[tp]
444    except KeyError:  #   The type was not defined in the pre-computed Type table
445        from . import dateconverter
446
447        if (
448            tp in dateconverter.types
449        ):  # maybe it is one of our supported Date/Time types
450            return adc.adDate
451        #  otherwise, attempt to discern the type by probing the data object itself -- to handle duck typing
452        if isinstance(d, StringTypes):
453            return adc.adBSTR
454        if isinstance(d, numbers.Integral):
455            return adc.adBigInt
456        if isinstance(d, numbers.Real):
457            return adc.adDouble
458        raise DataError('cannot convert "%s" (type=%s) to ADO' % (repr(d), tp))
459
460
461# # # # # # # # # # # # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
462# functions to convert database values to Python objects
463# ------------------------------------------------------------------------
464# variant type : function converting variant to Python value
465def variantConvertDate(v):
466    from . import dateconverter  # this function only called when adodbapi is running
467
468    return dateconverter.DateObjectFromCOMDate(v)
469
470
471def cvtString(variant):  # use to get old action of adodbapi v1 if desired
472    if onIronPython:
473        try:
474            return variant.ToString()
475        except:
476            pass
477    return str(variant)
478
479
480def cvtDecimal(variant):  # better name
481    return _convertNumberWithCulture(variant, decimal.Decimal)
482
483
484def cvtNumeric(variant):  # older name - don't break old code
485    return cvtDecimal(variant)
486
487
488def cvtFloat(variant):
489    return _convertNumberWithCulture(variant, float)
490
491
492def _convertNumberWithCulture(variant, f):
493    try:
494        return f(variant)
495    except (ValueError, TypeError, decimal.InvalidOperation):
496        try:
497            europeVsUS = str(variant).replace(",", ".")
498            return f(europeVsUS)
499        except (ValueError, TypeError, decimal.InvalidOperation):
500            pass
501
502
503def cvtInt(variant):
504    return int(variant)
505
506
507def cvtLong(variant):  # only important in old versions where long and int differ
508    return int(variant)
509
510
511def cvtBuffer(variant):
512    return bytes(variant)
513
514
515def cvtUnicode(variant):
516    return str(variant)
517
518
519def identity(x):
520    return x
521
522
523def cvtUnusual(variant):
524    if verbose > 1:
525        sys.stderr.write("Conversion called for Unusual data=%s\n" % repr(variant))
526    if isinstance(variant, DateTime):  # COMdate or System.Date
527        from .adodbapi import (  # this will only be called when adodbapi is in use, and very rarely
528            dateconverter,
529        )
530
531        return dateconverter.DateObjectFromCOMDate(variant)
532    return variant  # cannot find conversion function -- just give the data to the user
533
534
535def convert_to_python(variant, func):  # convert DB value into Python value
536    if isinstance(variant, NullTypes):  # IronPython Null or None
537        return None
538    return func(variant)  # call the appropriate conversion function
539
540
541class MultiMap(dict):  # builds a dictionary from {(sequence,of,keys) : function}
542    """A dictionary of ado.type : function -- but you can set multiple items by passing a sequence of keys"""
543
544    # useful for defining conversion functions for groups of similar data types.
545    def __init__(self, aDict):
546        for k, v in list(aDict.items()):
547            self[k] = v  # we must call __setitem__
548
549    def __setitem__(self, adoType, cvtFn):
550        "set a single item, or a whole sequence of items"
551        try:  # user passed us a sequence, set them individually
552            for type in adoType:
553                dict.__setitem__(self, type, cvtFn)
554        except TypeError:  # a single value fails attempt to iterate
555            dict.__setitem__(self, adoType, cvtFn)
556
557
558# initialize variantConversions dictionary used to convert SQL to Python
559# this is the dictionary of default conversion functions, built by the class above.
560# this becomes a class attribute for the Connection, and that attribute is used
561# to build the list of column conversion functions for the Cursor
562variantConversions = MultiMap(
563    {
564        adoDateTimeTypes: variantConvertDate,
565        adoApproximateNumericTypes: cvtFloat,
566        adoExactNumericTypes: cvtDecimal,  # use to force decimal rather than unicode
567        adoLongTypes: cvtLong,
568        adoIntegerTypes: cvtInt,
569        adoRowIdTypes: cvtInt,
570        adoStringTypes: identity,
571        adoBinaryTypes: cvtBuffer,
572        adoRemainingTypes: cvtUnusual,
573    }
574)
575
576# # # # # classes to emulate the result of cursor.fetchxxx() as a sequence of sequences # # # # #
577# "an ENUM of how my low level records are laid out"
578RS_WIN_32, RS_ARRAY, RS_REMOTE = list(range(1, 4))
579
580
581class SQLrow(object):  # a single database row
582    # class to emulate a sequence, so that a column may be retrieved by either number or name
583    def __init__(self, rows, index):  # "rows" is an _SQLrows object, index is which row
584        self.rows = rows  # parent 'fetch' container object
585        self.index = index  # my row number within parent
586
587    def __getattr__(self, name):  # used for row.columnName type of value access
588        try:
589            return self._getValue(self.rows.columnNames[name.lower()])
590        except KeyError:
591            raise AttributeError('Unknown column name "{}"'.format(name))
592
593    def _getValue(self, key):  # key must be an integer
594        if (
595            self.rows.recordset_format == RS_ARRAY
596        ):  # retrieve from two-dimensional array
597            v = self.rows.ado_results[key, self.index]
598        elif self.rows.recordset_format == RS_REMOTE:
599            v = self.rows.ado_results[self.index][key]
600        else:  # pywin32 - retrieve from tuple of tuples
601            v = self.rows.ado_results[key][self.index]
602        if self.rows.converters is NotImplemented:
603            return v
604        return convert_to_python(v, self.rows.converters[key])
605
606    def __len__(self):
607        return self.rows.numberOfColumns
608
609    def __getitem__(self, key):  # used for row[key] type of value access
610        if isinstance(key, int):  # normal row[1] designation
611            try:
612                return self._getValue(key)
613            except IndexError:
614                raise
615        if isinstance(key, slice):
616            indices = key.indices(self.rows.numberOfColumns)
617            vl = [self._getValue(i) for i in range(*indices)]
618            return tuple(vl)
619        try:
620            return self._getValue(
621                self.rows.columnNames[key.lower()]
622            )  # extension row[columnName] designation
623        except (KeyError, TypeError):
624            er, st, tr = sys.exc_info()
625            raise er(
626                'No such key as "%s" in %s' % (repr(key), self.__repr__())
627            ).with_traceback(tr)
628
629    def __iter__(self):
630        return iter(self.__next__())
631
632    def __next__(self):
633        for n in range(self.rows.numberOfColumns):
634            yield self._getValue(n)
635
636    def __repr__(self):  # create a human readable representation
637        taglist = sorted(list(self.rows.columnNames.items()), key=lambda x: x[1])
638        s = "<SQLrow={"
639        for name, i in taglist:
640            s += name + ":" + repr(self._getValue(i)) + ", "
641        return s[:-2] + "}>"
642
643    def __str__(self):  # create a pretty human readable representation
644        return str(
645            tuple(str(self._getValue(i)) for i in range(self.rows.numberOfColumns))
646        )
647
648    # TO-DO implement pickling an SQLrow directly
649    # def __getstate__(self): return self.__dict__
650    # def __setstate__(self, d): self.__dict__.update(d)
651    # which basically tell pickle to treat your class just like a normal one,
652    # taking self.__dict__ as representing the whole of the instance state,
653    #  despite the existence of the __getattr__.
654    # # # #
655
656
657class SQLrows(object):
658    # class to emulate a sequence for multiple rows using a container object
659    def __init__(self, ado_results, numberOfRows, cursor):
660        self.ado_results = ado_results  # raw result of SQL get
661        try:
662            self.recordset_format = cursor.recordset_format
663            self.numberOfColumns = cursor.numberOfColumns
664            self.converters = cursor.converters
665            self.columnNames = cursor.columnNames
666        except AttributeError:
667            self.recordset_format = RS_ARRAY
668            self.numberOfColumns = 0
669            self.converters = []
670            self.columnNames = {}
671        self.numberOfRows = numberOfRows
672
673    def __len__(self):
674        return self.numberOfRows
675
676    def __getitem__(self, item):  # used for row or row,column access
677        if not self.ado_results:
678            return []
679        if isinstance(item, slice):  # will return a list of row objects
680            indices = item.indices(self.numberOfRows)
681            return [SQLrow(self, k) for k in range(*indices)]
682        elif isinstance(item, tuple) and len(item) == 2:
683            # d = some_rowsObject[i,j] will return a datum from a two-dimension address
684            i, j = item
685            if not isinstance(j, int):
686                try:
687                    j = self.columnNames[j.lower()]  # convert named column to numeric
688                except KeyError:
689                    raise KeyError('adodbapi: no such column name as "%s"' % repr(j))
690            if self.recordset_format == RS_ARRAY:  # retrieve from two-dimensional array
691                v = self.ado_results[j, i]
692            elif self.recordset_format == RS_REMOTE:
693                v = self.ado_results[i][j]
694            else:  # pywin32 - retrieve from tuple of tuples
695                v = self.ado_results[j][i]
696            if self.converters is NotImplemented:
697                return v
698            return convert_to_python(v, self.converters[j])
699        else:
700            row = SQLrow(self, item)  # new row descriptor
701            return row
702
703    def __iter__(self):
704        return iter(self.__next__())
705
706    def __next__(self):
707        for n in range(self.numberOfRows):
708            row = SQLrow(self, n)
709            yield row
710            # # # # #
711
712    # # # # # functions to re-format SQL requests to other paramstyle requirements # # # # # # # # # #
713
714
715def changeNamedToQmark(
716    op,
717):  # convert from 'named' paramstyle to ADO required '?'mark parameters
718    outOp = ""
719    outparms = []
720    chunks = op.split(
721        "'"
722    )  # quote all literals -- odd numbered list results are literals.
723    inQuotes = False
724    for chunk in chunks:
725        if inQuotes:  # this is inside a quote
726            if chunk == "":  # double apostrophe to quote one apostrophe
727                outOp = outOp[:-1]  # so take one away
728            else:
729                outOp += "'" + chunk + "'"  # else pass the quoted string as is.
730        else:  # is SQL code -- look for a :namedParameter
731            while chunk:  # some SQL string remains
732                sp = chunk.split(":", 1)
733                outOp += sp[0]  # concat the part up to the :
734                s = ""
735                try:
736                    chunk = sp[1]
737                except IndexError:
738                    chunk = None
739                if chunk:  # there was a parameter - parse it out
740                    i = 0
741                    c = chunk[0]
742                    while c.isalnum() or c == "_":
743                        i += 1
744                        try:
745                            c = chunk[i]
746                        except IndexError:
747                            break
748                    s = chunk[:i]
749                    chunk = chunk[i:]
750                if s:
751                    outparms.append(s)  # list the parameters in order
752                    outOp += "?"  # put in the Qmark
753        inQuotes = not inQuotes
754    return outOp, outparms
755
756
757def changeFormatToQmark(
758    op,
759):  # convert from 'format' paramstyle to ADO required '?'mark parameters
760    outOp = ""
761    outparams = []
762    chunks = op.split(
763        "'"
764    )  # quote all literals -- odd numbered list results are literals.
765    inQuotes = False
766    for chunk in chunks:
767        if inQuotes:
768            if (
769                outOp != "" and chunk == ""
770            ):  # he used a double apostrophe to quote one apostrophe
771                outOp = outOp[:-1]  # so take one away
772            else:
773                outOp += "'" + chunk + "'"  # else pass the quoted string as is.
774        else:  # is SQL code -- look for a %s parameter
775            if "%(" in chunk:  # ugh! pyformat!
776                while chunk:  # some SQL string remains
777                    sp = chunk.split("%(", 1)
778                    outOp += sp[0]  # concat the part up to the %
779                    if len(sp) > 1:
780                        try:
781                            s, chunk = sp[1].split(")s", 1)  # find the ')s'
782                        except ValueError:
783                            raise ProgrammingError(
784                                'Pyformat SQL has incorrect format near "%s"' % chunk
785                            )
786                        outparams.append(s)
787                        outOp += "?"  # put in the Qmark
788                    else:
789                        chunk = None
790            else:  # proper '%s' format
791                sp = chunk.split("%s")  # make each %s
792                outOp += "?".join(sp)  # into ?
793        inQuotes = not inQuotes  # every other chunk is a quoted string
794    return outOp, outparams
795 
codekingpro/portable-devtools · Team Ai