Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

Changeset 55

Show
Ignore:
Timestamp:
04/02/07 18:07:48
Author:
fumanchu
Message:

sqlserver and msaccess now work.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/geniusql/adapters.py

    r54 r55  
    143143     
    144144    def database_type(self, pytype, hints=None): 
    145         """Recommend a DatabaseType for the given Python type. 
     145        """Return a DatabaseType instance for the given Python type. 
    146146         
    147147        hints: if provided, this should be a dict of property attributes 
     
    173173        for dbtype in self.known_types['varchar']: 
    174174            if bytes <= dbtype.max_bytes: 
    175                 print dbtype, bytes 
    176175                return dbtype(bytes=bytes) 
    177176        raise ValueError("%r is greater than the maximum bytes %r." 
     
    332331        """Return the SQL for the given binary operation.""" 
    333332        return "%s %s %s" % (op1.sql, op, op2.sql) 
     333     
     334    def __copy__(self): 
     335        return self.__class__() 
     336    copy = __copy__ 
    334337 
    335338 
     
    500503        # Coerce to str for pickle.loads restriction. 
    501504        if isinstance(value, unicode): 
    502             value = value.encode(self.encoding
     505            value = value.encode('raw-unicode-escape'
    503506        else: 
    504507            value = str(value) 
  • trunk/geniusql/codewalk.py

    r54 r55  
    175175        b = self._bytecode 
    176176        if verbose: 
    177             self.debug("\nWALKING: ", b) 
     177            self.debug("\n\nWALKING: ", b) 
    178178        b_len = len(b)      # Speed hack 
    179179        while self.cursor < b_len: 
  • trunk/geniusql/dbtypes.py

    r54 r55  
    88    """A database type, such as 'REAL' or 'DATE' (with size details).""" 
    99     
     10    # This isn't the most elegant solution, but it's better than making 
     11    # producers of each subclass write their own repr and copy methods. 
     12    _initargs = () 
     13     
     14    # These are class-level attributes and should not be overridden or copied 
     15    # (they're only used at the class level, so it wouldn't make much sense). 
    1016    default_pytype = None 
    1117    default_adapters = None 
     
    1723     
    1824    def __repr__(self): 
    19         return "%s.%s()" % (self.__module__, self.__class__.__name__) 
     25        attrs = ", ".join(["%s=%r" % (k, getattr(self, k)) 
     26                           for k in self._initargs]) 
     27        return "%s.%s(%s)" % (self.__module__, self.__class__.__name__, attrs) 
     28     
     29    def __copy__(self): 
     30        attrs = dict([(k, getattr(self, k)) for k in self._initargs]) 
     31        return self.__class__(**attrs) 
     32    copy = __copy__ 
    2033     
    2134    def cast(self, sql, totype): 
     
    2841 
    2942 
    30 class FixableByteType(DatabaseType): 
    31     """DatabaseType which specifies a 'bytes' attribute.""" 
     43class FrozenByteType(DatabaseType): 
     44    """DatabaseType which specifies a frozen 'bytes' attribute.""" 
     45     
     46    _initargs = DatabaseType._initargs + ("bytes", "max_bytes") 
     47     
     48    _bytes = max_bytes = 255 
     49    def _get_bytes(self): 
     50        return self._bytes 
     51    def _set_bytes(self, value): 
     52        pass 
     53    bytes = property(_get_bytes, _set_bytes, 
     54                     doc="The bytes used for this type.") 
     55 
     56 
     57class AdjustableByteType(DatabaseType): 
     58    """DatabaseType which specifies an adjustable 'bytes' attribute.""" 
     59     
     60    _initargs = DatabaseType._initargs + ("bytes", "max_bytes") 
    3261     
    3362    # The maximum allowable maximum. 
    3463    max_bytes = 255 
    35     # Whether the type allows the bytes to be configured. 
    36     fixed_bytes = False 
    3764     
    3865    _bytes = 255 
     
    4067        return self._bytes 
    4168    def _set_bytes(self, value): 
    42         if self.fixed_bytes: 
    43             value = self.max_bytes 
    4469        if value is not None: 
    4570            if value < 1: 
     
    5479    def ddl(self): 
    5580        """Return the type for use in CREATE or ALTER statements.""" 
    56         if not self.fixed_bytes: 
    57             return "%s(%s)" % (self.__class__.__name__, self.bytes) 
    58         return self.__class__.__name__ 
    59  
    60  
    61 class StringType(FixableByteType): 
    62      
    63     # Whether the type is a variable-length type. 
     81        return "%s(%s)" % (self.__class__.__name__, self.bytes) 
     82 
     83 
     84class FrozenStringType(DatabaseType): 
     85    """DatabaseType for string types whose byte-length is not adjuatable. 
     86     
     87    A FrozenStringType does not imply that the type is a fixed-byte 
     88    type like CHAR as opposed to a variable-byte type like VARCHAR 
     89    (that difference is specified in the "variable" attribute). 
     90    Instead, a Frozen* type implies that the byte limit is not 
     91    user-specifiable; for example, Microsoft Access' MEMO type 
     92    is always 65535 bytes, and takes no size argument in DDL. 
     93    """ 
     94     
     95    # CHAR vs VARCHAR 
    6496    variable = True 
    6597     
     
    82114 
    83115 
    84 class IntegerType(FixableByteType): 
     116class AdjustableStringType(AdjustableByteType): 
     117    """DatabaseType for adjustable-byte string types.""" 
     118     
     119    # CHAR vs VARCHAR 
     120    variable = True 
     121     
     122    default_pytype = str 
     123    default_adapters = {str: adapters.SQL92VARCHAR, 
     124                        unicode: adapters.UNICODE, 
     125                        float: _numtxt(float), 
     126                        int: _numtxt(int), 
     127                        long: _numtxt(long), 
     128                        datetime.timedelta: adapters.INTERVAL, 
     129                        None: adapters.Pickler, 
     130                        } 
     131    if typerefs.decimal: 
     132        if hasattr(typerefs.decimal, "Decimal"): 
     133            default_adapters[typerefs.decimal.Decimal] = adapters.DECIMAL 
     134        else: 
     135            default_adapters[typerefs.decimal] = adapters.DECIMAL 
     136    if typerefs.fixedpoint: 
     137        default_adapters[typerefs.fixedpoint.FixedPoint] = adapters.FIXEDPOINT 
     138 
     139 
     140class IntegerType(FrozenByteType): 
     141     
     142    _initargs = FrozenByteType._initargs + ("signed", ) 
    85143     
    86144    default_pytype = int 
     
    109167 
    110168 
    111 class FixablePrecisionType(DatabaseType): 
    112     """DatabaseType which specifies a 'precision' attribute.""" 
     169class FrozenPrecisionType(DatabaseType): 
     170    """DatabaseType which specifies a frozen 'precision' attribute.""" 
     171     
     172    _initargs = DatabaseType._initargs + ("precision", "max_precision") 
     173     
     174    _precision = max_precision = 255 
     175    def _get_precision(self): 
     176        return self._precision 
     177    def _set_precision(self, value): 
     178        pass 
     179    precision = property(_get_precision, _set_precision, 
     180                         doc="The frozen precision in binary digits (bits).") 
     181 
     182 
     183class AdjustablePrecisionType(DatabaseType): 
     184    """DatabaseType which specifies an adjustable 'precision' attribute.""" 
     185     
     186    _initargs = DatabaseType._initargs + ("precision", "max_precision") 
    113187     
    114188    # Max binary precision for floating-point columns (= 53 for PostgreSQL 8). 
     
    118192    max_precision = 53 
    119193     
    120     # Whether the type allows the precision to be configured. 
    121     fixed_precision = False 
    122      
    123194    _precision = 255 
    124195    def _get_precision(self): 
    125196        return self._precision 
    126197    def _set_precision(self, value): 
    127         if self.fixed_precision: 
    128             value = self.max_precision 
    129198        if value is not None: 
    130199            if value > 1: 
     
    140209    def ddl(self): 
    141210        """Return the type for use in CREATE or ALTER statements.""" 
    142         if not self.fixed_precision: 
    143             return "%s(%s)" % (self.__class__.__name__, self.precision) 
    144         return self.__class__.__name__ 
    145  
    146  
    147 class InexactNumericType(FixablePrecisionType): 
     211        return "%s(%s)" % (self.__class__.__name__, self.precision) 
     212 
     213 
     214class InexactNumericType(FrozenPrecisionType): 
    148215    default_pytype = float 
    149216 
    150217 
    151218class ExactNumericType(DatabaseType): 
     219     
     220    _initargs = DatabaseType._initargs + ("precision", "max_precision", 
     221                                          "scale") 
    152222     
    153223    # For exact numeric types, 'precision' refers to decimal digits 
     
    215285    _max = None 
    216286     
     287    _initargs = DatabaseType._initargs + ("_min", "_max") 
     288     
    217289    def min(self): 
    218290        """Return the minimum value allowed.""" 
  • trunk/geniusql/objects.py

    r54 r55  
    155155     
    156156    def __copy__(self): 
    157         newcol = self.__class__(self.pytype, self.dbtype, self.default, 
    158                                 self.key, self.name, self.qname) 
     157        newcol = self.__class__(self.pytype, self.dbtype.copy(), 
     158                                self.default, self.key, 
     159                                self.name, self.qname) 
    159160        newcol.autoincrement = self.autoincrement 
    160161        newcol.initial = self.initial 
    161         newcol.adapter = self.adapter 
     162        newcol.adapter = self.adapter.copy() 
    162163        return newcol 
    163164    copy = __copy__ 
  • trunk/geniusql/providers/__init__.py

    r54 r55  
    5050 
    5151registry = _Registry({ 
    52     "access": "geniusql.providers.ado.MSAccessDatabase", 
    53     "msaccess": "geniusql.providers.ado.MSAccessDatabase", 
     52    "access": "geniusql.providers.msaccess.MSAccessDatabase", 
     53    "msaccess": "geniusql.providers.msaccess.MSAccessDatabase", 
    5454     
    5555    "firebird": "geniusql.providers.firebird.FirebirdDatabase", 
  • trunk/geniusql/providers/ado.py

    r54 r55  
    2525import pywintypes 
    2626import datetime 
    27  
    2827import time 
    2928 
    30 import threading 
    31 import warnings 
    32  
    33  
    3429import geniusql 
    35 from geniusql import adapters, conns, decompile, errors, select, typerefs 
     30from geniusql import adapters, conns, decompile, errors 
    3631from geniusql import isolation as _isolation 
    3732 
     
    5247 
    5348adUseClient = 3 
    54  
    55 # See http://www.carlprothman.net/Technology/DataTypeMapping/tabid/97/Default.aspx 
    56 dbtypes = {# ADO Name           SQL Server              MS Access 
    57         0: 'EMPTY', 
    58         2: 'SMALLINT',        # SMALLINT                INTEGER 
    59         3: 'INTEGER',         # IDENTITY (6.5), INT     AUTONUMBER, LONG 
    60         4: 'SINGLE',          # REAL                    SINGLE 
    61         5: 'DOUBLE',          # FLOAT                   DOUBLE 
    62         6: 'CURRENCY',        # MONEY, SMALLMONEY       CURRENCY 
    63         7: 'DATE',            #                         DATETIME (Access 97) 
    64         8: 'BSTR', 9: 'IDISPATCH', 10: 'ERROR', 
    65         11: 'BOOLEAN',        # BIT                     YESNO 
    66         12: 'VARIANT',        # SQL_VARIANT (2000 +) 
    67         13: 'IUNKNOWN', 14: 'DECIMAL', 16: 'TINYINT', 
    68         17: 'UNSIGNEDTINYINT',# TINYINT                 BYTE 
    69         18: 'UNSIGNEDSMALLINT', 19: 'UNSIGNEDINT', 
    70         20: 'BIGINT',         # BIGINT 
    71         21: 'UNSIGNEDBIGINT', 
    72         72: 'GUID', 
    73         128: 'BINARY',        # BINARY, TIMESTAMP 
    74         129: 'CHAR',          # CHAR 
    75         130: 'WCHAR',         # NCHAR (7.0+) 
    76         131: 'NUMERIC',       # DECIMAL, NUMERIC        DECIMAL (Access 2000) 
    77         132: 'USERDEFINED', 
    78         133: 'DBDATE', 134: 'DBTIME', 
    79         135: 'DBTIMESTAMP',   # DATETIME, SMALLDATETIME DATETIME (ODBC 97) 
    80         200: 'VARCHAR',       # VARCHAR                 TEXT (Access 97) 
    81         201: 'LONGVARCHAR',   # TEXT                    MEMO (Access 97) 
    82         202: 'VARWCHAR',      # NVARCHAR                TEXT (Access 2000) 
    83         203: 'LONGVARWCHAR',  # NTEXT (7.0+)            MEMO (Access 2000+) 
    84         204: 'VARBINARY',     # VARBINARY 
    85         205: 'LONGVARBINARY', # IMAGE                   OLEOBJECT 
    86 } 
    8749 
    8850DBCOLUMNFLAGS_WRITE = 0x4 
     
    264226        raise TypeError("unsupported operand type(s) for %s: " 
    265227                        "%r and %r" % (op, op1.pytype, op2.pytype)) 
    266  
    267  
    268 class CURRENCY(adapters.SQL92DOUBLE): 
    269     def pull(self, value): 
    270         if isinstance(value, tuple): 
    271             # See http://groups.google.com/group/comp.lang.python/ 
    272             #           browse_frm/thread/fed03c64735c9e9c 
    273             value = map(long, value) 
    274             return ((value[1] & 0xFFFFFFFFL) | (value[0] << 32)) / 1e4 
    275         return float(value) 
    276  
    277 class decimalCURRENCY(adapters.DECIMAL): 
    278     def pull(self, value): 
    279         # pywin32 build 205 began support for returning 
    280         # COM Currency objects as decimal objects. 
    281         # See http://pywin32.cvs.sourceforge.net/pywin32/pywin32/CHANGES.txt?view=markup 
    282         if not isinstance(value, typerefs.decimal.Decimal): 
    283             # See http://groups.google.com/group/comp.lang.python/ 
    284             #           browse_frm/thread/fed03c64735c9e9c 
    285             value = map(long, value) 
    286             value = (value[1] & 0xFFFFFFFFL) | (value[0] << 32) 
    287             return typerefs.decimal.Decimal(value) / 10000 
    288         return value 
    289  
    290 class fpCURRENCY(adapters.FIXEDPOINT): 
    291     def pull(self, value): 
    292         if isinstance(value, typerefs.decimal.Decimal): 
    293             value = str(value) 
    294             scale = 0 
    295             atoms = value.rsplit(".", 1) 
    296             if len(atoms) > 1: 
    297                 scale = len(atoms[-1]) 
    298             return typerefs.fixedpoint.FixedPoint(value, scale) 
    299         else: 
    300             # See http://groups.google.com/group/comp.lang.python/ 
    301             #           browse_frm/thread/fed03c64735c9e9c 
    302             value = map(long, value) 
    303             value = (value[1] & 0xFFFFFFFFL) | (value[0] << 32) 
    304             return typerefs.fixedpoint.FixedPoint(value, 4) / 1e4 
    305228 
    306229 
     
    555478        raise errors.MappingError(tablename) 
    556479     
    557     def _get_columns(self, tablename, conn=None): 
    558         # For some reason, adSchemaPrimaryKeys would only return a single 
    559         # record for a PK that had multiple columns. Use adSchemaIndexes. 
    560         # coldefs will be: 
    561         # [(u'TABLE_CATALOG', 202), (u'TABLE_SCHEMA', 202), (u'TABLE_NAME', 202), 
    562         # (u'INDEX_CATALOG', 202), (u'INDEX_SCHEMA', 202), (u'INDEX_NAME', 202), 
    563         # (u'PRIMARY_KEY', 11), (u'UNIQUE', 11), (u'CLUSTERED', 11), (u'TYPE', 18), 
    564         # (u'FILL_FACTOR', 3), (u'INITIAL_SIZE', 3), (u'NULLS', 3), 
    565         # (u'SORT_BOOKMARKS', 11), (u'AUTO_UPDATE', 11), (u'NULL_COLLATION', 3), 
    566         # (u'ORDINAL_POSITION', 19), (u'COLUMN_NAME', 202), (u'COLUMN_GUID', 72), 
    567         # (u'COLUMN_PROPID', 19), (u'COLLATION', 2), (u'CARDINALITY', 21), 
    568         # (u'PAGES', 3), (u'FILTER_CONDITION', 202), (u'INTEGRATED', 11)] 
    569         data, _ = self.db.fetch(adSchemaIndexes, conn=conn, schema=True) 
    570         pknames = [row[17] for row in data 
    571                    if (tablename == row[2]) and row[6]] 
    572          
    573         # columns will be 
    574         # [(u'TABLE_CATALOG', 202), (u'TABLE_SCHEMA', 202), (u'TABLE_NAME', 202), 
    575         # (u'COLUMN_NAME', 202), (u'COLUMN_GUID', 72), (u'COLUMN_PROPID', 19), 
    576         # (u'ORDINAL_POSITION', 19), (u'COLUMN_HASDEFAULT', 11), 
    577         # (u'COLUMN_DEFAULT', 203), (u'COLUMN_FLAGS', 19), (u'IS_NULLABLE', 11), 
    578         # (u'DATA_TYPE', 18), (u'TYPE_GUID', 72), (u'CHARACTER_MAXIMUM_LENGTH', 19), 
    579         # (u'CHARACTER_OCTET_LENGTH', 19), (u'NUMERIC_PRECISION', 18), 
    580         # (u'NUMERIC_SCALE', 2), (u'DATETIME_PRECISION', 19), 
    581         # (u'CHARACTER_SET_CATALOG', 202), (u'CHARACTER_SET_SCHEMA', 202), 
    582         # (u'CHARACTER_SET_NAME', 202), (u'COLLATION_CATALOG', 202), 
    583         # (u'COLLATION_SCHEMA', 202), (u'COLLATION_NAME', 202), 
    584         # (u'DOMAIN_CATALOG', 202), (u'DOMAIN_SCHEMA', 202), 
    585         # (u'DOMAIN_NAME', 202), (u'DESCRIPTION', 203)] 
    586         data, _ = self.db.fetch(adSchemaColumns, conn=conn, schema=True) 
    587          
    588         cols = [] 
    589         typer = self.db.adapterset 
    590         for row in data: 
    591             # I tried passing criteria to OpenSchema, but passing None is 
    592             # not the same as passing pythoncom.Empty (which errors). 
    593             if row[2] != tablename: 
    594                 continue 
    595              
    596             dbtypename = dbtypes[row[11]] 
    597             dbtype = typer.canonicalize(dbtypename)() 
    598             pytype = dbtype.default_pytype 
    599             if pytype is None: 
    600                 raise TypeError("%r has no default pytype." % dbtype) 
    601              
    602             default = row[8] 
    603             if default is not None: 
    604                 if issubclass(pytype, (int, long, float)): 
    605                     # We may have stuck extraneous quotes in the default 
    606                     # value when using numeric defaults with MSAccess. 
    607                     if default.startswith("'") and default.endswith("'"): 
    608                         default = default[1:-1] 
    609                 default = pytype(default) 
    610              
    611             name = str(row[3]) 
    612             c = geniusql.Column(pytype, dbtype, default, 
    613                                 key=(name in pknames), 
    614                                 name=name, qname=self.db.quote(name)) 
    615              
    616             # This only works for SQL Server. The MSAccessDatabase will 
    617             # wrap this method and override autoincrement. 
    618             colflags = int(row[9]) 
    619             if ((colflags & DBCOLUMNFLAGS_ISFIXEDLENGTH) 
    620                 and not (colflags & DBCOLUMNFLAGS_WRITE)): 
    621                 c.autoincrement = True 
    622              
    623             if dbtype in typer.known_types['int']: 
    624                 dbtype.bytes = row[15] 
    625             elif dbtype in typer.known_types['float']: 
    626                 dbtype.precision = row[15] 
    627                 dbtype.scale = row[16] 
    628 ##            elif dbtype == "CURRENCY": 
    629 ##                # CURRENCY allows 15 places to the left of the decimal point, 
    630 ##                # and 4 places to the right. 
    631 ##                c.hints['precision'] = 19 
    632 ##                c.hints['scale'] = 4 
    633             elif dbtype in typer.known_types['numeric']: 
    634                 dbtype.precision = row[15] 
    635                 dbtype.scale = row[16] 
    636             elif (dbtype in typer.known_types['char'] or 
    637                   dbtype in typer.known_types['varchar']): 
    638                 if row[13]: 
    639                     # row[13] will be a float 
    640                     dbtype.bytes = b = int(row[13]) 
    641 ##                else: 
    642 ##                    # I'm kinda guessing on this. If we use "MEMO" in an 
    643 ##                    # MSAccess CREATE statement, it comes back as "WCHAR", 
    644 ##                    # and seems to support over 65536 bytes. 
    645 ##                    dbtype.bytes = b = (2 ** 31) - 1 
    646 ##            elif dbtype in ("LONGVARCHAR", "LONGVARBINARY", "LONGVARWCHAR"): 
    647 ##                if row[13]: 
    648 ##                    # row[13] will be a float 
    649 ##                    c.hints['bytes'] = b = int(row[13]) 
    650 ##                    c.dbtype = "%s(%s)" % (c.dbtype, b) 
    651 ##                else: 
    652 ##                    c.hints['bytes'] = 65535 
    653              
    654             c.adapter = typer.default(pytype, dbtype) 
    655             cols.append(c) 
    656         return cols 
    657      
    658480    def _get_indices(self, tablename=None, conn=None): 
    659481        # cols will be 
  • trunk/geniusql/providers/msaccess.py

    r54 r55  
     1import datetime 
     2 
     3from geniusql import adapters, conns, dbtypes, objects, typerefs 
    14from geniusql.providers import ado 
     5 
     6import win32com.client 
     7 
     8 
     9# ------------------------------ Adapters ------------------------------ # 
    210 
    311 
     
    2937                (value.days, (value.seconds / 86400.0))) 
    3038 
    31  
    3239class MSAccess_date(ado.COM_date): 
    3340     
     
    5057        return '#%s/%s/%s#' % (value.month, value.day, value.year) 
    5158 
    52  
    5359class MSAccess_datetime(ado.COM_datetime): 
    5460     
     
    6975                 value.hour, value.minute, value.second)) 
    7076 
    71  
    7277class MSAccess_time(ado.COM_time): 
    7378     
     
    7681 
    7782 
     83class CURRENCY_float(adapters.SQL92DOUBLE): 
     84     
     85    def pull(self, value): 
     86        if isinstance(value, tuple): 
     87            # See http://groups.google.com/group/comp.lang.python/ 
     88            #           browse_frm/thread/fed03c64735c9e9c 
     89            value = map(long, value) 
     90            return ((value[1] & 0xFFFFFFFFL) | (value[0] << 32)) / 1e4 
     91        return float(value) 
     92 
     93class CURRENCY_decimal(adapters.DECIMAL): 
     94     
     95    def pull(self, value): 
     96        # pywin32 build 205 began support for returning 
     97        # COM Currency objects as decimal objects. 
     98        # See http://pywin32.cvs.sourceforge.net/pywin32/pywin32/CHANGES.txt?view=markup 
     99        if not isinstance(value, typerefs.decimal.Decimal): 
     100            # See http://groups.google.com/group/comp.lang.python/ 
     101            #           browse_frm/thread/fed03c64735c9e9c 
     102            value = map(long, value) 
     103            value = (value[1] & 0xFFFFFFFFL) | (value[0] << 32) 
     104            return typerefs.decimal.Decimal(value) / 10000 
     105        return value 
     106 
     107class CURRENCY_FixedPoint(adapters.FIXEDPOINT): 
     108     
     109    def pull(self, value): 
     110        if isinstance(value, typerefs.decimal.Decimal): 
     111            value = str(value) 
     112            scale = 0 
     113            atoms = value.rsplit(".", 1) 
     114            if len(atoms) > 1: 
     115                scale = len(atoms[-1]) 
     116            return typerefs.fixedpoint.FixedPoint(value, scale) 
     117        else: 
     118            # See http://groups.google.com/group/comp.lang.python/ 
     119            #           browse_frm/thread/fed03c64735c9e9c 
     120            value = map(long, value) 
     121            value = (value[1] & 0xFFFFFFFFL) | (value[0] << 32) 
     122            return typerefs.fixedpoint.FixedPoint(value, 4) / 1e4 
     123 
     124 
     125# ---------------------------- DatabaseTypes ---------------------------- # 
     126 
     127# These are Access 2000+ types. 
     128# See http://msdn2.microsoft.com/en-us/library/aa140015(office.10).aspx 
     129# http://msdn2.microsoft.com/en-us/library/ms714540.aspx 
     130# http://office.microsoft.com/en-us/access/HP010322481033.aspx 
     131 
     132# "A Text field can store up to 255 characters, but the default field size 
     133# is 50 characters. A Memo field can store up to 65,536 characters. If you 
     134# want to store formatted text or long documents, you should create an OLE 
     135# Object field instead of a Memo field. Both Text and Memo data types store 
     136# only the characters entered in a field; space characters for unused 
     137# positions in the field aren't stored. You can sort or group on a Text 
     138# field or a Memo field, but Access only uses the first 255 characters 
     139# when you sort or group on a Memo field." 
     140 
     141class TEXT(dbtypes.AdjustableStringType): 
     142     
     143    _initargs = dbtypes.AdjustableStringType._initargs + ("with_compression",) 
     144     
     145    # Actually 255 chars, 2 bytes per char unless compressed 
     146    max_bytes = 255 
     147    bytes = 255 
     148    variable = True 
     149     
     150    # "With the Microsoft Jet 4.0 database engine, all data for the TEXT 
     151    # data types are now stored in the Unicode 2-byte character 
     152    # representation format. It replaces the Multi-byte Character Set 
     153    # (MBCS) format that was used in previous versions. Although Unicode 
     154    # representation requires more space to store each character, columns 
     155    # with TEXT data types can be defined to automatically compress the 
     156    # data if it is possible to do so. 
     157    #  
     158    # When you create TEXT data types with SQL, the Unicode compression 
     159    # property defaults to No. To set the Unicode compression property 
     160    # to Yes, use the WITH COMPRESSION (or WITH COMP) keywords at the 
     161    # field-level declaration." 
     162    with_compression = False 
     163     
     164    def ddl(self): 
     165        """Return the type for use in CREATE or ALTER statements.""" 
     166        withcomp = "" 
     167        if self.with_compression: 
     168            withcomp = " WITH COMPRESSION" 
     169        return "%s(%s)%s" % (self.__class__.__name__, self.bytes, withcomp) 
     170 
     171 
     172class MEMO(dbtypes.FrozenStringType): 
     173    # MEMO is 1 GB max when set programatically (only 64K when set 
     174    # in Access UI). But then, 1 GB is the limit for the whole DB. 
     175    # Note that OpenSchema will return a DATA_TYPE of "WCHAR". 
     176    synonyms = ['WCHAR'] 
     177    bytes = max_bytes = 65535           # (2.14 GB if not binary data) 
     178    variable = True 
     179 
     180 
     181class TINYINT(dbtypes.IntegerType): 
     182    synonyms = ['INTEGER1', 'BYTE'] 
     183    bytes = max_bytes = 1 
     184    signed = False 
     185 
     186class SMALLINT(dbtypes.IntegerType): 
     187    synonyms = ['SHORT', 'INTEGER2'] 
     188    bytes = max_bytes = 2 
     189 
     190class INTEGER(dbtypes.IntegerType): 
     191    synonyms = ['LONG', 'INT', 'INTEGER4'] 
     192    bytes = max_bytes = 4 
     193 
     194 
     195class FLOAT(dbtypes.InexactNumericType): 
     196    synonyms = ['DOUBLE', 'FLOAT8', 'IEEEDOUBLE', 'NUMBER'] 
     197    precision = max_precision = 53 
     198    default_adapters = {float: adapters.SQL92DOUBLE} 
     199 
     200class REAL(dbtypes.InexactNumericType): 
     201    synonyms = ['SINGLE', 'FLOAT4', 'IEEESINGLE'] 
     202    precision = max_precision = 24 
     203    default_adapters = {float: adapters.SQL92REAL} 
     204 
     205 
     206class DECIMAL(dbtypes.ExactNumericType): 
     207    synonyms = ['NUMERIC', 'DEC'] 
     208     
     209    # "...precision, the default is 18 and the maximum allowed value is 28. 
     210    # For the scale, the default is 0 and the maximum allowed value is 28." 
     211    precision = 18 
     212    max_precision = 28 
     213    scale = 0 
     214##     
     215##    # Hm. Docs say 28, but I can't seem to get more than 12 working. 
     216##    numeric_max_precision = 12 
     217##    numeric_max_bytes = 6 
     218 
     219 
     220class CURRENCY(dbtypes.FrozenPrecisionType): 
     221     
     222    _initargs = dbtypes.FrozenPrecisionType._initargs + ("scale", ) 
     223     
     224    # "The CURRENCY data type is used to store numeric data that contains 
     225    # up to 15 digits on the left side of the decimal point, and up to 4 
     226    # digits on the right. It uses 8 bytes of memory for storage, and its 
     227    # only synonym is MONEY." 
     228    synonyms = ['MONEY'] 
     229     
     230    precision = max_precision = 19 
     231    scale = property(lambda self: 4, lambda self, value: None) 
     232     
     233    default_adapters = {float: CURRENCY_float} 
     234    if typerefs.decimal: 
     235        if hasattr(typerefs.decimal, "Decimal"): 
     236            default_adapters[typerefs.decimal.Decimal] = CURRENCY_decimal 
     237        else: 
     238            default_adapters[typerefs.decimal] = CURRENCY_decimal 
     239    if typerefs.fixedpoint: 
     240        default_adapters[typerefs.fixedpoint.FixedPoint] = CURRENCY_FixedPoint 
     241 
     242 
     243class YESNO(dbtypes.DatabaseType): 
     244    # "The BOOLEAN data types are logical types that result in either True 
     245    # or False values. They use 1 byte of memory for storage, and their 
     246    # synonyms are BIT, LOGICAL, LOGICAL1, and YESNO. A True value is 
     247    # equal to -1 while a False value is equal to 0." 
     248    default_adapters = {bool: adapters.BOOLEAN} 
     249 
     250 
     251class BINARY(dbtypes.AdjustableByteType): 
     252    # "The BINARY data type is used to store a small amount of any type 
     253    # of data in its native, binary format. It uses 1 byte of memory for 
     254    # each character stored, and you can optionally specify the number 
     255    # of bytes to be allocated. If the number of bytes is not specified, 
     256    # it defaults to 510 bytes, which is the maximum number of bytes 
     257    # allowed. Its synonyms are BINARY, VARBINARY, and BINARY VARYING. 
     258    # The BINARY data type is not available in the Access user interface." 
     259    synonyms = ['VARBINARY', 'BINARY VARYING'] 
     260    bytes = 510 
     261    max_bytes = 510 
     262    variable = False 
     263 
     264 
     265class DATETIME(dbtypes.DateTimeType): 
     266    # "The DATETIME data type is used to store date, time, and combination 
     267    # date/time values for the years ranging from 100 to 9999. 
     268    # It uses 8 bytes of memory for storage, and its synonyms are 
     269    # DATE, TIME, DATETIME, and TIMESTAMP. 
     270    synonyms = ['DATE', 'TIME', 'TIMESTAMP'] 
     271    _min = datetime.datetime(100, 1, 1) 
     272    _max = datetime.datetime(9999, 12, 31) 
     273     
     274    default_adapters = {datetime.datetime: MSAccess_datetime, 
     275                        datetime.date: MSAccess_date, 
     276                        datetime.time: MSAccess_time, 
     277                        datetime.timedelta: MSAccess_timedelta, 
     278                        } 
     279 
     280 
    78281class MSAccessAdapterSet(ado.ADOAdapterSet): 
    79282     
    80     # http://msdn2.microsoft.com/en-us/library/ms714540.aspx 
    81     # http://office.microsoft.com/en-us/access/HP010322481033.aspx 
    82      
    83     # Hm. Docs say 28/38, but I can't seem to get more than 12 working. 
    84     numeric_max_precision = 12 
    85     numeric_max_bytes = 6 
    86      
    87     def pytype_for_LONG(self, hints): return int 
    88     def pytype_for_MEMO(self, hints): str 
    89      
    90     def dbtype_for_bool(self, hints): return "BIT" 
    91     def dbtype_for_datetime_datetime(self, hints): return "DATETIME" 
    92     def dbtype_for_datetime_date(self, hints): return "DATETIME" 
    93     def dbtype_for_datetime_time(self, hints): return "DATETIME" 
    94     def dbtype_for_datetime_timedelta(self, hints): return "DATETIME" 
    95      
    96     def int_type(self, bytes): 
    97         if bytes <= 2: 
    98             return "INTEGER" 
    99         elif bytes <= 4: 
    100             return "LONG" 
    101         else: 
    102             # Anything larger than 4 bytes, use decimal/numeric. 
    103             return "DECIMAL" 
    104      
    105     def float_type(self, precision): 
    106         """Return a datatype which can handle floats of the given binary precision.""" 
    107         if precision <= 24: 
    108             return "SINGLE" 
    109         else: 
    110             return "DOUBLE" 
    111      
    112     def dbtype_for_str(self, hints): 
    113         # The bytes hint shall not reflect the usual 4-byte base for varchar. 
    114         bytes = int(hints.get('bytes', 255)) 
    115          
    116         # 255 chars is the upper limit for TEXT / VARCHAR in MS Access. 
    117         if bytes == 0 or bytes > 255: 
    118             # MEMO is 1 GB max when set programatically (only 64K when set 
    119             # in Access UI). But then, 1 GB is the limit for the whole DB. 
    120             # Note that OpenSchema will return a DATA_TYPE of "WCHAR". 
    121             return "MEMO" 
    122          
    123         return "VARCHAR(%s)" % bytes 
    124  
    125 MSAccessAdapterSet.defaults.update({ 
    126     (datetime.datetime, "any"): MSAccess_datetime, 
    127     (datetime.date, "any"): MSAccess_date, 
    128     (datetime.time, "any"): MSAccess_time, 
    129     (datetime.timedelta, "any"): MSAccess_timedelta, 
    130     }) 
    131 if typerefs.decimal: 
    132     if hasattr(typerefs.decimal, "Decimal"): 
    133         MSAccessAdapterSet.defaults[(typerefs.decimal.Decimal, "CURRENCY") 
    134                                     ] = decimalCURRENCY() 
    135     else: 
    136         MSAccessAdapterSet.defaults[(typerefs.decimal, "CURRENCY") 
    137                                     ] = decimalCURRENCY() 
    138 if typerefs.fixedpoint: 
    139     MSAccessAdapterSet.defaults[(typerefs.fixedpoint.FixedPoint, "CURRENCY") 
    140                                 ] = fpCURRENCY() 
     283    known_types = {'float': [REAL, FLOAT], 
     284                   'varchar': [TEXT, MEMO], 
     285                   'char': [BINARY], 
     286                   'int': [TINYINT, SMALLINT, INTEGER], 
     287                   'bool': [YESNO], 
     288                   'datetime': [DATETIME], 
     289                   'date': [DATETIME], 
     290                   'time': [DATETIME], 
     291                   'timedelta': [DATETIME], 
     292                   'numeric': [DECIMAL], 
     293                   'other': [CURRENCY], 
     294                   } 
    141295 
    142296 
     
    236390    tableclass = MSAccessTable 
    237391     
     392    # See http://www.carlprothman.net/Technology/DataTypeMapping/tabid/97/Default.aspx 
     393    adotypes = { 
     394        # MS Access             ADO Name 
     395        2: SMALLINT,            # SMALLINT 
     396        3: INTEGER,             # INTEGER 
     397        4: REAL,                # SINGLE 
     398        5: FLOAT,               # DOUBLE 
     399        6: CURRENCY,            # CURRENCY 
     400        7: DATETIME,            # DATE (Access 97) 
     401        11: YESNO,              # BOOLEAN 
     402        17: TINYINT,            # UNSIGNEDTINYINT 
     403        128: BINARY,            # BINARY 
     404        130: MEMO,              # WCHAR 
     405        131: DECIMAL,           # NUMERIC (Access 2000) 
     406        135: DATETIME,          # DBTIMESTAMP (ODBC 97) 
     407        200: TEXT,              # VARCHAR (Access 97) 
     408        201: MEMO,              # LONGVARCHAR (Access 97) 
     409        202: TEXT,              # VARWCHAR (Access 2000) 
     410        203: MEMO,              # LONGVARWCHAR (Access 2000+) 
     411        # 205: OLEOBJECT,       # LONGVARBINARY 
     412        # 0: EMPTY, 
     413        # 8: BSTR, 9: IDISPATCH, 10: ERROR, 
     414        # 12: VARIANT, 13: IUNKNOWN, 14: DECIMAL, 16: TINYINT, 
     415        # 18: UNSIGNEDSMALLINT, 19: UNSIGNEDINT, 20: BIGINT, 
     416        # 21: UNSIGNEDBIGINT, 72: GUID, 
     417        # 129: CHAR,  
     418        # 132: USERDEFINED, 133: DBDATE, 134: DBTIME, 
     419        # 204: VARBINARY, 
     420    } 
     421     
    238422    def _get_columns(self, tablename, conn=None): 
    239         cols = ado.ADOSchema._get_columns(self, tablename, conn) 
     423        # For some reason, adSchemaPrimaryKeys would only return a single 
     424        # record for a PK that had multiple columns. Use adSchemaIndexes. 
     425        # coldefs will be: 
     426        # [(u'TABLE_CATALOG', 202), (u'TABLE_SCHEMA', 202), (u'TABLE_NAME', 202), 
     427        # (u'INDEX_CATALOG', 202), (u'INDEX_SCHEMA', 202), (u'INDEX_NAME', 202), 
     428        # (u'PRIMARY_KEY', 11), (u'UNIQUE', 11), (u'CLUSTERED', 11), (u'TYPE', 18), 
     429        # (u'FILL_FACTOR', 3), (u'INITIAL_SIZE', 3), (u'NULLS', 3), 
     430        # (u'SORT_BOOKMARKS', 11), (u'AUTO_UPDATE', 11), (u'NULL_COLLATION', 3), 
     431        # (u'ORDINAL_POSITION', 19), (u'COLUMN_NAME', 202), (u'COLUMN_GUID', 72), 
     432        # (u'COLUMN_PROPID', 19), (u'COLLATION', 2), (u'CARDINALITY', 21), 
     433        # (u'PAGES', 3), (u'FILTER_CONDITION', 202), (u'INTEGRATED', 11)] 
     434        data, _ = self.db.fetch(ado.adSchemaIndexes, conn=conn, schema=True) 
     435        pknames = [row[17] for row in data 
     436                   if (tablename == row[2]) and row[6]] 
     437         
     438        # columns will be 
     439        # [(u'TABLE_CATALOG', 202), (u'TABLE_SCHEMA', 202), (u'TABLE_NAME', 202), 
     440        # (u'COLUMN_NAME', 202), (u'COLUMN_GUID', 72), (u'COLUMN_PROPID', 19), 
     441        # (u'ORDINAL_POSITION', 19), (u'COLUMN_HASDEFAULT', 11), 
     442        # (u'COLUMN_DEFAULT', 203), (u'COLUMN_FLAGS', 19), (u'IS_NULLABLE', 11), 
     443        # (u'DATA_TYPE', 18), (u'TYPE_GUID', 72), (u'CHARACTER_MAXIMUM_LENGTH', 19), 
     444        # (u'CHARACTER_OCTET_LENGTH', 19), (u'NUMERIC_PRECISION', 18), 
     445        # (u'NUMERIC_SCALE', 2), (u'DATETIME_PRECISION', 19), 
     446        # (u'CHARACTER_SET_CATALOG', 202), (u'CHARACTER_SET_SCHEMA', 202), 
     447        # (u'CHARACTER_SET_NAME', 202), (u'COLLATION_CATALOG', 202), 
     448        # (u'COLLATION_SCHEMA', 202), (u'COLLATION_NAME', 202), 
     449        # (u'DOMAIN_CATALOG', 202), (u'DOMAIN_SCHEMA', 202), 
     450        # (u'DOMAIN_NAME', 202), (u'DESCRIPTION', 203)] 
     451        data, _ = self.db.fetch(ado.adSchemaColumns, conn=conn, schema=True) 
     452         
     453        cols = [] 
     454        typer = self.db.adapterset 
     455        for row in data: 
     456            # I tried passing criteria to OpenSchema, but passing None is 
     457            # not the same as passing pythoncom.Empty (which errors). 
     458            if row[2] != tablename: 
     459                continue 
     460             
     461            dbtype = self.adotypes[row[11]]() 
     462            pytype = dbtype.default_pytype 
     463            if pytype is None: 
     464                raise TypeError("%r has no default pytype." % dbtype) 
     465             
     466            default = row[8] 
     467            if default is not None: 
     468                if issubclass(pytype, (int, long, float)): 
     469                    # We may have stuck extraneous quotes in the default 
     470                    # value when using numeric defaults with MSAccess. 
     471                    if default.startswith("'") and default.endswith("'"): 
     472                        default = default[1:-1] 
     473                default = pytype(default) 
     474             
     475            name = str(row[3]) 
     476            c = objects.Column(pytype, dbtype, default, 
     477                               key=(name in pknames), 
     478                               name=name, qname=self.db.quote(name)) 
     479             
     480            if dbtype in typer.known_types['int']: 
     481                dbtype.bytes = row[15] 
     482            elif dbtype in typer.known_types['float']: 
     483                dbtype.precision = row[15] 
     484                dbtype.scale = row[16] 
     485            elif dbtype in typer.known_types['numeric']: 
     486                dbtype.precision = row[15] 
     487                dbtype.scale = row[16] 
     488            elif isinstance(dbtype, MEMO): 
     489                pass 
     490            elif (dbtype in typer.known_types['char'] or 
     491                  dbtype in typer.known_types['varchar']): 
     492                if row[13]: 
     493                    # row[13] will be a float 
     494                    dbtype.bytes = int(row[13]) 
     495                else: 
     496                    # I'm kinda guessing on this. If we use "MEMO" in an 
     497                    # MSAccess CREATE statement, it comes back as "WCHAR", 
     498                    # and seems to support over 65536 bytes. 
     499                    dbtype.bytes = (2 ** 31) - 1 
     500             
     501            c.adapter = typer.default(pytype, dbtype) 
     502            cols.append(c) 
     503         
     504        # Horrible hack to get autoincrement property 
    240505        if conn is None: 
    241506            conn = self.db.connections._factory() 
    242          
    243507        try: 
    244             # Horrible hack to get autoincrement property 
    245508            query = "SELECT * FROM %s WHERE FALSE;" % self.db.quote(tablename) 
    246509            bareconn = conn 
     
    254517                                            ((8, 1), (16396, 18), (3, 49)), 
    255518                                            # *args = 
    256                                             query, pythoncom.Missing, -1) 
    257         except pywintypes.com_error, x: 
     519                                            query, ado.pythoncom.Missing, -1) 
     520        except ado.pywintypes.com_error, x: 
    258521            try: 
    259                 res.InvokeTypes(*Recordset_Close) 
     522                res.InvokeTypes(*ado.Recordset_Close) 
    260523            except: 
    261524                pass 
     
    265528             
    266529            try: 
     530                # Return no columns when inspecting system tables 
    267531                if "no read permission" in x.args[2][2]: 
    268532                    conn = None 
     
    274538            raise x 
    275539         
    276         resFields = res.InvokeTypes(*Recordset_Fields) 
     540        resFields = res.InvokeTypes(*ado.Recordset_Fields) 
    277541        for c in cols: 
    278542            f = resFields.InvokeTypes(0, 0, 2, (9, 0), ((12, 1),), c.name) 
    279             fprops = f.InvokeTypes(*Field_Properties) 
     543            fprops = f.InvokeTypes(*ado.Field_Properties) 
    280544            fprop = fprops.InvokeTypes(0, 0, 2, (9, 0), ((12, 1), ), "ISAUTOINCREMENT") 
    281             c.autoincrement = fprop.InvokeTypes(*Property_Value) 
     545            c.autoincrement = fprop.InvokeTypes(*ado.Property_Value) 
     546            if c.autoincrement: 
     547                # Grumble. Get the Seed value from ADOX. 
     548                try: 
     549                    cat = win32com.client.Dispatch(r'ADOX.Catalog') 
     550                    cat.ActiveConnection = conn 
     551                    adoxcol = cat.Tables(tablename).Columns(c.name) 
     552                    c.initial = adoxcol.Properties('Seed').Value 
     553                    adoxcol = None 
     554                finally: 
     555                    cat = None 
    282556         
    283557        try: 
    284             res.InvokeTypes(*Recordset_Close) 
     558            res.InvokeTypes(*ado.Recordset_Close) 
    285559        except: 
    286560            pass 
     
    295569            name type [DEFAULT x|AUTOINCREMENT(initial, 1)] 
    296570        """ 
    297         dbtype = column.dbtype 
     571        ddl = column.dbtype.ddl() 
    298572         
    299573        if column.autoincrement: 
     574            if column.dbtype.default_pytype not in (int, long): 
     575                raise ValueError("Microsoft Access does not allow COUNTER " 
     576                                 "columns of type %r" % dbtype) 
     577            ddl = " COUNTER(%s, 1) NOT NULL" % column.initial 
     578        else: 
    300579            # MS Access does not allow a column to have 
    301580            # both an AUTOINCREMENT clause and a DEFAULT clause. 
    302             # It also needs no type in this case. 
    303             dbtype = "AUTOINCREMENT(%s, 1)" % column.initial 
    304         else: 
    305581            default = column.default or "" 
    306582            if default: 
     
    309585                    # Crazy quote hack to get a numeric default to work. 
    310586                    defspec = "'%s'" % defspec 
    311                 dbtype = "%s DEFAULT %s" % (dbtype, defspec) 
    312          
    313         return '%s %s' % (column.qname, dbtype
     587                ddl = "%s DEFAULT %s" % (ddl, defspec) 
     588         
     589        return '%s %s' % (column.qname, ddl
    314590     
    315591    def create_database(self): 
  • trunk/geniusql/providers/sqlserver.py

    r54 r55  
    11import datetime 
    22 
    3 from geniusql import adapters, dbtypes 
     3from geniusql import adapters, dbtypes, objects 
    44from geniusql.providers import ado 
    55 
     
    1919 
    2020 
    21 class BINARY(dbtypes.StringType): 
     21class BINARY(dbtypes.AdjustableStringType): 
    2222    max_bytes = 8000 
    2323    variable = False 
    2424 
    25 class VARBINARY(dbtypes.StringType): 
     25class VARBINARY(dbtypes.AdjustableStringType): 
    2626    max_bytes = 8000 
    2727    variable = True 
    2828 
    29 class CHAR(dbtypes.StringType): 
     29class CHAR(dbtypes.AdjustableStringType): 
    3030    max_bytes = 8000 
    3131    variable = False 
    3232 
    33 class VARCHAR(dbtypes.StringType): 
     33class VARCHAR(dbtypes.AdjustableStringType): 
    3434    max_bytes = 8000 
    3535    variable = True 
     
    4343##                        % (sql, self, totype)) 
    4444 
    45 class NCHAR(dbtypes.StringType): 
     45class NCHAR(dbtypes.AdjustableStringType): 
    4646    synonyms=['WCHAR'] 
    4747    max_bytes = 4000 
    4848    variable = False 
    4949 
    50 class NVARCHAR(dbtypes.StringType): 
     50class NVARCHAR(dbtypes.AdjustableStringType): 
    5151    synonyms=['VARWCHAR'] 
    5252    max_bytes = 4000 
     
    5858# Use nvarchar(max), varchar(max), and varbinary(max) instead." 
    5959# http://msdn2.microsoft.com/en-us/library/ms187993.aspx 
    60 class NTEXT(dbtypes.StringType): 
     60class NTEXT(dbtypes.FrozenStringType): 
    6161    synonyms = ['LONGVARWCHAR'] 
    6262    bytes = max_bytes = ((2 ** 30) - 1) * 2 
    63     fixed_bytes = True 
    64  
    65 class TEXT(dbtypes.StringType): 
     63 
     64class TEXT(dbtypes.FrozenStringType): 
    6665    synonyms = ['LONGVARCHAR'] 
    6766    bytes = max_bytes = (2 ** 31) - 1 
    68     fixed_bytes = True 
    69  
    70 class IMAGE(dbtypes.StringType): 
     67 
     68class IMAGE(dbtypes.FrozenStringType): 
    7169    synonyms = ['LONGVARBINARY'] 
    7270    bytes = max_bytes = (2 ** 31) - 1 
    73     fixed_bytes = True 
    7471 
    7572 
     
    7774    synonyms=['BOOLEAN'] 
    7875    bytes = max_bytes = 1 
    79     fixed_bytes = True 
    8076    default_pytype = bool 
    8177    default_adapters = {bool: adapters.SQL92BIT} 
     
    9995                        } 
    10096 
    101  
    10297class SMALLDATETIME(dbtypes.DateTimeType): 
    10398    _min=datetime.datetime(1900, 1, 1) 
    10499    _max=datetime.datetime(2079, 6, 6) 
    105100 
     101 
    106102class FLOAT(dbtypes.InexactNumericType): 
    107103    synonyms = ['DOUBLE'] 
    108104    precision = max_precision = 53 
    109     fixed_precision = True 
    110105    default_adapters = {float: adapters.SQL92DOUBLE} 
    111106     
     
    121116 
    122117class REAL(dbtypes.InexactNumericType): 
    123     synonyms=['SINGLE'] 
     118    synonyms = ['SINGLE'] 
    124119    precision = max_precision = 24 
    125     fixed_precision = True 
    126120    default_adapters = {float: adapters.SQL92REAL} 
    127121     
     
    139133    synonyms = ['UNSIGNEDTINYINT'] 
    140134    bytes = max_bytes = 1 
    141     fixed_bytes = Tru
     135    signed = Fals
    142136    default_adapters = {int: adapters.SQL92SMALLINT, 
    143137                        long: adapters.SQL92SMALLINT, 
     
    146140class SMALLINT(dbtypes.IntegerType): 
    147141    bytes = max_bytes = 2 
    148     fixed_bytes = True 
    149142    default_adapters = {int: adapters.SQL92SMALLINT, 
    150143                        long: adapters.SQL92SMALLINT, 
     
    154147    synonyms = ['INTEGER', 'IDENTITY'] 
    155148    bytes = max_bytes = 4 
    156     fixed_bytes = True 
    157149 
    158150class BIGINT(dbtypes.IntegerType): 
    159151    """8-byte BIGINT type for SQL Server (2000+ only).""" 
    160152    bytes = max_bytes = 8 
    161     fixed_bytes = True 
    162153    default_pytype = long 
    163154 
     
    168159    default_adapters[datetime.timedelta] = ado.COM_timedelta 
    169160 
    170 class MoneyType(dbtypes.FixableByteType): 
     161class MoneyType(dbtypes.FrozenByteType): 
     162     
     163    _initargs = dbtypes.FrozenByteType._initargs + ("scale", "_min", "_max") 
    171164     
    172165    scale = 4 
     
    253246 
    254247 
    255  
    256248class SQLServerDecompiler(ado.ADOSQLDecompiler): 
    257249     
     
    326318    tableclass = SQLServerTable 
    327319     
     320    # See http://www.carlprothman.net/Technology/DataTypeMapping/tabid/97/Default.aspx 
     321    adotypes = { 
     322        # SQL Server              ADO Name 
     323        2: SMALLINT,        # SMALLINT 
     324        3: INT,             # INTEGER (also for IDENTITY (SQL Server 6.5)) 
     325        4: REAL,            # SINGLE 
     326        5: FLOAT,           # DOUBLE 
     327        6: MONEY,           # CURRENCY (also could be SMALLMONEY) 
     328        11: BIT,            # BOOLEAN 
     329        # 12: SQL_VARIANT,  # VARIANT (2000 +) 
     330        17: TINYINT,        # UNSIGNEDTINYINT 
     331        20: BIGINT,         # BIGINT 
     332        128: BINARY,        # BINARY (also could be TIMESTAMP) 
     333        129: CHAR,          # CHAR 
     334        130: NCHAR,         # WCHAR (7.0+) 
     335        131: NUMERIC,       # DECIMAL, NUMERIC 
     336        135: DATETIME,      # DBTIMESTAMP (also could be SMALLDATETIME) 
     337        200: VARCHAR,       # VARCHAR 
     338        201: TEXT,          # LONGVARCHAR 
     339        202: NVARCHAR,      # VARWCHAR 
     340        203: NTEXT,         # LONGVARWCHAR (7.0+) 
     341        204: VARBINARY,     # VARBINARY 
     342        205: IMAGE,         # LONGVARBINARY 
     343        # 0: EMPTY, 
     344        # 7: DATE, 8: BSTR, 9: IDISPATCH, 10: ERROR, 
     345        # 13: IUNKNOWN, 14: DECIMAL, 16: TINYINT, 
     346        # 18: UNSIGNEDSMALLINT, 19: UNSIGNEDINT, 21: UNSIGNEDBIGINT, 
     347        # 72: GUID, 
     348        # 132: USERDEFINED, 133: DBDATE, 134: DBTIME, 
     349    } 
     350     
     351    def _get_columns(self, tablename, conn=None): 
     352        # For some reason, adSchemaPrimaryKeys would only return a single 
     353        # record for a PK that had multiple columns. Use adSchemaIndexes. 
     354        # coldefs will be: 
     355        # [(u'TABLE_CATALOG', 202), (u'TABLE_SCHEMA', 202), (u'TABLE_NAME', 202), 
     356        # (u'INDEX_CATALOG', 202), (u'INDEX_SCHEMA', 202), (u'INDEX_NAME', 202), 
     357        # (u'PRIMARY_KEY', 11), (u'UNIQUE', 11), (u'CLUSTERED', 11), (u'TYPE', 18), 
     358        # (u'FILL_FACTOR', 3), (u'INITIAL_SIZE', 3), (u'NULLS', 3), 
     359        # (u'SORT_BOOKMARKS', 11), (u'AUTO_UPDATE', 11), (u'NULL_COLLATION', 3), 
     360        # (u'ORDINAL_POSITION', 19), (u'COLUMN_NAME', 202), (u'COLUMN_GUID', 72), 
     361        # (u'COLUMN_PROPID', 19), (u'COLLATION', 2), (u'CARDINALITY', 21), 
     362        # (u'PAGES', 3), (u'FILTER_CONDITION', 202), (u'INTEGRATED', 11)] 
     363        data, _ = self.db.fetch(ado.adSchemaIndexes, conn=conn, schema=True) 
     364        pknames = [row[17] for row in data 
     365                   if (tablename == row[2]) and row[6]] 
     366         
     367        # columns will be 
     368        # [(u'TABLE_CATALOG', 202), (u'TABLE_SCHEMA', 202), (u'TABLE_NAME', 202), 
     369        # (u'COLUMN_NAME', 202), (u'COLUMN_GUID', 72), (u'COLUMN_PROPID', 19), 
     370        # (u'ORDINAL_POSITION', 19), (u'COLUMN_HASDEFAULT', 11), 
     371        # (u'COLUMN_DEFAULT', 203), (u'COLUMN_FLAGS', 19), (u'IS_NULLABLE', 11), 
     372        # (u'DATA_TYPE', 18), (u'TYPE_GUID', 72), (u'CHARACTER_MAXIMUM_LENGTH', 19), 
     373        # (u'CHARACTER_OCTET_LENGTH', 19), (u'NUMERIC_PRECISION', 18), 
     374        # (u'NUMERIC_SCALE', 2), (u'DATETIME_PRECISION', 19), 
     375        # (u'CHARACTER_SET_CATALOG', 202), (u'CHARACTER_SET_SCHEMA', 202), 
     376        # (u'CHARACTER_SET_NAME', 202), (u'COLLATION_CATALOG', 202), 
     377        # (u'COLLATION_SCHEMA', 202), (u'COLLATION_NAME', 202), 
     378        # (u'DOMAIN_CATALOG', 202), (u'DOMAIN_SCHEMA', 202), 
     379        # (u'DOMAIN_NAME', 202), (u'DESCRIPTION', 203)] 
     380        data, _ = self.db.fetch(ado.adSchemaColumns, conn=conn, schema=True) 
     381         
     382        cols = [] 
     383        typer = self.db.adapterset 
     384        for row in data: 
     385            # I tried passing criteria to OpenSchema, but passing None is 
     386            # not the same as passing pythoncom.Empty (which errors). 
     387            if row[2] != tablename: 
     388                continue 
     389             
     390            dbtype = self.adotypes[row[11]]() 
     391            pytype = dbtype.default_pytype 
     392            if pytype is None: 
     393                raise TypeError("%r has no default pytype." % dbtype) 
     394             
     395            default = row[8] 
     396            if default is not None: 
     397                default = pytype(default) 
     398             
     399            name = str(row[3]) 
     400            c = objects.Column(pytype, dbtype, default, 
     401                               key=(name in pknames), 
     402                               name=name, qname=self.db.quote(name)) 
     403             
     404            colflags = int(row[9]) 
     405            if ((colflags & ado.DBCOLUMNFLAGS_ISFIXEDLENGTH) 
     406                and not (colflags & ado.DBCOLUMNFLAGS_WRITE)): 
     407                c.autoincrement = True 
     408             
     409            if dbtype in typer.known_types['int']: 
     410                dbtype.bytes = row[15] 
     411            elif dbtype in typer.known_types['float']: 
     412                dbtype.precision = row[15] 
     413                dbtype.scale = row[16] 
     414            elif dbtype in typer.known_types['numeric']: 
     415                dbtype.precision = row[15] 
     416                dbtype.scale = row[16] 
     417            elif (dbtype in typer.known_types['char'] or 
     418                  dbtype in typer.known_types['varchar']): 
     419                if row[13]: 
     420                    # row[13] will be a float 
     421                    dbtype.bytes = b = int(row[13]) 
     422             
     423            c.adapter = typer.default(pytype, dbtype) 
     424            cols.append(c) 
     425        return cols 
     426     
    328427    def create_database(self): 
    329428        conn = self.db.connections._get_conn(master=True) 
     
    337436        conn.Close() 
    338437        self.clear() 
    339      
    340     def column(self, pytype=unicode, dbtype=None, default=None, 
    341                key=False, autoincrement=False, hints=None): 
    342         """Return a Column object from the given arguments.""" 
    343         from geniusql import objects 
    344         col = objects.Column(pytype, dbtype, default, key) 
    345         col.autoincrement = autoincrement 
    346          
    347         typer = self.db.adapterset 
    348         if autoincrement: 
    349             col.dbtype = INT() 
    350         if col.dbtype is None: 
    351             col.dbtype = typer.database_type(pytype, hints or {}) 
    352         col.adapter = typer.default(pytype, col.dbtype) 
    353          
    354         return col 
    355438     
    356439    def columnclause(self, column): 
  • trunk/geniusql/test/test_msaccess.py

    r54 r55  
    1616                          "The MSAccess test will not be run.") 
    1717    else: 
    18          
    19         class CurrencyAdapter(ado.TypeAdapter_MSAccess): 
    20             """Stores Decimal and FixedPoint objects as CURRENCY.""" 
    21              
    22             def decimal_type(self, precision, scale): 
    23                 if precision == 0: 
    24                     precision = 19 
    25                 if scale > precision: 
    26                     scale = precision 
    27                 if scale > 4 or precision - scale > 15: 
    28                     return "TEXT" 
    29                 return "CURRENCY" 
    30              
    3118        DB_class = "access" 
    3219        opts = {'connections.Connect': 
     
    3724            import zoo_fixture 
    3825             
    39             def test_currency_dbtypes(obj): 
    40                 db = zoo_fixture.db 
    41                 fta = db.typeadapter.__class__.__name__ 
    42                 schema = zoo_fixture.schema 
    43                  
    44                 for c, p in [('Exhibit', 'Acreage'), ('Zoo', 'Admission')]: 
    45                     dbtype = schema[c][p].dbtype 
    46                     if fta == "CurrencyAdapter": 
    47                         if not (dbtype == "CURRENCY" or dbtype.startswith("WCHAR")): 
    48                             obj.fail("%s wrong type for %s.%s" % (dbtype, c, p)) 
    49                     else: 
    50                         if not (dbtype.startswith("NUMERIC") or dbtype.startswith("WCHAR")): 
    51                             obj.fail("%s wrong type for %s.%s" % (dbtype, c, p)) 
    52              
     26##            def test_currency_dbtypes(obj): 
     27##                db = zoo_fixture.db 
     28##                fta = db.adapterset.__class__.__name__ 
     29##                schema = zoo_fixture.schema 
     30##                 
     31##                for c, p in [('Exhibit', 'Acreage'), ('Zoo', 'Admission')]: 
     32##                    dbtype = schema[c][p].dbtype 
     33##                    if fta == "CurrencyAdapter": 
     34##                        if not (dbtype == "CURRENCY" or dbtype.startswith("WCHAR")): 
     35##                            obj.fail("%s wrong type for %s.%s" % (dbtype, c, p)) 
     36##                    else: 
     37##                        if not (dbtype.startswith("NUMERIC") or dbtype.startswith("WCHAR")): 
     38##                            obj.fail("%s wrong type for %s.%s" % (dbtype, c, p)) 
     39##             
    5340            # test the standard MS Access setup where Decimal and FixedPoint 
    5441            # objects are stored in the database as INTEGERS, LONGS or NUMERIC 
     
    5643            print 
    5744            print "Standard MSAccess test." 
    58             zoo_fixture.ZooTests.test_currency = test_currency_dbtypes 
     45##            zoo_fixture.ZooTests.test_currency = test_currency_dbtypes 
    5946            zoo_fixture.run(DB_class, "zoo.mdb", opts) 
    6047             
  • trunk/geniusql/typerefs.py

    r54 r55  
    2626    fixedpoint = None 
    2727 
    28  
     28try: 
     29    # Builtin in Python 2.4+ 
     30    set 
     31except NameError: 
     32    try: 
     33        # Module in Python 2.3 
     34        from sets import Set as set 
     35    except ImportError: 
     36        set = None