Changeset 191
- Timestamp:
- 03/10/06 16:35:42
- Files:
-
- trunk/__init__.py (modified) (1 diff)
- trunk/doc/advanced.html (modified) (2 diffs)
- trunk/storage/db.py (modified) (23 diffs)
- trunk/storage/sockets.py (modified) (2 diffs)
- trunk/storage/storeado.py (modified) (2 diffs)
- trunk/storage/storemysql.py (modified) (2 diffs)
- trunk/storage/storepypgsql.py (modified) (2 diffs)
- trunk/storage/storesqlite.py (modified) (2 diffs)
- trunk/units.py (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/__init__.py
r190 r191 33 33 """ 34 34 35 __version__ = "1.4 beta"35 __version__ = "1.4.0" 36 36 37 37 trunk/doc/advanced.html
r190 r191 257 257 <tt>threading.Lock</tt>.</li> 258 258 <li><b>save</b>: If <tt>not unit.dirty()</tt>, you can exit. Otherwise, 259 iterate through the Unit's properties ()and persist each value,259 iterate through the Unit's properties and persist each value, 260 260 using an Adapter to coerce each value to the type expected by your 261 261 database (or not, it's up to you; storeado special-cases … … 272 272 than to make a separate call for each row.</li> 273 273 <li><b>create_storage</b>: <tt>CREATE TABLE</tt> using 274 <tt>Unit.properties ()</tt>, especially the <tt>UnitProperty.type</tt>274 <tt>Unit.properties</tt>, especially the <tt>UnitProperty.type</tt> 275 275 attribute. If <tt>UnitProperty.index == True</tt>, make a 276 276 <tt>CREATE INDEX</tt> call.</li> trunk/storage/db.py
r190 r191 1 1 """Base classes and tools for writing database Storage Managers. 2 3 Unit type -> [SQL repr ->] DB -> incoming Python value -> Unit type4 5 2 6 3 DATA TYPES 7 4 ========== 5 Database Storage Manager modules are mostly adapters to support round-trip 6 data coercion: 7 8 Unit type -> [SQL repr ->] DB -> incoming Python value -> Unit type 9 8 10 Since Dejavu relies on external database servers for its persistence, 9 11 Python datatypes must be converted to column types in the DB. When writing 10 12 a StorageManager, you should make sure that your type conversions can handle 11 at least the following limitations .If possible, implement the type with no13 at least the following limitations: If possible, implement the type with no 12 14 limits. Also, follow UnitProperty.hints['bytes'] where possible. A value 13 15 of zero for hints['bytes'] implies no limit. If no value is given, try to … … 61 63 62 64 63 def getCoerceName(value, valuetype=None): 65 def getCoerceName(valuetype): 66 """Return the name of the coercion method for a given type.""" 64 67 mod = valuetype.__module__ 65 68 if mod == "__builtin__": … … 71 74 72 75 def getCoerceMethod(obj, value, valuetype=None): 76 """Return the coercion method for a given value [or type].""" 73 77 if valuetype is None: 74 78 valuetype = type(value) 75 79 76 meth = getCoerceName(value , valuetype)80 meth = getCoerceName(valuetype) 77 81 if hasattr(obj, meth): 78 82 return getattr(obj, meth) … … 80 84 methods = [] 81 85 for base in valuetype.__bases__: 82 meth = getCoerceName( value,base)86 meth = getCoerceName(base) 83 87 methods.append(meth) 84 88 if hasattr(obj, meth): … … 322 326 make decisions about coercion when they don't have control over 323 327 the types of database columns, and must make do with legacy 324 database simplementations.328 database implementations. 325 329 326 330 This base class is designed to work out-of-the-box with PostgreSQL 8. … … 430 434 431 435 class TableRef: 436 """Wraps a table reference for use in SQLDecompiler's stack. 437 438 When we hit LOAD_FAST while decompiling, that should always be translated 439 into a table reference in the SQL. 440 """ 432 441 def __init__(self, classname): 433 442 self.classname = classname … … 724 733 725 734 class ConnectionWrapper(object): 735 """Connection object wrapper, so it can be used as a weak reference.""" 736 726 737 def __init__(self, conn=None): 727 738 self.conn = conn … … 732 743 733 744 class UnitClassWrapper(object): 745 """Unit class wrapper, for use in parsing multiselect joins.""" 734 746 735 747 def __init__(self, wclass, sm): … … 746 758 # Place the identifier properties first 747 759 # in case others depend upon them. 748 keys = list(wclass.identifiers) + [k for k in wclass.properties ()760 keys = list(wclass.identifiers) + [k for k in wclass.properties 749 761 if k not in wclass.identifiers] 750 762 cols = [(wclass, k) for k in keys] … … 778 790 779 791 class StorageManagerDB(storage.StorageManager): 780 """StoreManager to save and retrieve Units using a DB."""792 """StoreManager base class to save and retrieve Units using a DB.""" 781 793 782 794 sql_name_max_length = 64 … … 877 889 878 890 def connection(self): 891 """Return a connection from the pool.""" 879 892 if not self.threaded: 880 893 # Place a single 'conn' entry in self.refs. … … 932 945 933 946 def shutdown(self): 947 """Release all database connections.""" 934 948 # Empty the pool. 935 949 if self.pool: … … 946 960 947 961 def select(self, cls, expr, fields=None, distinct=False): 962 """Return an SQL SELECT statement, and an 'imperfect' flag. 963 964 imperfect: True or False depending on whether the generated SQL 965 perfectly satisfies the given expression. 966 """ 948 967 clsname = cls.__name__ 949 968 tablename = self.table_name(clsname) … … 967 986 968 987 def where(self, classnames, expr): 988 """Return an SQL WHERE clause, and an 'imperfect' flag. 989 990 imperfect: True or False depending on whether the generated SQL 991 perfectly satisfies the given expression. 992 """ 969 993 decom = self.decompiler(classnames, expr, self, self.toAdapter) 970 994 return decom.code(), decom.imperfect … … 991 1015 992 1016 def recall(self, cls, expr=None): 1017 """Yield a sequence of Unit instances which satisfy the expression.""" 993 1018 clsname = cls.__name__ 994 1019 … … 1006 1031 props = [] 1007 1032 idnames = list(cls.identifiers) 1008 for key in idnames + [x for x in cls.properties ()if x not in idnames]:1033 for key in idnames + [x for x in cls.properties if x not in idnames]: 1009 1034 index, ftype = columns[self.column_name(clsname, key, quoted=False)] 1010 1035 subtype = self.expanded_columns.get((clsname, key)) … … 1078 1103 fields = [] 1079 1104 values = [] 1080 for key in cls.properties ():1105 for key in cls.properties: 1081 1106 subtype = self.expanded_columns.get((clsname, key)) 1082 1107 if subtype: … … 1093 1118 1094 1119 def id_clause(self, unit): 1120 """Return an SQL expression for the identifiers of the given Unit.""" 1095 1121 clsname = unit.__class__.__name__ 1096 1122 col = self.column_name … … 1107 1133 1108 1134 parms = [] 1109 for key in cls.properties ():1135 for key in cls.properties: 1110 1136 if key not in cls.identifiers: 1111 1137 subtype = self.expanded_columns.get((clsname, key)) … … 1244 1270 1245 1271 def join(self, unitjoin): 1272 """Return an SQL FROM clause for the given unitjoin.""" 1246 1273 cls1, cls2 = unitjoin.class1, unitjoin.class2 1247 1274 if isinstance(cls1, dejavu.UnitJoin): … … 1279 1306 1280 1307 def multiselect(self, classes, expr): 1308 """Return an SQL SELECT statement, an imperfect flag, and column names.""" 1281 1309 1282 1310 # Create a new unitjoin tree where each class is wrapped. … … 1326 1354 1327 1355 def multirecall(self, classes, expr): 1328 """ multirecall(classes, expr) -> Full inner join units."""1356 """Yield Unit instance sets which satisfy the expression.""" 1329 1357 sql, imp, supplied_cols = self.multiselect(classes, expr) 1330 1358 data, recvd_cols = self.fetch(sql) … … 1383 1411 1384 1412 fields = [] 1385 for key in cls.properties ():1413 for key in cls.properties: 1386 1414 fields.append('%s %s' % (self.column_name(clsname, key), 1387 1415 typename(cls, key))) trunk/storage/sockets.py
r190 r191 31 31 else: 32 32 # Assume it's a dejavu.Unit. 33 for key in unit. __class__.properties():33 for key in unit.properties: 34 34 value = getattr(unit, key) 35 35 if value.__class__.__name__ == "FixedPoint": … … 46 46 except pickle.UnpicklingError: 47 47 raise TypeError(data) 48 cls = unit.__class__ 49 for key in cls.properties(): 48 for key in unit.properties: 50 49 # Set the attribute directly to avoid __set__ overhead. 51 50 unit._properties[key] = attrdict[key] trunk/storage/storeado.py
r190 r191 656 656 # We have to delay expanded tables until we have an ID. 657 657 to_expand = [] 658 for key in cls.properties ():658 for key in cls.properties: 659 659 typename = self.typeAdapter.coerce(cls, key) 660 660 if "IDENTITY" in typename: … … 896 896 # We have to delay expanded tables until we have an ID. 897 897 to_expand = [] 898 for key in cls.properties ():898 for key in cls.properties: 899 899 typename = self.typeAdapter.coerce(cls, key) 900 900 if typename.startswith("AUTOINCREMENT"): trunk/storage/storemysql.py
r190 r191 233 233 # We have to delay expanded tables until we have an ID. 234 234 to_expand = [] 235 for key in cls.properties ():235 for key in cls.properties: 236 236 typename = self.typeAdapter.coerce(cls, key) 237 237 if typename.endswith("AUTO_INCREMENT"): … … 281 281 fields = [] 282 282 pk = [] 283 for key in cls.properties ():283 for key in cls.properties: 284 284 qname = self.column_name(clsname, key) 285 285 dbtype = typename(cls, key) trunk/storage/storepypgsql.py
r190 r191 140 140 # We have to delay expanded tables until we have an ID. 141 141 to_expand = [] 142 for key in cls.properties ():142 for key in cls.properties: 143 143 typename = self.typeAdapter.coerce(cls, key) 144 144 if 'nextval' in typename: … … 192 192 193 193 fields = [] 194 for key in cls.properties ():194 for key in cls.properties: 195 195 dbtype = typename(cls, key) 196 196 if 'nextval' in dbtype: trunk/storage/storesqlite.py
r190 r191 286 286 # We have to delay expanded tables until we have an ID. 287 287 to_expand = [] 288 for key in cls.properties ():288 for key in cls.properties: 289 289 if "AUTOINCREMENT" in self.typeAdapter.coerce(cls, key): 290 290 # Skip this field, since we're using AUTOINCREMENT … … 329 329 typename = self.typeAdapter.coerce 330 330 331 for key in cls.properties ():331 for key in cls.properties: 332 332 if "AUTOINCREMENT" in typename(cls, key): 333 333 # SQLite AUTOINCREMENT columns start at 1 by default. trunk/units.py
r190 r191 381 381 assocs = {} 382 382 383 # Make a copy of the parent class' _properties keys, and store384 # it in the _properties attribute of this subclass. In this383 # Make a copy of the parent class' properties, and store 384 # it in the properties attribute of this subclass. In this 385 385 # manner, Unit Property keys should propagate down to subclasses, 386 386 # but not back up to superclasses. 387 if hasattr(cls, " _properties"):388 props = dict.fromkeys(cls._properties.keys())389 else: 390 props = {}387 if hasattr(cls, "properties"): 388 props = cls.properties[:] 389 else: 390 props = [] 391 391 392 392 for name, val in dct.iteritems(): … … 398 398 if val.key is None: 399 399 val.key = name 400 props[name] = val 400 if name not in props: 401 props.append(name) 401 402 402 403 # Remove any properties from the parent class if requested 403 404 # (request by binding the parent's UnitProperty.key to None). 404 if name in props and val is None:405 del props[name]406 405 if val is None and name in props: 406 props.remove(name) 407 407 408 # Now grab any new UnitAssociations defined in this class. 408 409 if isinstance(val, UnitAssociation): … … 410 411 assocs[name] = val 411 412 412 cls. _properties = props413 cls.properties = props 413 414 cls._associations = assocs 414 415 … … 501 502 502 503 def __init__(self, **kwargs): 503 # Copy the class _properties dictinto self._properties,504 # Copy the class properties into self._properties, 504 505 # setting each value to the UnitProperty.default. 505 506 cls = self.__class__ 506 507 self._properties = dict([(k, getattr(cls, k).default) 507 for k in cls. _properties.keys()])508 for k in cls.properties]) 508 509 509 510 self.sandbox = None … … 525 526 def __copy__(self): 526 527 newUnit = self.__class__() 527 for key , prop in self.__class__._properties.iteritems():528 for key in self.properties: 528 529 if key in self.identifiers: 530 prop = getattr(self.__class__, key) 529 531 newUnit._properties[key] = prop.default 530 532 else: … … 566 568 """Set a Unit Property for cls.""" 567 569 setattr(cls, key, descriptor(type, index, key=key)) 568 cls._properties[key] = None 570 if key not in cls.properties: 571 cls.properties.append(key) 569 572 set_property = classmethod(set_property) 570 573 … … 577 580 def remove_property(cls, key): 578 581 delattr(cls, key) 579 del cls._properties[key]582 cls.properties.remove(key) 580 583 remove_property = classmethod(remove_property) 581 584 … … 583 586 """cls.indices() -> tuple of names of indexed UnitProperties.""" 584 587 product = [] 585 for key in cls.properties ():588 for key in cls.properties: 586 589 try: 587 590 if getattr(cls, key).index: … … 592 595 return tuple(product) 593 596 indices = classmethod(indices) 594 595 def properties(cls):596 """cls.properties() -> list of UnitProperty names."""597 return cls._properties.iterkeys()598 properties = classmethod(properties)599 597 600 598 def property_type(cls, key):
