Changeset 25
- Timestamp:
- 11/03/04 00:18:32
- Files:
-
- trunk/engines.py (modified) (15 diffs)
- trunk/storage/__init__.py (modified) (1 diff)
- trunk/storage/storeado.py (modified) (18 diffs)
- trunk/storage/test.mdb (modified) (previous)
- trunk/storage/test_storeado.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/engines.py
r24 r25 41 41 """ 42 42 43 _IDs = None43 Members = dejavu.UnitProperty(u'Members', list) 44 44 EngineID = dejavu.UnitProperty(u'EngineID', int, index=True) 45 45 Type = dejavu.UnitProperty(u'Type') … … 49 49 def __init__(self, **kwargs): 50 50 dejavu.Unit.__init__(self) 51 self. _IDs = sets.Set()51 self.Members = [] 52 52 self._mutex = threading.RLock() 53 53 … … 56 56 57 57 def __getstate__(self): 58 return (self._properties, self.dirty , self._IDs)58 return (self._properties, self.dirty) 59 59 60 60 def __setstate__(self, state): 61 61 self.sandbox = None 62 62 self._mutex = threading.RLock() 63 self._properties, self.dirty , self._IDs= state63 self._properties, self.dirty = state 64 64 65 65 def acquire(self): … … 70 70 71 71 def __len__(self): 72 return len(self. _IDs)72 return len(self.Members) 73 73 74 74 def add(self, ID): 75 75 self.acquire() 76 76 try: 77 self._IDs.add(ID) 77 if ID not in self.Members: 78 self.Members.append(ID) 78 79 finally: 79 80 self.release() … … 85 86 self.acquire() 86 87 try: 87 return self. _IDs.copy()88 return self.Members.copy() 88 89 finally: 89 90 self.release() … … 94 95 self.acquire() 95 96 try: 96 for i, eachID in enumerate(self. _IDs):97 for i, eachID in enumerate(self.Members): 97 98 if quota and i >= quota: 98 99 break … … 118 119 def __copy__(self): 119 120 newUnit = dejavu.Unit.__copy__(self) 120 newUnit. _IDs = self._IDs.copy()121 newUnit.Members = self.Members.copy() 121 122 return newUnit 122 123 … … 416 417 B.acquire() 417 418 try: 418 B. _IDs = A._IDs.copy()419 B.Members = A.Members.copy() 419 420 finally: 420 421 A.release() … … 434 435 A.acquire() 435 436 try: 436 for unit in self.sandbox.recall(self.arena.class_by_name(A.Type)): 437 A._IDs.add(unit.ID) 437 mem = A.Members 438 cls = self.arena.class_by_name(A.Type) 439 for unit in self.sandbox.recall(cls): 440 id = unit.ID 441 if id not in mem: 442 mem.append(id) 438 443 finally: 439 444 A.release() … … 446 451 B.acquire() 447 452 try: 448 A. _IDs = A._IDs.difference(B._IDs)453 A.Members = [x for x in A.Members if x not in B.Members] 449 454 finally: 450 455 A.release() … … 460 465 try: 461 466 cls = self.arena.class_by_name(A.Type) 467 mem = A.Members 462 468 for unit in self.sandbox.recall(cls, expr): 463 A._IDs.add(unit.ID) 469 id = unit.ID 470 if id not in mem: 471 mem.append(unit.ID) 464 472 finally: 465 473 A.release() … … 468 476 try: 469 477 cls = self.arena.class_by_name(A.Type) 470 newset = sets.Set()471 for id in A. _IDs:478 newset = [] 479 for id in A.Members: 472 480 unit = self.sandbox.unit(cls, ID=id) 473 481 if unit and expr.evaluate(unit): 474 newset.a dd(id)475 A. _IDs = newset482 newset.append(id) 483 A.Members = newset 476 484 finally: 477 485 A.release() … … 494 502 B.acquire() 495 503 try: 496 A. _IDs = A._IDs.intersection(B._IDs)504 A.Members = [x for x in A.Members if x in B.Members] 497 505 finally: 498 506 A.release() … … 521 529 oppfunc = getattr(start, eachType.__name__) 522 530 cls = self.arena.class_by_name(A.Type) 523 newset = sets.Set()524 for id in A. _IDs:531 newset = [] 532 for id in A.Members: 525 533 unit = self.sandbox.unit(cls, ID=id) 526 534 if unit: 527 535 for farUnit in oppfunc(unit): 528 newset.add(farUnit.ID) 529 A._IDs = newset 536 farid = farUnit.ID 537 if farid not in newset: 538 newset.append(farid) 539 A.Members = newset 530 540 start = eachType 531 541 A.Type = eachType.__name__ … … 540 550 B.acquire() 541 551 try: 542 A._IDs = A._IDs.union(B._IDs) 552 amem = A.Members 553 for id in B.Members: 554 if id not in amem: 555 amem.append(id) 543 556 finally: 544 557 A.release() trunk/storage/__init__.py
r24 r25 158 158 def save(self, unit, forceSave=False): 159 159 """Store the unit.""" 160 # Defer saves to sweep(). 161 pass 160 # Defer store.save() for sweepers. 161 if unit.dirty: 162 lock = self._get_lock(unit.__class__) 163 try: 164 cache = self._caches[unit.__class__] 165 cache[unit.ID] = pickle.dumps(unit) 166 finally: 167 lock.release() 162 168 163 169 def destroy(self, unit): trunk/storage/storeado.py
r24 r25 71 71 if value is None: 72 72 return None 73 else:74 # Coerce to str for pickle.loads restriction.75 value = str(value)73 74 # Coerce to str for pickle.loads restriction. 75 value = str(value) 76 76 return pickle.loads(value) 77 77 … … 480 480 self.sql, self.imperfect = store.select(unitClass, expr) 481 481 482 def field(self, name, row):482 def field(self, key, row): 483 483 try: 484 col = self.colIndices[ name]484 col = self.colIndices[key] 485 485 except KeyError, x: 486 x.args += ( name, self.unitClass.__name__)486 x.args += (key, self.unitClass.__name__) 487 487 raise x 488 else:489 return (self.fieldTypes[col], self.data[col][row])488 489 return (self.fieldTypes[col], self.data[col][row]) 490 490 491 491 def load_data(self): 492 try: 493 anRS = self.store.recordset(self.sql, adOpenForwardOnly, 494 adLockReadOnly) 495 except pywintypes.com_error, x: 496 x.args += (self.sql, ) 497 raise x 492 anRS = self.store.recordset(self.sql, adOpenForwardOnly, 493 adLockReadOnly) 498 494 499 495 for col, x in enumerate(anRS.Fields): … … 511 507 512 508 def units(self): 509 s = self.store 510 clsname = self.unitClass.__name__ 511 tbl = "%s_%s" % (s.prefix, safe_name(clsname)) 513 512 self.load_data() 514 513 if len(self.data) > 0: … … 517 516 coercer = AdapterFromADO(unit) 518 517 for key in unit.__class__.properties(): 519 value = self.field(key, row) 520 coercer.consume(key, value) 518 if (clsname, key) in s.expanded_columns: 519 # Grab the expanded data 520 try: 521 rs = s.recordset(u"SELECT EXPVAL FROM [%s_%s_%s]" 522 % (tbl, 523 safe_name(self.field('ID', row)[1]), 524 safe_name(key))) 525 except pywintypes.com_error, x: 526 # This usually occurs because the parent Unit 527 # was reserved but no table yet made for these 528 # expanded values. This is OK. TODO: trap this 529 # more specifically by examining the errmsg. 530 values = [] 531 else: 532 values = [pickle.loads(str(x)) for x in rs.GetRows()[0]] 533 rs.Close() 534 expectedType = unit.__class__.property_type(key) 535 values = expectedType(values) 536 # Set the attribute directly to avoid __set__ overhead. 537 unit._properties[key] = values 538 else: 539 value = self.field(key, row) 540 coercer.consume(key, value) 521 541 # If our SQL is imperfect, don't yield it to the 522 542 # caller unless it passes evaluate(). 523 543 if (not self.imperfect) or self.expr.evaluate(unit): 524 yield unit525 526 527 class CollectionLoaderADO(StoreIteratorADO):528 """Iterable Factory for populating UnitCollections from storage."""529 530 def units(self):531 self.load_data()532 if len(self.data) > 0:533 coll_coercer = AdapterFromADO().coerce534 for row in range(len(self.data[0])):535 unit = self.unitClass()536 coercer = AdapterFromADO(unit)537 for key in unit.__class__.properties():538 value = self.field(key, row)539 coercer.consume(key, value)540 # If our SQL is imperfect, don't yield it to the541 # caller unless it passes evaluate().542 if (not self.imperfect) or self.expr.evaluate(unit):543 # Load the collection.544 # Grab the data dictionary (list of Unit ID's)545 rsource = (u"SELECT ID FROM [%s__%s]" %546 (self.store.prefix, safe_name(unit.ID)))547 try:548 dataRS = self.store.recordset(rsource)549 except pywintypes.com_error, x:550 # This usually occurs because the UnitCollection was551 # reserved but no table yet made for IDs. This is OK.552 pass553 else:554 idtype = self.store.arena.class_by_name(unit.Type).ID.type555 while not dataRS.EOF:556 ID = coll_coercer((0, dataRS.Fields.Item(u'ID')),557 idtype)558 unit.add(ID)559 dataRS.MoveNext()560 dataRS.Close()561 544 yield unit 562 545 … … 590 573 591 574 def load_data(self): 592 try: 593 anRS = self.store.recordset(self.sql, adOpenForwardOnly, 594 adLockReadOnly) 595 except pywintypes.com_error, x: 596 x.args += (self.sql, ) 597 raise x 575 anRS = self.store.recordset(self.sql, adOpenForwardOnly, 576 adLockReadOnly) 598 577 599 578 for col, x in enumerate(anRS.Fields): … … 628 607 629 608 609 class FieldTypeAdapter(object): 610 """Return the SQL typename of a DB column.""" 611 612 def coerce(self, cls, key): 613 """coerce(cls, key) -> SQL typename for valuetype.""" 614 valuetype = cls.property_type(key) 615 mod = valuetype.__module__ 616 if mod == "__builtin__": 617 xform = "coerce_%s" % valuetype.__name__ 618 else: 619 xform = "coerce_%s_%s" % (mod, valuetype.__name__) 620 xform = xform.replace(".", "_") 621 try: 622 xform = getattr(self, xform) 623 except AttributeError: 624 raise TypeError("'%s' is not handled by %s." % 625 (valuetype, self.__class__)) 626 return xform(cls, key) 627 628 def _create_str_storage(self, cls, key): 629 """This basic string handler does not know anything about the size 630 limitations of the particular database. You should use one of the 631 subclasses for your particular database if you need storage for 632 strings over 255 characters.""" 633 prop = getattr(cls, key) 634 size = prop.hints.get(u'Size', '255') 635 return u"VARCHAR(%s)" % size 636 637 def coerce_bool(self, cls, key): return u"BIT" 638 639 def coerce_datetime_datetime(self, cls, key): return u"TIMESTAMP" 640 def coerce_datetime_date(self, cls, key): return u"DATE" 641 def coerce_datetime_time(self, cls, key): return u"TIME" 642 643 coerce_dict = _create_str_storage 644 645 def coerce_fixedpoint_FixedPoint(self, cls, key): return u"FLOAT" 646 def coerce_float(self, cls, key): return u"FLOAT" 647 def coerce_int(self, cls, key): return u"INTEGER" 648 649 coerce_list = _create_str_storage 650 coerce_str = _create_str_storage 651 coerce_tuple = _create_str_storage 652 coerce_unicode = _create_str_storage 653 654 630 655 class StorageManagerADO(storage.StorageManager): 631 656 """StoreManager to save and retrieve Units via ADO 2.7. … … 635 660 636 661 decompiler = ADOSQLDecompiler 662 createAdapter = FieldTypeAdapter() 637 663 threaded = False 638 664 … … 653 679 self.cursorType = int(allOptions.get(u'CursorType', adOpenDynamic)) 654 680 self.lockType = int(allOptions.get(u'LockType', adLockOptimistic)) 681 682 ec = [] 683 for prop in allOptions.get(u'Expanded Columns', '').split(","): 684 if prop: 685 lastdot = prop.rfind(".") 686 clsname, key = prop[:lastdot], prop[lastdot + 1:] 687 ec.append((clsname, key)) 688 self.expanded_columns = ec 655 689 656 690 self.reserve_lock = threading.Lock() … … 682 716 anRS.Open(aQuery, self.connection(), cursorType, lockType) 683 717 except pywintypes.com_error, x: 718 try: 719 anRS.Close() 720 except: 721 pass 684 722 x.args += (aQuery, ) 685 723 raise x … … 695 733 cls = spath[0] 696 734 leftkey, rightkey = firstcls._associations[cls] 697 params = {u'prefix': u'djv',735 params = {u'prefix': self.prefix, 698 736 u'left': firstcls.__name__, 699 737 u'right': cls.__name__, … … 755 793 return self.decompiler(self, cls, expr).code() 756 794 757 def execute(self, aQuery): 758 self.connection().Execute(aQuery) 795 def execute(self, aQuery, conn=None): 796 if conn is None: 797 conn = self.connection() 798 try: 799 conn.Execute(aQuery) 800 except pywintypes.com_error, x: 801 x.args += (aQuery, ) 802 raise x 759 803 760 804 def recall(self, cls, expr=None, pairs=None): 761 805 if expr is None: 762 806 expr = logic.Expression(lambda x: True) 807 763 808 if pairs is not None: 764 809 return StoreMultiIteratorADO(self, cls, expr, pairs).units() 765 810 else: 766 if cls.__name__ == u'UnitCollection': 767 aLoader = CollectionLoaderADO 768 else: 769 aLoader = StoreIteratorADO 770 return aLoader(self, cls, expr).units() 811 return StoreIteratorADO(self, cls, expr).units() 771 812 772 813 def reserve(self, unit): … … 780 821 (self.prefix, safe_name(clsname))) 781 822 if not (anRS.BOF and anRS.EOF): 782 ## anRS.MoveFirst()783 ## if not (anRS.BOF or anRS.EOF):784 823 data = anRS.GetRows()[0] 785 824 unit.ID = unit.sequencer.next(data) … … 801 840 if unit.dirty or forceSave: 802 841 cls = unit.__class__ 842 clsname = cls.__name__ 803 843 # Use a cursor always--makes mixed-quotes, newline, etc easier. 804 844 anRS = self.recordset("SELECT * FROM [%s%s] WHERE ID = %s" % 805 (self.prefix, safe_name(cls .__name__),845 (self.prefix, safe_name(clsname), 806 846 AdapterToADOSQL().coerce(unit.ID))) 807 847 if anRS.EOF and anRS.BOF: … … 810 850 fmt = AdapterToADOFields() 811 851 for key in cls.properties(): 812 eachType = cls.property_type(key) 813 newValue = fmt.coerce(getattr(unit, key), eachType) 814 try: 815 anRS.Fields(key).Value = newValue 816 except pywintypes.com_error, x: 852 if (clsname, key) in self.expanded_columns: 853 # Special-case this field into its own table. 854 self.save_expanded(unit, key) 855 else: 856 eachType = cls.property_type(key) 857 newValue = fmt.coerce(getattr(unit, key), eachType) 817 858 try: 818 anRS.Close() 819 except: 820 pass 821 x.args += (cls.__name__, key, eachType, newValue) 822 raise x 859 anRS.Fields(key).Value = newValue 860 except pywintypes.com_error, x: 861 try: 862 anRS.Close() 863 except: 864 pass 865 x.args += (clsname, key, eachType, newValue) 866 raise x 823 867 anRS.Update() 824 # Need to explicitly close here, or save_collection825 # will fail on BeginTrans.826 868 anRS.Close() 827 if cls.__name__ == u'UnitCollection':828 self.save_collection(unit)829 869 unit.dirty = False 830 870 831 def save_collection(self, unitColl): 832 """Update the database from the UnitCollection's data.""" 871 def save_expanded(self, unit, key): 872 """Save a field using a table specifically for that purpose.""" 873 unitcls = unit.__class__ 874 table = ("%s_%s_%s_%s" % (self.prefix, safe_name(unitcls.__name__), 875 safe_name(unit.ID), safe_name(key))) 876 833 877 conn = self.connection() 834 ## # Dropped the begintrans; we were running into limits in MS Access. 835 ## conn.BeginTrans() 836 deleteStatement = (u"DROP TABLE [%s__%s];" % 837 (self.prefix, safe_name(unitColl.ID))) 838 conn.Execute(deleteStatement) 839 840 cls = unitColl.unit_class() 841 fieldtype = self.createCoercions[cls.ID.type](cls, 'ID') 842 createStatement = (u"CREATE TABLE [%s__%s] (ID %s);" 843 % (self.prefix, safe_name(unitColl.ID), 844 fieldtype)) 845 conn.Execute(createStatement) 846 847 ins = u"INSERT INTO [%s__%s] (ID) VALUES (%s);" 848 coercer = AdapterToADOSQL().coerce 849 for eachID in unitColl.ids(): 878 try: 879 self.execute((u"DROP TABLE [%s];" % table), conn) 880 except pywintypes.com_error, x: 881 pass 882 883 # Ugly, ugly hack to get NTEXT or MEMO as appropriate. The point 884 # is, we want a large text field so we can pickle each item. 885 ftype = self.createAdapter.coerce_list(None, None) 886 self.execute(u"CREATE TABLE [%s] (EXPVAL %s);" % (table, ftype), conn) 887 888 ins = u"INSERT INTO [" + table + "] (EXPVAL) VALUES ('%s');" 889 for v in getattr(unit, key): 850 890 # Create a row for the unit. 851 891 # Use an INSERT command (not a cursor) for better performance. 852 # TODO: cluster inserts. 853 insStatement = ins % (self.prefix, safe_name(unitColl.ID), 854 coercer(eachID)) 855 try: 856 conn.Execute(insStatement) 857 except pywintypes.com_error, x: 858 x.args += (insStatement, createStatement) 859 raise x 860 ## conn.CommitTrans() 892 v = pickle.dumps(v).replace("'", "''") 893 self.execute(ins % v, conn) 861 894 862 895 def destroy(self, unit): … … 866 899 (self.prefix, safe_name(unit.__class__.__name__), 867 900 AdapterToADOSQL().coerce(unit.ID))) 868 try: 869 self.execute(deleteStatement) 870 except pywintypes.com_error, x: 871 x.args += (deleteStatement, ) 872 raise x 873 874 def _create_str_storage(unitClass, key): 875 """This basic string handler does not know anything about the size 876 limitations of the particular database. You should use one of the 877 subclasses for your particular database if you need storage for 878 strings over 255 characters.""" 879 # 'self' is missing from the func sig ON PURPOSE. 880 try: 881 prop = getattr(unitClass, key) 882 size = prop.hints[u'Size'] 883 return u"VARCHAR(%s)" % size 884 except KeyError: 885 return u"VARCHAR(255)" 886 887 createCoercions = {datetime.datetime: lambda x, y: u"TIMESTAMP", 888 datetime.date: lambda x, y: u"DATE", 889 datetime.time: lambda x, y: u"TIME", 890 str: _create_str_storage, 891 unicode: _create_str_storage, 892 dict: _create_str_storage, 893 list: _create_str_storage, 894 fixedpoint.FixedPoint: lambda x, y: u"FLOAT", 895 int: lambda x, y: u"INTEGER", 896 bool: lambda x, y: u"BIT", 897 float: lambda x, y: u"FLOAT", 898 } 901 self.execute(deleteStatement) 899 902 900 903 def create_storage(self, unitClass): 904 clsname = safe_name(unitClass.__name__) 905 906 coerce = self.createAdapter.coerce 901 907 fields = [] 902 908 for key in unitClass.properties(): 903 eachType = unitClass.property_type(key) 904 aType = self.createCoercions[eachType](unitClass, key) 905 fields.append(u"[%s] %s" % (key, aType)) 906 indices = [x + " ASC" for x in unitClass.indices()] 907 908 createStatement = (u"CREATE TABLE [%s%s] (%s)" % 909 (self.prefix, 910 safe_name(unitClass.__name__), 911 ", ".join(fields))) 912 try: 913 self.execute(createStatement) 914 except Exception, x: 915 x.args += (createStatement, ) 916 raise x 917 918 for index in indices: 919 indexStatement = (u"CREATE INDEX [%si%s%s] ON [%s%s] (%s)" 920 % (self.prefix, safe_name(unitClass.__name__), 921 safe_name(index), 922 self.prefix, safe_name(unitClass.__name__), 923 index)) 924 try: 925 self.execute(indexStatement) 926 except Exception, x: 927 x.args += (indexStatement, ) 928 raise x 929 930 return True 909 if (unitClass.__name__, key) not in self.expanded_columns: 910 fields.append(u"[%s] %s" % (key, coerce(unitClass, key))) 911 self.execute(u"CREATE TABLE [%s%s] (%s)" % 912 (self.prefix, clsname, ", ".join(fields))) 913 914 for index in unitClass.indices(): 915 self.execute(u"CREATE INDEX [%si%s%s] ON [%s%s] (%s ASC)" 916 % (self.prefix, clsname, safe_name(index), 917 self.prefix, clsname, index)) 931 918 932 919 def distinct(self, cls, fields, expr=None): … … 945 932 ## u"distinct()", cls, fields, expr) 946 933 947 try: 948 anRS = self.recordset(sql, adOpenForwardOnly, adLockReadOnly) 949 except pywintypes.com_error, x: 950 x.args += (self.sql, ) 951 raise x 934 anRS = self.recordset(sql, adOpenForwardOnly, adLockReadOnly) 952 935 953 936 fieldTypes = [x.Type for x in anRS.Fields] … … 972 955 973 956 957 ########################################################################### 958 ## ## 959 ## SQL Server ## 960 ## ## 961 ########################################################################### 962 963 964 class FieldTypeAdapter_SQLServer(FieldTypeAdapter): 965 966 def _create_str_storage(self, cls, key): 967 prop = getattr(cls, key) 968 size = prop.hints.get(u'Size', '255') 969 if size == 0 or size > 8000: 970 # 8000 *bytes* is the absolute upper limit, based on T_SQL docs 971 # for varchar. If there are further fields defined for the class, 972 # or the code page uses a double-byte character set, we still 973 # might exceed the max size (8060) for a record. We could calc 974 # the total requested record size, and adjust accordingly. For 975 # now, we just trust that units generally use a size of 0 to 976 # bump up to NTEXT (1 gig characters). 977 return u"NTEXT" 978 return u"VARCHAR(%s)" % size 979 980 # dict, list, and tuple will all be pickled in AdapterToADO 981 def coerce_dict(self, cls, key): return u"NTEXT" 982 def coerce_list(self, cls, key): return u"NTEXT" 983 coerce_str = _create_str_storage 984 def coerce_tuple(self, cls, key): return u"NTEXT" 985 coerce_unicode = _create_str_storage 986 987 974 988 class StorageManagerADO_SQLServer(StorageManagerADO): 975 976 def _create_str_storage(unitClass, key): 977 try: 978 prop = getattr(unitClass, key) 979 size = prop.hints[u'Size'] 980 except KeyError: 981 return u"VARCHAR(255)" 982 else: 983 if size == 0 or size > 8060: 984 # 8060 is the absolute upper limit, based on the page size 985 # of SQL server. If there are further fields defined for 986 # the unitClass, we could exceed the max size for a record. 987 # Perhaps someday we can calc the total requested record 988 # size, and adjust accordingly. For now, we just trust that 989 # units generally use a size of 0 to bump up to NTEXT. 990 return u"NTEXT" 991 return u"VARCHAR(%s)" % size 992 993 createCoercions = {datetime.datetime: lambda x, y: u"TIMESTAMP", 994 datetime.date: lambda x, y: u"DATE", 995 datetime.time: lambda x, y: u"TIME", 996 str: _create_str_storage, 997 unicode: _create_str_storage, 998 dict: lambda x, y: u"NTEXT", 999 list: lambda x, y: u"NTEXT", 1000 fixedpoint.FixedPoint: lambda x, y: u"FLOAT", 1001 float: lambda x, y: u"FLOAT", 1002 int: lambda x, y: u"INTEGER", 1003 bool: lambda x, y: u"BIT", 1004 } 989 createAdapter = FieldTypeAdapter_SQLServer() 1005 990 1006 991 … … 1025 1010 1026 1011 1012 class FieldTypeAdapter_MSAccess(FieldTypeAdapter): 1013 1014 def _create_str_storage(self, cls, key): 1015 prop = getattr(cls, key) 1016 size = prop.hints.get(u'Size', '255') 1017 if size == 0 or size > 255: 1018 # 255 chars is the upper limit for TEXT / VARCHAR in MS Access. 1019 # MEMO is 1 gigabyte when set programatically (only 64K when set 1020 # in Access UI). But then, 1 GB is the limit for the whole DB. 1021 return u"MEMO" 1022 return u"VARCHAR(%s)" % size 1023 1024 # dict, list, and tuple will all be pickled in AdapterToADO 1025 def coerce_dict(self, cls, key): return u"MEMO" 1026 def coerce_list(self, cls, key): return u"MEMO" 1027 coerce_str = _create_str_storage 1028 def coerce_tuple(self, cls, key): return u"MEMO" 1029 coerce_unicode = _create_str_storage 1030 1031 1027 1032 class StorageManagerADO_MSAccess(StorageManagerADO): 1028 1033 1029 1034 decompiler = ADOSQLDecompiler_MSAccess 1030 1031 def _create_str_storage(unitClass, key): 1032 try: 1033 prop = getattr(unitClass, key) 1034 size = prop.hints[u'Size'] 1035 except KeyError: 1036 return u"VARCHAR(255)" 1037 else: 1038 if size == 0 or size > 255: 1039 # 255 is the upper limit, based on the max size of 'Text' 1040 # fields within MS Access. If there are further fields 1041 # defined for the unitClass, we could exceed the max size 1042 # for a record. Perhaps someday we can calc the total 1043 # requested record size, and adjust accordingly. For now, 1044 # we just trust that units generally use aSize of 0 to 1045 # bump up to MEMO. 1046 return u"MEMO" 1047 return u"VARCHAR(%s)" % size 1048 1049 createCoercions = {datetime.datetime: lambda x, y: u"TIMESTAMP", 1050 datetime.date: lambda x, y: u"DATE", 1051 datetime.time: lambda x, y: u"TIME", 1052 str: _create_str_storage, 1053 unicode: _create_str_storage, 1054 dict: lambda x, y: u"MEMO", 1055 list: lambda x, y: u"MEMO", 1056 fixedpoint.FixedPoint: lambda x, y: u"FLOAT", 1057 float: lambda x, y: u"FLOAT", 1058 int: lambda x, y: u"INTEGER", 1059 bool: lambda x, y: u"BIT", 1060 } 1035 createAdapter = FieldTypeAdapter_MSAccess() 1036 1061 1037 1062 1038 if __name__ == '__main__': trunk/storage/test_storeado.py
r24 r25 9 9 10 10 smOptions = {u'Connect': ("PROVIDER=MICROSOFT.JET.OLEDB.4.0;" 11 "DATA SOURCE=test.mdb;"),} 11 "DATA SOURCE=test.mdb;"), 12 u'Expanded Columns': "Other.Bunch", 13 } 12 14 testSM = storeado.StorageManagerADO_MSAccess("test", arena, smOptions) 13 15 arena.stores['testSM'] = arena.defaultStore = testSM 14 16 15 17 16 class Things(dejavu.Unit): pass 17 Things.set_properties({"Name": unicode, 18 "Size": int, 19 "Date": datetime.date, 20 "Time": datetime.time, 21 "DateTime": datetime.datetime, 22 }) 18 class Things(dejavu.Unit): 19 Name = dejavu.UnitProperty("Name", unicode) 20 Size = dejavu.UnitProperty("Size", int) 21 Date = dejavu.UnitProperty("Date", datetime.date) 22 Time = dejavu.UnitProperty("Time", datetime.time) 23 DateTime = dejavu.UnitProperty("DateTime", datetime.datetime) 23 24 24 25 class Animals(dejavu.Unit): pass … … 28 29 }) 29 30 arena.associate(Things, 'Size', Animals, 'Legs') 31 32 class Other(dejavu.Unit): 33 Blob = dejavu.UnitProperty("Blob", list) 34 Bunch = dejavu.UnitProperty("Bunch", list) 35 arena.register(Other) 30 36 31 37 … … 82 88 pass 83 89 testSM.create_storage(Things) 90 91 try: 92 testSM.execute("DROP TABLE djvOther") 93 except pywintypes.com_error: 94 pass 95 testSM.create_storage(Other) 84 96 85 97 def test_recordset(self): … … 91 103 self.assertEqual(len(data[0]), 4) 92 104 rs.Close() 105 106 def test_expanded_columns(self): 107 o = Other(ID=1, Blob=[1, 2, 3], Bunch=['a', 'b', 'c']) 108 box = dejavu.Sandbox(arena) 109 box.memorize(o) 110 box.flush_all() 111 o = box.unit(Other, ID=1) 112 self.assertEqual(o.Blob, [1, 2, 3]) 113 self.assertEqual(o.Bunch, ['a', 'b', 'c']) 93 114 94 115 def test_unit_roundtrip(self): … … 179 200 "([djvThings].[Date] = 3) and (([djvThings].[Qty] > 4) and ([djvThings].[Qty] < 20))", False) 180 201 202 181 203 class AdapterTests(unittest.TestCase): 182 204 … … 188 210 gaiter.Name = 'Presentation pair' 189 211 gaiter.Size = 3 190 gaiter.Date = d atetime.date(2000, 1, 1)212 gaiter.Date = d = datetime.date(2000, 1, 1) 191 213 # 59 should give rounding errors with divmod, which 192 214 # AdapterFromADO needs to correct. 193 gaiter.Time = datetime.time(8, 15, 59)194 gaiter.DateTime = d atetime.datetime(2004, 7, 29, 5, 6, 7)215 gaiter.Time = t = datetime.time(8, 15, 59) 216 gaiter.DateTime = dt = datetime.datetime(2004, 7, 29, 5, 6, 7) 195 217 box.memorize(gaiter) 196 218 … … 199 221 gaiter = box.unit(Things, Size=3) 200 222 self.assertEqual(gaiter.Size, 3) 201 self.assertEqual(gaiter.Date, d atetime.date(2000, 1, 1))202 self.assertEqual(gaiter.Time, datetime.time(8, 15, 59))203 self.assertEqual(gaiter.DateTime, d atetime.datetime(2004, 7, 29, 5, 6, 7))223 self.assertEqual(gaiter.Date, d) 224 self.assertEqual(gaiter.Time, t) 225 self.assertEqual(gaiter.DateTime, dt) 204 226 205 227
