Changeset 193
- Timestamp:
- 03/15/06 15:45:27
- Files:
-
- trunk/doc/storage.html (modified) (1 diff)
- trunk/storage/db.py (modified) (9 diffs)
- trunk/storage/storeado.py (modified) (6 diffs)
- trunk/storage/storemysql.py (modified) (3 diffs)
- trunk/storage/storepypgsql.py (modified) (3 diffs)
- trunk/storage/storesqlite.py (modified) (2 diffs)
- trunk/test/test_storemsaccess.py (modified) (1 diff)
- trunk/test/test_storeodbc.py (modified) (1 diff)
- trunk/test/test_storesqlserver.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/doc/storage.html
r192 r193 219 219 The value should be the full dotted package name of the class you 220 220 wish to use.</td> 221 </tr>222 <tr>223 <td>Expanded Columns</td>224 <td><tt>Animal.PreviousZoos:int, Exhibit.Animals:int</tt></td>225 <td>Optional. A comma-separated list of UnitClass.Property:subtype226 strings. Each such property should be of .type list or tuple.227 Usually, lists are pickled for storage in a normal database228 field. Properties listed in <tt>Expanded Columns</tt> will229 be stored each in their own table. The "subtype" portion tells230 the Storage Manager the type of each value in the list. This is231 mostly here to support a legacy database which already normalizes232 the values in this way; for new projects, we recommend using the233 default pickle method, which is much faster and more manageable.</td>234 221 </tr> 235 222 </table> trunk/storage/db.py
r192 r193 295 295 # self.encoding. 296 296 value = pickle.dumps(value) 297 return self.coerce_str(value, skip_encoding=True) 297 value = self.coerce_str(value, skip_encoding=True) 298 return value 298 299 299 300 coerce_dict = do_pickle … … 832 833 self.prefix = allOptions.get('Prefix', "djv") 833 834 self.reserve_lock = threading.Lock() 834 835 ec = {}836 for prop in allOptions.get('Expanded Columns', '').split(","):837 if prop:838 cls, type = [x.strip() for x in prop.split(":", 1)]839 lastdot = cls.rfind(".")840 clsname, key = cls[:lastdot], cls[lastdot + 1:]841 ec[(clsname, key)] = type842 self.expanded_columns = ec843 835 844 836 # Naming # … … 1028 1020 # Get specs on properties. Put the identifier properties 1029 1021 # first, in case other fields depend upon them. 1030 # See load_expanded, for example.1031 1022 props = [] 1032 1023 idnames = list(cls.identifiers) 1033 1024 for key in idnames + [x for x in cls.properties if x not in idnames]: 1034 1025 index, ftype = columns[self.column_name(clsname, key, quoted=False)] 1035 subtype = self.expanded_columns.get((clsname, key)) 1036 props.append((key, index, ftype, subtype)) 1026 props.append((key, index, ftype)) 1037 1027 1038 1028 consume = self.fromAdapter.consume 1039 1029 for row in data: 1040 1030 unit = cls() 1041 for key, index, ftype , subtypein props:1031 for key, index, ftype in props: 1042 1032 value = row[index] 1043 if subtype: 1044 self.load_expanded(unit, key, subtype) 1045 else: 1046 consume(unit, key, value, ftype) 1033 consume(unit, key, value, ftype) 1047 1034 1048 1035 # If our SQL is imperfect, don't yield it to the … … 1104 1091 values = [] 1105 1092 for key in cls.properties: 1106 subtype = self.expanded_columns.get((clsname, key)) 1107 if subtype: 1108 self.save_expanded(unit, key, subtype) 1109 else: 1110 val = self.toAdapter.coerce(getattr(unit, key)) 1111 fields.append(self.column_name(clsname, key)) 1112 values.append(val) 1093 val = self.toAdapter.coerce(getattr(unit, key)) 1094 fields.append(self.column_name(clsname, key)) 1095 values.append(val) 1113 1096 1114 1097 fields = ", ".join(fields) … … 1135 1118 for key in cls.properties: 1136 1119 if key not in cls.identifiers: 1137 subtype = self.expanded_columns.get((clsname, key)) 1138 if subtype: 1139 self.save_expanded(unit, key, subtype) 1140 else: 1141 val = self.toAdapter.coerce(getattr(unit, key)) 1142 parms.append('%s = %s' % 1143 (self.column_name(clsname, key), val)) 1120 val = self.toAdapter.coerce(getattr(unit, key)) 1121 parms.append('%s = %s' % 1122 (self.column_name(clsname, key), val)) 1144 1123 1145 1124 if parms: … … 1149 1128 self.execute(sql) 1150 1129 unit.cleanse() 1151 1152 def save_expanded(self, unit, key, subtype, conn=None):1153 """save_expanded(unit, key, subtype). Save list in separate table."""1154 unitcls = unit.__class__1155 id = "_".join(map(str, unit.identity()))1156 table = self.table_name("_%s_%s_%s" % (unitcls.__name__, id, key))1157 1158 # Just drop the old table and start with a new one.1159 try:1160 self.execute(("DROP TABLE %s;" % table), conn)1161 except:1162 pass1163 1164 val = getattr(unit, key)1165 if val is None:1166 # Don't create a new table at all. This will signal1167 # recall() to set the attribute to None on load.1168 pass1169 else:1170 ftype = getattr(self.typeAdapter, "coerce_" + subtype)(unitcls, key)1171 self.execute(("CREATE TABLE %s (EXPVAL %s);"1172 % (table, ftype)), conn)1173 1174 for v in val:1175 self.execute(("INSERT INTO %s (EXPVAL) VALUES ('%s');"1176 % (table, self.toAdapter.coerce(v))), conn)1177 1178 def load_expanded(self, unit, key, subtype):1179 """load_expanded(unit, key, subtype). Load list from separate table."""1180 unitcls = unit.__class__1181 id = "_".join(map(str, unit.identity()))1182 table = self.table_name("_%s_%s_%s" % (unitcls.__name__, id, key))1183 try:1184 data, col_defs = self.fetch("SELECT EXPVAL FROM %s" % table)1185 except:1186 values = None1187 else:1188 coltype = col_defs[0][1]1189 coercer = getattr(self.fromAdapter, "coerce_" + subtype)1190 values = [coercer(row[0], coltype) for row in data]1191 1192 expected_type = unitcls.property_type(key)1193 values = expected_type(values)1194 1195 # Set the attribute directly to avoid __set__ overhead.1196 unit._properties[key] = values1197 1130 1198 1131 def destroy(self, unit): … … 1363 1296 c, key = sup 1364 1297 name, ftype = rec[0], rec[1] 1365 subtype = self.expanded_columns.get((c.__name__, key)) 1366 props.append((c, key, ftype, subtype)) 1298 props.append((c, key, ftype)) 1367 1299 1368 1300 consume = self.fromAdapter.consume … … 1370 1302 index = 0 1371 1303 units = {} 1372 for c, key, ftype , subtypein props:1304 for c, key, ftype in props: 1373 1305 if c in units: 1374 1306 unit = units[c] … … 1376 1308 units[c] = unit = c() 1377 1309 value = row[index] 1378 if subtype: 1379 self.load_expanded(unit, key, subtype) 1380 else: 1381 consume(unit, key, value, ftype) 1310 consume(unit, key, value, ftype) 1382 1311 index += 1 1383 1312 trunk/storage/storeado.py
r192 r193 675 675 fields = [] 676 676 values = [] 677 # We have to delay expanded tables until we have an ID.678 to_expand = []679 677 for key in cls.properties: 680 678 typename = self.typeAdapter.coerce(cls, key) … … 682 680 # Skip this field, since we're using IDENTITY 683 681 continue 684 subtype = self.expanded_columns.get((clsname, key)) 685 if subtype: 686 to_expand.append((key, subtype)) 687 else: 688 val = self.toAdapter.coerce(getattr(unit, key)) 689 fields.append(self.column_name(clsname, key)) 690 values.append(val) 682 val = self.toAdapter.coerce(getattr(unit, key)) 683 fields.append(self.column_name(clsname, key)) 684 values.append(val) 691 685 692 686 fields = ", ".join(fields) … … 702 696 % str(tablename)) 703 697 setattr(unit, cls.identifiers[0], data[0][0]) 704 705 # Now that we have an ID, save our expanded tables.706 for key, subtype in to_expand:707 self.save_expanded(unit, key, subtype)708 698 709 699 # Schemas # … … 915 905 fields = [] 916 906 values = [] 917 # We have to delay expanded tables until we have an ID.918 to_expand = []919 907 for key in cls.properties: 920 908 typename = self.typeAdapter.coerce(cls, key) … … 922 910 # Skip this field, since we're using AUTOINCREMENT 923 911 continue 924 subtype = self.expanded_columns.get((clsname, key)) 925 if subtype: 926 to_expand.append((key, subtype)) 927 else: 928 val = self.toAdapter.coerce(getattr(unit, key)) 929 fields.append(self.column_name(clsname, key)) 930 values.append(val) 912 val = self.toAdapter.coerce(getattr(unit, key)) 913 fields.append(self.column_name(clsname, key)) 914 values.append(val) 931 915 932 916 fields = ", ".join(fields) … … 938 922 data, col_defs = self.fetch("SELECT @@IDENTITY;") 939 923 setattr(unit, cls.identifiers[0], data[0][0]) 940 941 # Now that we have an ID, save our expanded tables.942 for key, subtype in to_expand:943 self.save_expanded(unit, key, subtype)944 924 945 925 # Schemas # trunk/storage/storemysql.py
r192 r193 231 231 fields = [] 232 232 values = [] 233 # We have to delay expanded tables until we have an ID.234 to_expand = []235 233 for key in cls.properties: 236 234 typename = self.typeAdapter.coerce(cls, key) … … 238 236 # Skip this field, since we're using AUTO_INCREMENT 239 237 continue 240 subtype = self.expanded_columns.get((clsname, key)) 241 if subtype: 242 to_expand.append((key, subtype)) 243 else: 244 val = self.toAdapter.coerce(getattr(unit, key)) 245 fields.append(self.column_name(clsname, key)) 246 values.append(val) 238 val = self.toAdapter.coerce(getattr(unit, key)) 239 fields.append(self.column_name(clsname, key)) 240 values.append(val) 247 241 248 242 fields = ", ".join(fields) … … 254 248 data, col_defs = self.fetch("SELECT LAST_INSERT_ID();") 255 249 setattr(unit, cls.identifiers[0], data[0][0]) 256 257 # Now that we have an ID, save our expanded tables.258 for key, subtype in to_expand:259 self.save_expanded(unit, key, subtype)260 250 261 251 # Schemas # trunk/storage/storepypgsql.py
r192 r193 138 138 fields = [] 139 139 values = [] 140 # We have to delay expanded tables until we have an ID.141 to_expand = []142 140 for key in cls.properties: 143 141 typename = self.typeAdapter.coerce(cls, key) … … 145 143 # Skip this field, since we're using a sequencer 146 144 continue 147 subtype = self.expanded_columns.get((clsname, key)) 148 if subtype: 149 to_expand.append((key, subtype)) 150 else: 151 val = self.toAdapter.coerce(getattr(unit, key)) 152 fields.append(self.column_name(clsname, key)) 153 values.append(val) 145 val = self.toAdapter.coerce(getattr(unit, key)) 146 fields.append(self.column_name(clsname, key)) 147 values.append(val) 154 148 155 149 fields = ", ".join(fields) … … 162 156 % (clsname, cls.identifiers[0])) 163 157 setattr(unit, cls.identifiers[0], data[0][0]) 164 165 # Now that we have an ID, save our expanded tables.166 for key, subtype in to_expand:167 self.save_expanded(unit, key, subtype)168 158 169 159 # Schemas # trunk/storage/storesqlite.py
r192 r193 284 284 fields = [] 285 285 values = [] 286 # We have to delay expanded tables until we have an ID.287 to_expand = []288 286 for key in cls.properties: 289 287 if "AUTOINCREMENT" in self.typeAdapter.coerce(cls, key): 290 288 # Skip this field, since we're using AUTOINCREMENT 291 289 continue 292 subtype = self.expanded_columns.get((clsname, key)) 293 if subtype: 294 to_expand.append((key, subtype)) 295 else: 296 val = self.toAdapter.coerce(getattr(unit, key)) 297 fields.append(self.column_name(clsname, key)) 298 values.append(val) 290 val = self.toAdapter.coerce(getattr(unit, key)) 291 fields.append(self.column_name(clsname, key)) 292 values.append(val) 299 293 300 294 fields = ", ".join(fields) … … 309 303 new_id = conn.sqlite_last_insert_rowid() 310 304 setattr(unit, cls.identifiers[0], new_id) 311 312 # Now that we have an ID, save our expanded tables.313 for key, subtype in to_expand:314 self.save_expanded(unit, key, subtype, conn)315 305 316 306 # Schemas # trunk/test/test_storemsaccess.py
r192 r193 21 21 opts = {u'Connect': "PROVIDER=MICROSOFT.JET.OLEDB.4.0;" 22 22 "DATA SOURCE=zoo.mdb;", 23 u'Expanded Columns': "Animal.PreviousZoos:int",24 23 } 25 24 trunk/test/test_storeodbc.py
r192 r193 13 13 "Driver={Microsoft Access Driver (*.mdb)};" 14 14 "DBQ=zooodbc.mdb;Provider=MSDASQL;"), 15 u'Expanded Columns': "Animal.PreviousZoos:int",16 15 } 17 16 SM_class = "dejavu.storage.storeodbc.StorageManagerODBC" trunk/test/test_storesqlserver.py
r192 r193 10 10 "Initial Catalog=dejavu_test; " 11 11 "Data Source=REDROVER\\"), 12 u'Expanded Columns': "Animal.PreviousZoos:int",13 12 } 14 13 SM_class = "dejavu.storage.storeado.StorageManagerADO_SQLServer"
