Changeset 233
- Timestamp:
- 07/20/06 23:55:22
- Files:
-
- trunk/storage/db.py (modified) (6 diffs)
- trunk/storage/dbmodel.py (modified) (5 diffs)
- trunk/storage/storeado.py (modified) (10 diffs)
- trunk/storage/storemysql.py (modified) (9 diffs)
- trunk/storage/storepypgsql.py (modified) (2 diffs)
- trunk/storage/storesqlite.py (modified) (8 diffs)
- trunk/test/test_storemsaccess.py (modified) (3 diffs)
- trunk/test/zoo_fixture.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/storage/db.py
r232 r233 45 45 fixedpoint = None 46 46 47 import sys48 for maxint_bytes in xrange(9):49 if sys.maxint <= 2 ** ((maxint_bytes * 8) - 1):50 break51 52 53 47 import threading 54 48 import warnings … … 58 52 from dejavu import logic, storage, LOGSQL, xray 59 53 from 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 = 5370 71 # Max decimal precision for NUMERIC columns (= 1000 for PostgreSQL 8).72 numeric_max_precision = 100073 74 # "The actual storage requirement is two bytes for each group of four75 # decimal digits, plus eight bytes overhead." Note we omit the overhead.76 numeric_max_bytes = 50077 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 type83 valuetype = prop.type84 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 dbtype98 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 decimal112 precision = int(prop.hints.get('precision', 0))113 if precision == 0:114 precision = self.float_max_precision115 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_precision122 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)" % bytes130 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_precision158 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_precision165 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 = precision170 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_precision182 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_precision189 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 = precision194 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 bytes206 return "BIGINT"207 else:208 # Anything larger than 8 bytes, use decimal/numeric.209 # For PostgreSQL, "The actual storage requirement is two bytes210 # for each group of four decimal digits, plus eight bytes211 # 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_bytes219 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_bytes226 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_bytes234 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_bytes241 242 return self.int_type(bytes)243 54 244 55 … … 301 112 302 113 use_asterisk_to_get_all = False 303 304 typeAdapter = FieldTypeAdapter()305 114 databaseclass = Database 306 115 … … 316 125 return item 317 126 318 adapter = get_option('Type Adapter')319 if adapter:320 self.typeAdapter = adapter321 322 127 adapter = get_option('Database Class') 323 128 if adapter: … … 328 133 self.db = self.databaseclass(name, **allOptions) 329 134 self.db.log = self.arena.log 135 136 adapter = get_option('Type Adapter') 137 if adapter: 138 self.db.typeadapter = adapter 330 139 331 140 def version(self): … … 692 501 693 502 def _make_column(self, cls, key): 694 dbtype = self.typeAdapter.coerce(cls, key)695 col = self.db.make_column(cls.__name__, key, dbtype)696 503 prop = getattr(cls, key) 504 col = self.db.make_column(cls.__name__, key) 697 505 col.default = prop.default 698 506 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 699 513 return col 700 514 trunk/storage/dbmodel.py
r232 r233 30 30 import pickle 31 31 import Queue 32 33 import sys 34 for maxint_bytes in xrange(9): 35 if sys.maxint <= 2 ** ((maxint_bytes * 8) - 1): 36 break 37 32 38 import time 33 39 from types import FunctionType … … 274 280 else: 275 281 return unicode(value, self.encoding) 282 283 284 class 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) 276 444 277 445 … … 915 1083 adaptertosql = AdapterToSQL() 916 1084 adapterfromdb = AdapterFromDB() 1085 typeadapter = TypeAdapter() 917 1086 918 1087 columnsetclass = ColumnSet … … 943 1112 raise TypeError("Database type %s could not be converted " 944 1113 "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) 945 1118 946 1119 def __setitem__(self, key, table): … … 1023 1196 return self.sql_name(columnkey) 1024 1197 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.""" 1026 1200 name = self.column_name(tablekey, columnkey) 1027 return Column(name, self.quote(name), dbtype)1201 return Column(name, self.quote(name), None) 1028 1202 1029 1203 def table_name(self, key): trunk/storage/storeado.py
r232 r233 589 589 adoconn = win32com.client.Dispatch(r'ADODB.Connection') 590 590 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.default597 col.hints = prop.hints.copy()598 if key in cls.identifiers:599 if isinstance(cls.sequencer, dejavu.UnitSequencerInteger):600 col.autoincrement = True601 col.default = cls.sequencer.initial602 return col603 591 604 592 … … 630 618 631 619 632 class FieldTypeAdapter_SQLServer(db.FieldTypeAdapter):620 class TypeAdapter_SQLServer(db.TypeAdapter): 633 621 634 622 # Hm. Docs say 38, but I can't seem to get more than 12 working. 635 623 numeric_max_precision = 12 636 624 637 def coerce_bool(self, c ls, key):625 def coerce_bool(self, col): 638 626 return "BIT" 639 627 640 def coerce_datetime_datetime(self, c ls, key):628 def coerce_datetime_datetime(self, col): 641 629 return "DATETIME" 642 630 643 def coerce_datetime_date(self, c ls, key):631 def coerce_datetime_date(self, col): 644 632 return "DATETIME" 645 633 646 def coerce_datetime_time(self, c ls, key):634 def coerce_datetime_time(self, col): 647 635 return "DATETIME" 648 636 649 def coerce_str(self, c ls, key):637 def coerce_str(self, col): 650 638 # 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')) 653 640 if bytes == 0: 654 641 # Okay, what the @#$%& is wrong with Redmond??!?! We can't even … … 701 688 columnsetclass = SQLServerColumnSet 702 689 adaptertosql = AdapterToADOSQL_SQLServer() 690 typeadapter = TypeAdapter_SQLServer() 703 691 704 692 def create_database(self): … … 751 739 class StorageManagerADO_SQLServer(StorageManagerADO): 752 740 753 typeAdapter = FieldTypeAdapter_SQLServer()754 741 databaseclass = SQLServerDatabase 755 742 … … 809 796 810 797 811 class FieldTypeAdapter_MSAccess(db.FieldTypeAdapter):798 class TypeAdapter_MSAccess(db.TypeAdapter): 812 799 813 800 # Hm. Docs say 28/38, but I can't seem to get more than 12 working. 814 801 numeric_max_precision = 12 815 802 816 def coerce_bool(self, c ls, key): return "BIT"817 818 def coerce_datetime_datetime(self, c ls, key): return "DATETIME"819 def coerce_datetime_date(self, c ls, key): return "DATETIME"820 def coerce_datetime_time(self, c ls, 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" 821 808 822 809 def int_type(self, bytes): … … 831 818 return "DECIMAL" 832 819 833 def coerce_str(self, c ls, key):820 def coerce_str(self, col): 834 821 # 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)) 837 823 if bytes and bytes <= 255: 838 824 # 255 chars is the upper limit for TEXT / VARCHAR in MS Access. … … 842 828 # in Access UI). But then, 1 GB is the limit for the whole DB. 843 829 # 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)850 830 return "MEMO" 851 831 … … 896 876 decompiler = ADOSQLDecompiler_MSAccess 897 877 adaptertosql = AdapterToADOSQL_MSAccess() 878 typeadapter = TypeAdapter_MSAccess() 879 898 880 columnsetclass = MSAccessColumnSet 899 881 … … 989 971 990 972 use_asterisk_to_get_all = True 991 992 typeAdapter = FieldTypeAdapter_MSAccess()993 973 databaseclass = MSAccessDatabase 994 974 … … 1024 1004 data, col_defs = self.db.fetch("SELECT @@IDENTITY;") 1025 1005 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 1027 1016 1028 1017 trunk/storage/storemysql.py
r232 r233 86 86 87 87 88 class FieldTypeAdapterMySQL(db.FieldTypeAdapter): 89 """Return the SQL typename of a DB column.""" 88 class TypeAdapterMySQL(db.TypeAdapter): 90 89 91 90 float_max_precision = 53 … … 102 101 return "FLOAT(%s)" % precision 103 102 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')) 107 105 if bytes: 108 106 # MySQL VARBINARY/BLOBs will do case-sensitive comparisons. … … 111 109 return "VARBINARY(%s)" % bytes 112 110 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" 117 112 elif bytes < 2 ** 24: 118 113 return "MEDIUMBLOB" 119 114 return "LONGBLOB" 120 115 121 def coerce_bool(self, c ls, key):116 def coerce_bool(self, col): 122 117 # We could use BOOLEAN, but it wasn't introduced until 4.1.0. 123 118 return "BOOL" 124 119 125 def coerce_datetime_datetime(self, c ls, key):120 def coerce_datetime_datetime(self, col): 126 121 return "DATETIME" 127 122 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')) 131 125 if bytes == 1: 132 126 return "BOOLEAN" … … 140 134 141 135 136 class 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 142 144 143 145 class MySQLIndexSet(db.IndexSet): … … 190 192 adaptertosql = AdapterToMySQL() 191 193 adapterfromdb = AdapterFromMySQL() 194 typeadapter = TypeAdapterMySQL() 192 195 193 196 columnsetclass = MySQLColumnSet … … 206 209 v = rowdata[0][0] 207 210 self._version = storage.Version(v) 211 212 # decompiler 208 213 if self._version > storage.Version("4.1.1"): 209 214 self.decompiler = MySQLDecompiler411 215 216 # type adapter 217 if self._version >= storage.Version("4.1"): 218 self.typeadapter = TypeAdapterMySQL41() 210 219 211 220 def version(self): … … 422 431 """StoreManager to save and retrieve Units via _mysql.""" 423 432 424 typeAdapter = FieldTypeAdapterMySQL()425 433 databaseclass = MySQLDatabase 426 434 … … 436 444 allOptions['name'] = connargs['db'] 437 445 db.StorageManagerDB.__init__(self, name, arena, allOptions) 438 self.typeAdapter.db = self.db439 446 440 447 def destroy(self, unit): … … 492 499 # Attach to self.db, which should call CREATE TABLE. 493 500 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.default500 col.hints = prop.hints.copy()501 502 if key in cls.identifiers:503 if isinstance(cls.sequencer, dejavu.UnitSequencerInteger):504 col.autoincrement = True505 col.default = cls.sequencer.initial506 return coltrunk/storage/storepypgsql.py
r232 r233 318 318 allOptions['name'] = v 319 319 db.StorageManagerDB.__init__(self, name, arena, allOptions) 320 # Stick a reference to self into the typeAdapter. See coerce_int.321 self.typeAdapter.sm = self322 320 323 321 def _seq_UnitSequencerInteger(self, unit): … … 347 345 data, col_defs = self.db.fetch("SELECT last_value FROM %s;" % seqname) 348 346 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 141 141 142 142 143 class FieldTypeAdapterSQLite(db.FieldTypeAdapter):144 """For a UnitProperty, return a database type.143 class TypeAdapterSQLite(db.TypeAdapter): 144 """For a column and Python type, return a database type. 145 145 146 146 From http://www.sqlite.org/datatype3.html: … … 164 164 numeric_max_bytes = 7 165 165 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 175 166 def float_type(self, precision): 176 167 """Return a datatype which can handle floats of the given binary precision.""" 177 168 return "REAL" 178 169 179 def coerce_str(self, c ls, key):170 def coerce_str(self, col): 180 171 # The bytes hint shall not reflect the usual 4-byte base for varchar. 181 172 return "TEXT" 182 173 183 def coerce_bool(self, c ls, key): return "INTEGER"184 185 def coerce_datetime_datetime(self, c ls, key): return "TEXT"186 def coerce_datetime_date(self, c ls, key): return "TEXT"187 def coerce_datetime_time(self, c ls, 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" 188 179 189 180 # I was seriously disinterested in writing a parser for interval. 190 def coerce_datetime_timedelta(self, c ls, key):191 return self.coerce_float(c ls, key)192 193 def coerce_decimal_Decimal(self, c ls, key):181 def coerce_datetime_timedelta(self, col): 182 return self.coerce_float(col) 183 184 def coerce_decimal_Decimal(self, col): 194 185 return "NUMERIC" 195 186 196 def coerce_fixedpoint_FixedPoint(self, c ls, key):187 def coerce_fixedpoint_FixedPoint(self, col): 197 188 return "NUMERIC" 198 189 … … 201 192 return "INTEGER" 202 193 194 195 class 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 "" 203 201 204 202 … … 348 346 adaptertosql = AdapterToSQLite() 349 347 adapterfromdb = AdapterFromSQLite() 348 typeadapter = TypeAdapterSQLite() 350 349 351 350 columnsetclass = SQLiteColumnSet 352 353 typeless = False354 351 355 352 def _get_tables(self, conn=None): … … 392 389 def python_type(self, dbtype): 393 390 """Return a Python type which can store values of the given dbtype.""" 394 if self.typeless:391 if isinstance(self.typeadapter, TypeAdapterSQLiteTypeless): 395 392 return str 396 393 … … 520 517 """StoreManager to save and retrieve Units via _sqlite.""" 521 518 522 typeAdapter = FieldTypeAdapterSQLite()523 519 databaseclass = SQLiteDatabase 524 520 … … 531 527 allOptions['mode'] = int(allOptions.pop('Mode', '0755'), 8) 532 528 db.StorageManagerDB.__init__(self, name, arena, allOptions) 533 self.typeAdapter.db = self.db534 529 535 530 def version(self): … … 641 636 def _make_column(self, cls, key): 642 637 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 647 641 return col 648 642 trunk/test/test_storemsaccess.py
r232 r233 19 19 else: 20 20 21 class CurrencyAdapter(storeado. FieldTypeAdapter_MSAccess):21 class CurrencyAdapter(storeado.TypeAdapter_MSAccess): 22 22 """Stores Decimal and FixedPoint objects as CURRENCY.""" 23 23 24 def coerce_decimal_Decimal(self, c ls, key):24 def coerce_decimal_Decimal(self, col): 25 25 return "CURRENCY" 26 26 27 def coerce_fixedpoint_FixedPoint(self, c ls, key):27 def coerce_fixedpoint_FixedPoint(self, col): 28 28 return "CURRENCY" 29 29 … … 42 42 def test_currency(obj): 43 43 sm = zoo_fixture.arena.stores['testSM'] 44 fta = sm. typeAdapter.__class__.__name__44 fta = sm.db.typeadapter.__class__.__name__ 45 45 46 46 for c, p in [('Exhibit', 'Acreage'), ('Zoo', 'Admission')]: … … 65 65 66 66 # plugin the adapter for testing CURRENCY columns 67 storeado. StorageManagerADO_MSAccess.typeAdapter = CurrencyAdapter()67 storeado.MSAccessDatabase.typeadapter = CurrencyAdapter() 68 68 69 69 reload(zoo_fixture) trunk/test/zoo_fixture.py
r232 r233 209 209 self.assertEqual(leopard.PreviousZoos, None) 210 210 box.memorize(leopard) 211 self.assertEqual(leopard.ID, 1) 212 211 213 leopard.add(WAP) 212 214 leopard.LastEscape = datetime.datetime(2004, 12, 21, 8, 15, 0) … … 859 861 import math 860 862 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_precision863 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 864 866 else: 865 867 num_prec_log2 = 9
