Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

I think I've seen this ORM somewhere before...

Changeset 233

Show
Ignore:
Timestamp:
07/20/06 23:55:22
Author:
fumanchu
Message:

Moved db.FieldTypeAdapter? to dbmodel.TypeAdapter?, so it no longer depends on any Unit logic.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/storage/db.py

    r232 r233  
    4545    fixedpoint = None 
    4646 
    47 import sys 
    48 for maxint_bytes in xrange(9): 
    49     if sys.maxint <= 2 ** ((maxint_bytes * 8) - 1): 
    50         break 
    51  
    52  
    5347import threading 
    5448import warnings 
     
    5852from dejavu import logic, storage, LOGSQL, xray 
    5953from dbmodel import * 
    60  
    61  
    62 class FieldTypeAdapter(object): 
    63     """For a UnitProperty, return a database type. 
    64      
    65     This base class is designed to work out-of-the-box with PostgreSQL 8. 
    66     """ 
    67      
    68     # Max binary precision for floating-point columns (= 53 for PostgreSQL 8). 
    69     float_max_precision = 53 
    70      
    71     # Max decimal precision for NUMERIC columns (= 1000 for PostgreSQL 8). 
    72     numeric_max_precision = 1000 
    73      
    74     # "The actual storage requirement is two bytes for each group of four 
    75     # decimal digits, plus eight bytes overhead." Note we omit the overhead. 
    76     numeric_max_bytes = 500 
    77      
    78     def coerce(self, cls, key): 
    79         """Return a column object for the given UnitProperty (cls and key).""" 
    80         prop = cls.property(key) 
    81          
    82         # Obtain the DB type 
    83         valuetype = prop.type 
    84         mod = valuetype.__module__ 
    85         if mod == "__builtin__": 
    86             xform = "coerce_%s" % valuetype.__name__ 
    87         else: 
    88             xform = "coerce_%s_%s" % (mod, valuetype.__name__) 
    89         xform = xform.replace(".", "_") 
    90         try: 
    91             xform = getattr(self, xform) 
    92         except AttributeError: 
    93             raise TypeError("'%s' is not handled by %s." % 
    94                             (valuetype, self.__class__)) 
    95         dbtype = xform(cls, key) 
    96          
    97         return dbtype 
    98      
    99     def float_type(self, precision): 
    100         """Return a datatype which can handle floats of the given binary precision.""" 
    101         if precision <= 24: 
    102             return "REAL" 
    103         else: 
    104             # Python floats are implemented using C doubles; 
    105             # actual precision depends on platform. 
    106             # PostgreSQL DOUBLE is 53 binary-digit precision. 
    107             return "DOUBLE PRECISION" 
    108      
    109     def coerce_float(self, cls, key): 
    110         prop = getattr(cls, key) 
    111         # Note that 'precision' is binary digits, not decimal 
    112         precision = int(prop.hints.get('precision', 0)) 
    113         if precision == 0: 
    114             precision = self.float_max_precision 
    115         elif precision > self.float_max_precision: 
    116             warnings.warn("Float precision %s > maximum %s for %s.%s, " 
    117                           "using %s. Values may be stored incorrectly." 
    118                           % (precision, self.float_max_precision, 
    119                              cls.__name__, key, self.__class__.__name__), 
    120                           dejavu.StorageWarning) 
    121             precision = self.float_max_precision 
    122         return self.float_type(precision) 
    123      
    124     def coerce_str(self, cls, key): 
    125         # The bytes hint shall not reflect the usual 4-byte base for varchar. 
    126         prop = getattr(cls, key) 
    127         bytes = int(prop.hints.get('bytes', 0)) 
    128         if bytes: 
    129             return "VARCHAR(%s)" % bytes 
    130         else: 
    131             # TEXT is not an SQL standard, but it's common. 
    132             return "TEXT" 
    133      
    134     def coerce_dict(self, cls, key): 
    135         return self.coerce_str(cls, key) 
    136     def coerce_list(self, cls, key): 
    137         return self.coerce_str(cls, key) 
    138     def coerce_tuple(self, cls, key): 
    139         return self.coerce_str(cls, key) 
    140     def coerce_unicode(self, cls, key): 
    141         return self.coerce_str(cls, key) 
    142      
    143     def coerce_bool(self, cls, key): return "BOOLEAN" 
    144      
    145     def coerce_datetime_datetime(self, cls, key): return "TIMESTAMP" 
    146     def coerce_datetime_date(self, cls, key): return "DATE" 
    147     def coerce_datetime_time(self, cls, key): return "TIME" 
    148      
    149     # I was seriously disinterested in writing a parser for interval. 
    150     def coerce_datetime_timedelta(self, cls, key): 
    151         return self.coerce_float(cls, key) 
    152      
    153     def coerce_decimal_Decimal(self, cls, key): 
    154         prop = getattr(cls, key) 
    155         precision = int(prop.hints.get('precision', decimal.getcontext().prec)) 
    156         if precision == 0: 
    157             precision = self.numeric_max_precision 
    158         elif precision > self.numeric_max_precision: 
    159             warnings.warn("Decimal precision %s > maximum %s for %s.%s, " 
    160                           "using %s. Values may be stored incorrectly." 
    161                           % (precision, self.numeric_max_precision, 
    162                              cls.__name__, key, self.__class__.__name__), 
    163                           dejavu.StorageWarning) 
    164             precision = self.numeric_max_precision 
    165          
    166         # Assume most people use decimal for money; default scale = 2. 
    167         scale = int(prop.hints.get('scale', 2)) 
    168         if scale > precision: 
    169             scale = precision 
    170         return "NUMERIC(%s, %s)" % (precision, scale) 
    171      
    172     def coerce_decimal(self, cls, key): 
    173         # If decimal ever becomes a builtin. Python 2.5? 
    174         return self.coerce_decimal_Decimal(cls, key) 
    175      
    176     def coerce_fixedpoint_FixedPoint(self, cls, key): 
    177         prop = getattr(cls, key) 
    178         # Note that fixedpoint has no theoretical precision limit. 
    179         precision = int(prop.hints.get('precision', 0)) 
    180         if precision == 0: 
    181             precision = self.numeric_max_precision 
    182         elif precision > self.numeric_max_precision: 
    183             warnings.warn("Fixedpoint precision %s > maximum %s for %s.%s, " 
    184                           "using %s. Values may be stored incorrectly." 
    185                           % (precision, self.numeric_max_precision, 
    186                              cls.__name__, key, self.__class__.__name__), 
    187                           dejavu.StorageWarning) 
    188             precision = self.numeric_max_precision 
    189          
    190         # Assume most people use fixedpoint for money; default scale = 2. 
    191         scale = int(prop.hints.get('scale', 2)) 
    192         if scale > precision: 
    193             scale = precision 
    194         return "NUMERIC(%s, %s)" % (precision, scale) 
    195      
    196     def int_type(self, bytes): 
    197         """Return a datatype which can handle the given number of bytes.""" 
    198         if bytes == 1: 
    199             return "BOOLEAN" 
    200         elif bytes == 2: 
    201             return "SMALLINT" 
    202         elif bytes <= 4: 
    203             return "INTEGER" 
    204         elif bytes <= 8: 
    205             # BIGINT is usually 8 bytes 
    206             return "BIGINT" 
    207         else: 
    208             # Anything larger than 8 bytes, use decimal/numeric. 
    209             # For PostgreSQL, "The actual storage requirement is two bytes 
    210             # for each group of four decimal digits, plus eight bytes 
    211             # overhead." Note we omit the overhead in our calculation. 
    212             return "NUMERIC(%s, 0)" % (bytes * 2) 
    213      
    214     def coerce_long(self, cls, key): 
    215         prop = getattr(cls, key) 
    216         bytes = int(prop.hints.get('bytes', self.numeric_max_precision)) 
    217         if bytes == 0: 
    218             bytes = self.numeric_max_bytes 
    219         elif bytes > self.numeric_max_bytes: 
    220             warnings.warn("Long bytes %s > maximum %s for %s.%s, " 
    221                           "using %s. Values may be stored incorrectly." 
    222                           % (bytes, self.numeric_max_bytes, 
    223                              cls.__name__, key, self.__class__.__name__), 
    224                           dejavu.StorageWarning) 
    225             bytes = self.numeric_max_bytes 
    226          
    227         return self.int_type(bytes) 
    228      
    229     def coerce_int(self, cls, key): 
    230         prop = getattr(cls, key) 
    231         bytes = int(prop.hints.get('bytes', maxint_bytes)) 
    232         if bytes == 0: 
    233             bytes = maxint_bytes 
    234         elif bytes > maxint_bytes: 
    235             warnings.warn("Integer bytes %s > maximum %s for %s.%s, " 
    236                           "using %s. Values may be stored incorrectly." 
    237                           % (bytes, maxint_bytes, 
    238                              cls.__name__, key, self.__class__.__name__), 
    239                           dejavu.StorageWarning) 
    240             bytes = maxint_bytes 
    241          
    242         return self.int_type(bytes) 
    24354 
    24455 
     
    301112     
    302113    use_asterisk_to_get_all = False 
    303      
    304     typeAdapter = FieldTypeAdapter() 
    305114    databaseclass = Database 
    306115     
     
    316125            return item 
    317126         
    318         adapter = get_option('Type Adapter') 
    319         if adapter: 
    320             self.typeAdapter = adapter 
    321          
    322127        adapter = get_option('Database Class') 
    323128        if adapter: 
     
    328133        self.db = self.databaseclass(name, **allOptions) 
    329134        self.db.log = self.arena.log 
     135         
     136        adapter = get_option('Type Adapter') 
     137        if adapter: 
     138            self.db.typeadapter = adapter 
    330139     
    331140    def version(self): 
     
    692501     
    693502    def _make_column(self, cls, key): 
    694         dbtype = self.typeAdapter.coerce(cls, key) 
    695         col = self.db.make_column(cls.__name__, key, dbtype) 
    696503        prop = getattr(cls, key) 
     504        col = self.db.make_column(cls.__name__, key) 
    697505        col.default = prop.default 
    698506        col.hints = prop.hints.copy() 
     507        col.dbtype = self.db.db_type(col, prop.type) 
     508         
     509        if key in cls.identifiers: 
     510            if isinstance(cls.sequencer, dejavu.UnitSequencerInteger): 
     511                col.autoincrement = True 
     512                col.default = cls.sequencer.initial 
    699513        return col 
    700514     
  • trunk/storage/dbmodel.py

    r232 r233  
    3030    import pickle 
    3131import Queue 
     32 
     33import sys 
     34for maxint_bytes in xrange(9): 
     35    if sys.maxint <= 2 ** ((maxint_bytes * 8) - 1): 
     36        break 
     37 
    3238import time 
    3339from types import FunctionType 
     
    274280        else: 
    275281            return unicode(value, self.encoding) 
     282 
     283 
     284class TypeAdapter(object): 
     285    """Determine the best database type for a given column + Python type. 
     286     
     287    This base class is designed to work out-of-the-box with PostgreSQL 8. 
     288    """ 
     289     
     290    # Max binary precision for floating-point columns (= 53 for PostgreSQL 8). 
     291    float_max_precision = 53 
     292     
     293    # Max decimal precision for NUMERIC columns (= 1000 for PostgreSQL 8). 
     294    numeric_max_precision = 1000 
     295     
     296    # "The actual storage requirement is two bytes for each group of four 
     297    # decimal digits, plus eight bytes overhead." Note we omit the overhead. 
     298    numeric_max_bytes = 500 
     299     
     300    def coerce(self, col, pytype): 
     301        """Return a database type for the given column object and Python type.""" 
     302        mod = pytype.__module__ 
     303        if mod == "__builtin__": 
     304            xform = "coerce_%s" % pytype.__name__ 
     305        else: 
     306            xform = "coerce_%s_%s" % (mod, pytype.__name__) 
     307        xform = xform.replace(".", "_") 
     308        try: 
     309            xform = getattr(self, xform) 
     310        except AttributeError: 
     311            raise TypeError("'%s' is not handled by %s." % 
     312                            (pytype, self.__class__)) 
     313        return xform(col) 
     314     
     315    def float_type(self, precision): 
     316        """Return a datatype which can handle floats of the given binary precision.""" 
     317        if precision <= 24: 
     318            return "REAL" 
     319        else: 
     320            # Python floats are implemented using C doubles; 
     321            # actual precision depends on platform. 
     322            # PostgreSQL DOUBLE is 53 binary-digit precision. 
     323            return "DOUBLE PRECISION" 
     324     
     325    def coerce_float(self, col): 
     326        # Note that 'precision' is binary digits, not decimal 
     327        precision = int(col.hints.get('precision', 0)) 
     328        if precision == 0: 
     329            precision = self.float_max_precision 
     330        elif precision > self.float_max_precision: 
     331            warnings.warn("Float precision %s > maximum %s for %s, " 
     332                          "using %s. Values may be stored incorrectly." 
     333                          % (precision, self.float_max_precision, 
     334                             col.name, self.__class__.__name__), 
     335                          dejavu.StorageWarning) 
     336            precision = self.float_max_precision 
     337        return self.float_type(precision) 
     338     
     339    def coerce_str(self, col): 
     340        # The bytes hint shall not reflect the usual 4-byte base for varchar. 
     341        bytes = int(col.hints.get('bytes', 0)) 
     342        if bytes: 
     343            return "VARCHAR(%s)" % bytes 
     344        else: 
     345            # TEXT is not an SQL standard, but it's common. 
     346            return "TEXT" 
     347     
     348    def coerce_dict(self, col): 
     349        return self.coerce_str(col) 
     350    def coerce_list(self, col): 
     351        return self.coerce_str(col) 
     352    def coerce_tuple(self, col): 
     353        return self.coerce_str(col) 
     354    def coerce_unicode(self, col): 
     355        return self.coerce_str(col) 
     356     
     357    def coerce_bool(self, col): return "BOOLEAN" 
     358     
     359    def coerce_datetime_datetime(self, col): return "TIMESTAMP" 
     360    def coerce_datetime_date(self, col): return "DATE" 
     361    def coerce_datetime_time(self, col): return "TIME" 
     362     
     363    # I was seriously disinterested in writing a parser for interval. 
     364    def coerce_datetime_timedelta(self, col): 
     365        return self.coerce_float(col) 
     366     
     367    def decimal_type(self, colname, precision, scale): 
     368        if precision == 0: 
     369            precision = self.numeric_max_precision 
     370        elif precision > self.numeric_max_precision: 
     371            warnings.warn("Decimal precision %s > maximum %s for %s, " 
     372                          "using %s. Values may be stored incorrectly." 
     373                          % (precision, self.numeric_max_precision, 
     374                             colname, self.__class__.__name__), 
     375                          dejavu.StorageWarning) 
     376            precision = self.numeric_max_precision 
     377         
     378        if scale > precision: 
     379            scale = precision 
     380        return "NUMERIC(%s, %s)" % (precision, scale) 
     381     
     382    def coerce_decimal_Decimal(self, col): 
     383        precision = int(col.hints.get('precision', decimal.getcontext().prec)) 
     384        # Assume most people use decimal for money; default scale = 2. 
     385        scale = int(col.hints.get('scale', 2)) 
     386        return self.decimal_type(col.name, precision, scale) 
     387     
     388    def coerce_decimal(self, col): 
     389        # If decimal ever becomes a builtin. Python 2.5? 
     390        return self.coerce_decimal_Decimal(col) 
     391     
     392    def coerce_fixedpoint_FixedPoint(self, col): 
     393        # Note that fixedpoint has no theoretical precision limit. 
     394        precision = int(col.hints.get('precision', 0)) 
     395        # Assume most people use fixedpoint for money; default scale = 2. 
     396        scale = int(col.hints.get('scale', 2)) 
     397        return self.decimal_type(col.name, precision, scale) 
     398     
     399    def int_type(self, bytes): 
     400        """Return a datatype which can handle the given number of bytes.""" 
     401        if bytes == 1: 
     402            return "BOOLEAN" 
     403        elif bytes == 2: 
     404            return "SMALLINT" 
     405        elif bytes <= 4: 
     406            return "INTEGER" 
     407        elif bytes <= 8: 
     408            # BIGINT is usually 8 bytes 
     409            return "BIGINT" 
     410        else: 
     411            # Anything larger than 8 bytes, use decimal/numeric. 
     412            # For PostgreSQL, "The actual storage requirement is two bytes 
     413            # for each group of four decimal digits, plus eight bytes 
     414            # overhead." Note we omit the overhead in our calculation. 
     415            return "NUMERIC(%s, 0)" % (bytes * 2) 
     416     
     417    def coerce_long(self, col): 
     418        bytes = int(col.hints.get('bytes', self.numeric_max_precision)) 
     419        if bytes == 0: 
     420            bytes = self.numeric_max_bytes 
     421        elif bytes > self.numeric_max_bytes: 
     422            warnings.warn("Long bytes %s > maximum %s for %s, " 
     423                          "using %s. Values may be stored incorrectly." 
     424                          % (bytes, self.numeric_max_bytes, 
     425                             col.name, self.__class__.__name__), 
     426                          dejavu.StorageWarning) 
     427            bytes = self.numeric_max_bytes 
     428         
     429        return self.int_type(bytes) 
     430     
     431    def coerce_int(self, col): 
     432        bytes = int(col.hints.get('bytes', maxint_bytes)) 
     433        if bytes == 0: 
     434            bytes = maxint_bytes 
     435        elif bytes > maxint_bytes: 
     436            warnings.warn("Integer bytes %s > maximum %s for %s, " 
     437                          "using %s. Values may be stored incorrectly." 
     438                          % (bytes, maxint_bytes, 
     439                             col.name, self.__class__.__name__), 
     440                          dejavu.StorageWarning) 
     441            bytes = maxint_bytes 
     442         
     443        return self.int_type(bytes) 
    276444 
    277445 
     
    9151083    adaptertosql = AdapterToSQL() 
    9161084    adapterfromdb = AdapterFromDB() 
     1085    typeadapter = TypeAdapter() 
    9171086     
    9181087    columnsetclass = ColumnSet 
     
    9431112        raise TypeError("Database type %s could not be converted " 
    9441113                        "to a Python type." % repr(dbtype)) 
     1114     
     1115    def db_type(self, col, pytype): 
     1116        """Return a database type which can store values of the given pytype.""" 
     1117        return self.typeadapter.coerce(col, pytype) 
    9451118     
    9461119    def __setitem__(self, key, table): 
     
    10231196        return self.sql_name(columnkey) 
    10241197     
    1025     def make_column(self, tablekey, columnkey, dbtype): 
     1198    def make_column(self, tablekey, columnkey): 
     1199        """Return a Column object from the given table and column keys.""" 
    10261200        name = self.column_name(tablekey, columnkey) 
    1027         return Column(name, self.quote(name), dbtype) 
     1201        return Column(name, self.quote(name), None) 
    10281202     
    10291203    def table_name(self, key): 
  • trunk/storage/storeado.py

    r232 r233  
    589589        adoconn = win32com.client.Dispatch(r'ADODB.Connection') 
    590590        return "ADO Version: %s" % adoconn.Version 
    591      
    592     def _make_column(self, cls, key): 
    593         dbtype = self.typeAdapter.coerce(cls, key) 
    594         col = self.db.make_column(cls.__name__, key, dbtype) 
    595         prop = getattr(cls, key) 
    596         col.default = prop.default 
    597         col.hints = prop.hints.copy() 
    598         if key in cls.identifiers: 
    599             if isinstance(cls.sequencer, dejavu.UnitSequencerInteger): 
    600                 col.autoincrement = True 
    601                 col.default = cls.sequencer.initial 
    602         return col 
    603591 
    604592 
     
    630618 
    631619 
    632 class FieldTypeAdapter_SQLServer(db.FieldTypeAdapter): 
     620class TypeAdapter_SQLServer(db.TypeAdapter): 
    633621     
    634622    # Hm. Docs say 38, but I can't seem to get more than 12 working. 
    635623    numeric_max_precision = 12 
    636624     
    637     def coerce_bool(self, cls, key): 
     625    def coerce_bool(self, col): 
    638626        return "BIT" 
    639627     
    640     def coerce_datetime_datetime(self, cls, key): 
     628    def coerce_datetime_datetime(self, col): 
    641629        return "DATETIME" 
    642630     
    643     def coerce_datetime_date(self, cls, key): 
     631    def coerce_datetime_date(self, col): 
    644632        return "DATETIME" 
    645633     
    646     def coerce_datetime_time(self, cls, key): 
     634    def coerce_datetime_time(self, col): 
    647635        return "DATETIME" 
    648636     
    649     def coerce_str(self, cls, key): 
     637    def coerce_str(self, col): 
    650638        # The bytes hint does not reflect the usual 4-byte base for varchar. 
    651         prop = getattr(cls, key) 
    652         bytes = int(prop.hints.get('bytes', '0')) 
     639        bytes = int(col.hints.get('bytes', '0')) 
    653640        if bytes == 0: 
    654641            # Okay, what the @#$%& is wrong with Redmond??!?! We can't even 
     
    701688    columnsetclass = SQLServerColumnSet 
    702689    adaptertosql = AdapterToADOSQL_SQLServer() 
     690    typeadapter = TypeAdapter_SQLServer() 
    703691     
    704692    def create_database(self): 
     
    751739class StorageManagerADO_SQLServer(StorageManagerADO): 
    752740     
    753     typeAdapter = FieldTypeAdapter_SQLServer() 
    754741    databaseclass = SQLServerDatabase 
    755742     
     
    809796 
    810797 
    811 class FieldTypeAdapter_MSAccess(db.FieldTypeAdapter): 
     798class TypeAdapter_MSAccess(db.TypeAdapter): 
    812799     
    813800    # Hm. Docs say 28/38, but I can't seem to get more than 12 working. 
    814801    numeric_max_precision = 12 
    815802     
    816     def coerce_bool(self, cls, key): return "BIT" 
    817      
    818     def coerce_datetime_datetime(self, cls, key): return "DATETIME" 
    819     def coerce_datetime_date(self, cls, key): return "DATETIME" 
    820     def coerce_datetime_time(self, cls, key): return "DATETIME" 
     803    def coerce_bool(self, col): return "BIT" 
     804     
     805    def coerce_datetime_datetime(self, col): return "DATETIME" 
     806    def coerce_datetime_date(self, col): return "DATETIME" 
     807    def coerce_datetime_time(self, col): return "DATETIME" 
    821808     
    822809    def int_type(self, bytes): 
     
    831818            return "DECIMAL" 
    832819     
    833     def coerce_str(self, cls, key): 
     820    def coerce_str(self, col): 
    834821        # The bytes hint shall not reflect the usual 4-byte base for varchar. 
    835         prop = getattr(cls, key) 
    836         bytes = int(prop.hints.get('bytes', 0)) 
     822        bytes = int(col.hints.get('bytes', 0)) 
    837823        if bytes and bytes <= 255: 
    838824            # 255 chars is the upper limit for TEXT / VARCHAR in MS Access. 
     
    842828            # in Access UI). But then, 1 GB is the limit for the whole DB. 
    843829            # Note that OpenSchema will return a DATA_TYPE of "WCHAR". 
    844             for assoc in cls._associations.itervalues(): 
    845                 if assoc.nearKey == key: 
    846                     warnings.warn("Memo fields cannot be used as join keys. " 
    847                                   "You should set %s.%s(hints={'bytes': 255})" 
    848                                   % (cls.__name__, key), 
    849                                   dejavu.StorageWarning) 
    850830            return "MEMO" 
    851831 
     
    896876    decompiler = ADOSQLDecompiler_MSAccess 
    897877    adaptertosql = AdapterToADOSQL_MSAccess() 
     878    typeadapter = TypeAdapter_MSAccess() 
     879     
    898880    columnsetclass = MSAccessColumnSet 
    899881     
     
    989971     
    990972    use_asterisk_to_get_all = True 
    991      
    992     typeAdapter = FieldTypeAdapter_MSAccess() 
    993973    databaseclass = MSAccessDatabase 
    994974     
     
    10241004        data, col_defs = self.db.fetch("SELECT @@IDENTITY;") 
    10251005        setattr(unit, cls.identifiers[0], data[0][0]) 
    1026  
     1006     
     1007    def _make_column(self, cls, key): 
     1008        col = StorageManagerADO._make_column(self, cls, key) 
     1009        if col.dbtype == "MEMO": 
     1010            for assoc in cls._associations.itervalues(): 
     1011                if assoc.nearKey == key: 
     1012                    warnings.warn("Memo fields cannot be used as join keys. " 
     1013                                  "You should set %s.%s(hints={'bytes': 255})" 
     1014                                  % (cls.__name__, key), dejavu.StorageWarning) 
     1015        return col 
    10271016 
    10281017 
  • trunk/storage/storemysql.py

    r232 r233  
    8686 
    8787 
    88 class FieldTypeAdapterMySQL(db.FieldTypeAdapter): 
    89     """Return the SQL typename of a DB column.""" 
     88class TypeAdapterMySQL(db.TypeAdapter): 
    9089     
    9190    float_max_precision = 53 
     
    102101        return "FLOAT(%s)" % precision 
    103102     
    104     def coerce_str(self, cls, key): 
    105         prop = getattr(cls, key) 
    106         bytes = int(prop.hints.get('bytes', '0')) 
     103    def coerce_str(self, col): 
     104        bytes = int(col.hints.get('bytes', '0')) 
    107105        if bytes: 
    108106            # MySQL VARBINARY/BLOBs will do case-sensitive comparisons. 
     
    111109                return "VARBINARY(%s)" % bytes 
    112110            elif bytes < 2 ** 16: 
    113                 if self.db._version >= storage.Version("4.1"): 
    114                     return "BLOB(%s)" % bytes 
    115                 else: 
    116                     return "BLOB" 
     111                return "BLOB" 
    117112            elif bytes < 2 ** 24: 
    118113                return "MEDIUMBLOB" 
    119114        return "LONGBLOB" 
    120115     
    121     def coerce_bool(self, cls, key): 
     116    def coerce_bool(self, col): 
    122117        # We could use BOOLEAN, but it wasn't introduced until 4.1.0. 
    123118        return "BOOL" 
    124119     
    125     def coerce_datetime_datetime(self, cls, key): 
     120    def coerce_datetime_datetime(self, col): 
    126121        return "DATETIME" 
    127122     
    128     def coerce_int(self, cls, key): 
    129         prop = getattr(cls, key) 
    130         bytes = int(prop.hints.get('bytes', '4')) 
     123    def coerce_int(self, col): 
     124        bytes = int(col.hints.get('bytes', '4')) 
    131125        if bytes == 1: 
    132126            return "BOOLEAN" 
     
    140134 
    141135 
     136class TypeAdapterMySQL41(TypeAdapterMySQL): 
     137     
     138    def coerce_str(self, col): 
     139        dbtype = TypeAdapterMySQL.coerce_str(self, col) 
     140        if dbtype == "BLOB": 
     141            dbtype = "BLOB(%s)" % col.hints['bytes'] 
     142        return dbtype 
     143 
    142144 
    143145class MySQLIndexSet(db.IndexSet): 
     
    190192    adaptertosql = AdapterToMySQL() 
    191193    adapterfromdb = AdapterFromMySQL() 
     194    typeadapter = TypeAdapterMySQL() 
    192195     
    193196    columnsetclass = MySQLColumnSet 
     
    206209        v = rowdata[0][0] 
    207210        self._version = storage.Version(v) 
     211         
     212        # decompiler 
    208213        if self._version > storage.Version("4.1.1"): 
    209214            self.decompiler = MySQLDecompiler411 
     215         
     216        # type adapter 
     217        if self._version >= storage.Version("4.1"): 
     218            self.typeadapter = TypeAdapterMySQL41() 
    210219     
    211220    def version(self): 
     
    422431    """StoreManager to save and retrieve Units via _mysql.""" 
    423432     
    424     typeAdapter = FieldTypeAdapterMySQL() 
    425433    databaseclass = MySQLDatabase 
    426434     
     
    436444        allOptions['name'] = connargs['db'] 
    437445        db.StorageManagerDB.__init__(self, name, arena, allOptions) 
    438         self.typeAdapter.db = self.db 
    439446     
    440447    def destroy(self, unit): 
     
    492499        # Attach to self.db, which should call CREATE TABLE. 
    493500        self.db[cls.__name__] = t 
    494      
    495     def _make_column(self, cls, key): 
    496         dbtype = self.typeAdapter.coerce(cls, key) 
    497         col = self.db.make_column(cls.__name__, key, dbtype) 
    498         prop = getattr(cls, key) 
    499         col.default = prop.default 
    500         col.hints = prop.hints.copy() 
    501          
    502         if key in cls.identifiers: 
    503             if isinstance(cls.sequencer, dejavu.UnitSequencerInteger): 
    504                 col.autoincrement = True 
    505                 col.default = cls.sequencer.initial 
    506         return col 
  • trunk/storage/storepypgsql.py

    r232 r233  
    318318                allOptions['name'] = v 
    319319        db.StorageManagerDB.__init__(self, name, arena, allOptions) 
    320         # Stick a reference to self into the typeAdapter. See coerce_int. 
    321         self.typeAdapter.sm = self 
    322320     
    323321    def _seq_UnitSequencerInteger(self, unit): 
     
    347345        data, col_defs = self.db.fetch("SELECT last_value FROM %s;" % seqname) 
    348346        setattr(unit, idcol, data[0][0]) 
    349      
    350     #                               Schemas                               # 
    351      
    352     def _make_column(self, cls, key): 
    353         dbtype = self.typeAdapter.coerce(cls, key) 
    354          
    355         col = self.db.make_column(cls.__name__, key, dbtype) 
    356         prop = getattr(cls, key) 
    357          
    358         # Here's where we differ from the superclass: 
    359         # we have to manually CREATE SEQUENCE, 
    360         # and use class attributes to do so. 
    361         if key in cls.identifiers: 
    362             if isinstance(cls.sequencer, dejavu.UnitSequencerInteger): 
    363                 col.autoincrement = True 
    364                 col.default = cls.sequencer.initial 
    365         else: 
    366             col.default = prop.default 
    367          
    368         col.hints = prop.hints.copy() 
    369         return col 
    370  
     347 
  • trunk/storage/storesqlite.py

    r232 r233  
    141141 
    142142 
    143 class FieldTypeAdapterSQLite(db.FieldTypeAdapter): 
    144     """For a UnitProperty, return a database type. 
     143class TypeAdapterSQLite(db.TypeAdapter): 
     144    """For a column and Python type, return a database type. 
    145145     
    146146    From http://www.sqlite.org/datatype3.html: 
     
    164164    numeric_max_bytes = 7 
    165165     
    166     def coerce(self, cls, key): 
    167         """coerce(cls, key) -> SQL typename for valuetype.""" 
    168         if self.db.typeless: 
    169             # SQLite can be 'typeless' (but we lose the type introspection) 
    170             # This will be returned inside _get_columns as NUMERIC. 
    171             return "" 
    172          
    173         return db.FieldTypeAdapter.coerce(self, cls, key) 
    174      
    175166    def float_type(self, precision): 
    176167        """Return a datatype which can handle floats of the given binary precision.""" 
    177168        return "REAL" 
    178169     
    179     def coerce_str(self, cls, key): 
     170    def coerce_str(self, col): 
    180171        # The bytes hint shall not reflect the usual 4-byte base for varchar. 
    181172        return "TEXT" 
    182173     
    183     def coerce_bool(self, cls, key): return "INTEGER" 
    184      
    185     def coerce_datetime_datetime(self, cls, key): return "TEXT" 
    186     def coerce_datetime_date(self, cls, key): return "TEXT" 
    187     def coerce_datetime_time(self, cls, key): return "TEXT" 
     174    def coerce_bool(self, col): return "INTEGER" 
     175     
     176    def coerce_datetime_datetime(self, col): return "TEXT" 
     177    def coerce_datetime_date(self, col): return "TEXT" 
     178    def coerce_datetime_time(self, col): return "TEXT" 
    188179     
    189180    # I was seriously disinterested in writing a parser for interval. 
    190     def coerce_datetime_timedelta(self, cls, key): 
    191         return self.coerce_float(cls, key
    192      
    193     def coerce_decimal_Decimal(self, cls, key): 
     181    def coerce_datetime_timedelta(self, col): 
     182        return self.coerce_float(col
     183     
     184    def coerce_decimal_Decimal(self, col): 
    194185        return "NUMERIC" 
    195186     
    196     def coerce_fixedpoint_FixedPoint(self, cls, key): 
     187    def coerce_fixedpoint_FixedPoint(self, col): 
    197188        return "NUMERIC" 
    198189     
     
    201192        return "INTEGER" 
    202193 
     194 
     195class TypeAdapterSQLiteTypeless(db.TypeAdapter): 
     196     
     197    def coerce(self, col, pytype): 
     198        # SQLite can be 'typeless' (but we lose the type introspection) 
     199        # This will be returned inside _get_columns as NUMERIC. 
     200        return "" 
    203201 
    204202 
     
    348346    adaptertosql = AdapterToSQLite() 
    349347    adapterfromdb = AdapterFromSQLite() 
     348    typeadapter = TypeAdapterSQLite() 
    350349     
    351350    columnsetclass = SQLiteColumnSet 
    352      
    353     typeless = False 
    354351     
    355352    def _get_tables(self, conn=None): 
     
    392389    def python_type(self, dbtype): 
    393390        """Return a Python type which can store values of the given dbtype.""" 
    394         if self.typeless
     391        if isinstance(self.typeadapter, TypeAdapterSQLiteTypeless)
    395392            return str 
    396393         
     
    520517    """StoreManager to save and retrieve Units via _sqlite.""" 
    521518     
    522     typeAdapter = FieldTypeAdapterSQLite() 
    523519    databaseclass = SQLiteDatabase 
    524520     
     
    531527        allOptions['mode'] = int(allOptions.pop('Mode', '0755'), 8) 
    532528        db.StorageManagerDB.__init__(self, name, arena, allOptions) 
    533         self.typeAdapter.db = self.db 
    534529     
    535530    def version(self): 
     
    641636    def _make_column(self, cls, key): 
    642637        col = db.StorageManagerDB._make_column(self, cls, key) 
    643         if (_autoincrement_support and key in cls.identifiers and 
    644                 isinstance(cls.sequencer, dejavu.UnitSequencerInteger)): 
    645             col.autoincrement = True 
    646             col.default = cls.sequencer.initial 
     638        if col.autoincrement and not _autoincrement_support: 
     639            col.autoincrement = False 
     640            col.default = None 
    647641        return col 
    648642 
  • trunk/test/test_storemsaccess.py

    r232 r233  
    1919    else: 
    2020         
    21         class CurrencyAdapter(storeado.FieldTypeAdapter_MSAccess): 
     21        class CurrencyAdapter(storeado.TypeAdapter_MSAccess): 
    2222            """Stores Decimal and FixedPoint objects as CURRENCY.""" 
    2323             
    24             def coerce_decimal_Decimal(self, cls, key): 
     24            def coerce_decimal_Decimal(self, col): 
    2525                return "CURRENCY" 
    2626 
    27             def coerce_fixedpoint_FixedPoint(self, cls, key): 
     27            def coerce_fixedpoint_FixedPoint(self, col): 
    2828                return "CURRENCY" 
    2929             
     
    4242            def test_currency(obj): 
    4343                sm = zoo_fixture.arena.stores['testSM'] 
    44                 fta = sm.typeAdapter.__class__.__name__ 
     44                fta = sm.db.typeadapter.__class__.__name__ 
    4545                 
    4646                for c, p in [('Exhibit', 'Acreage'), ('Zoo', 'Admission')]: 
     
    6565             
    6666            # plugin the adapter for testing CURRENCY columns 
    67             storeado.StorageManagerADO_MSAccess.typeAdapter = CurrencyAdapter() 
     67            storeado.MSAccessDatabase.typeadapter = CurrencyAdapter() 
    6868             
    6969            reload(zoo_fixture) 
  • trunk/test/zoo_fixture.py

    r232 r233  
    209209        self.assertEqual(leopard.PreviousZoos, None) 
    210210        box.memorize(leopard) 
     211        self.assertEqual(leopard.ID, 1) 
     212         
    211213        leopard.add(WAP) 
    212214        leopard.LastEscape = datetime.datetime(2004, 12, 21, 8, 15, 0) 
     
    859861        import math 
    860862        sm = arena.stores['testSM'] 
    861         if hasattr(sm, 'typeAdapter'): 
    862             num_prec_log2 = int(math.log(sm.typeAdapter.numeric_max_precision, 2)) 
    863             float_prec = sm.typeAdapter.float_max_precision 
     863        if hasattr(sm, 'db'): 
     864            num_prec_log2 = int(math.log(sm.db.typeadapter.numeric_max_precision, 2)) 
     865            float_prec = sm.db.typeadapter.float_max_precision 
    864866        else: 
    865867            num_prec_log2 = 9