| 1 |
"""Storage Managers for Dejavu.""" |
|---|
| 2 |
|
|---|
| 3 |
import datetime |
|---|
| 4 |
try: |
|---|
| 5 |
set |
|---|
| 6 |
except NameError: |
|---|
| 7 |
from sets import Set as set |
|---|
| 8 |
import types |
|---|
| 9 |
|
|---|
| 10 |
import dejavu |
|---|
| 11 |
from dejavu import errors, logflags, recur, sandboxes, xray |
|---|
| 12 |
from dejavu.containers import Graph |
|---|
| 13 |
from geniusql import logic, astwalk |
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
class StorageManager(object): |
|---|
| 17 |
"""A Manager base class for storing and retrieving Units. |
|---|
| 18 |
|
|---|
| 19 |
The base StorageManager class doesn't actually store anything; |
|---|
| 20 |
it needs to be subclassed. |
|---|
| 21 |
|
|---|
| 22 |
---- |
|---|
| 23 |
|
|---|
| 24 |
Regarding large systems: |
|---|
| 25 |
See http://www-db.cs.wisc.edu/cidr/cidr2007/papers/cidr07p15.pdf |
|---|
| 26 |
|
|---|
| 27 |
"A scale-agnostic programming abstraction must have the notion of |
|---|
| 28 |
entity as the boundary of atomicity." |
|---|
| 29 |
|
|---|
| 30 |
If you want Dejavu to function as a scale-aware lower layer, use Units |
|---|
| 31 |
to represent Pat Helland's "entities", and use them as a boundary for |
|---|
| 32 |
OLTP/CRUD operations like save and delete. DO NOT write StorageManagers |
|---|
| 33 |
which mix multiple entities into a single unit. Likewise, large-scale |
|---|
| 34 |
Dejavu apps should not expect atomic operations across/between Units. |
|---|
| 35 |
|
|---|
| 36 |
"...the scale-agnostic application must manage uncertainty |
|---|
| 37 |
itself using workflow [instead of distributed transactions] |
|---|
| 38 |
if it needs to reach agreement across multiple entities." |
|---|
| 39 |
""" |
|---|
| 40 |
|
|---|
| 41 |
def __init__(self, allOptions={}): |
|---|
| 42 |
self.classes = set() |
|---|
| 43 |
self.associations = Graph(directed=False) |
|---|
| 44 |
|
|---|
| 45 |
|
|---|
| 46 |
self.engine_functions = {} |
|---|
| 47 |
|
|---|
| 48 |
self.logflags = logflags.ERROR + logflags.IO |
|---|
| 49 |
|
|---|
| 50 |
def shutdown(self, conflicts='error'): |
|---|
| 51 |
"""Shut down all connections to internal storage. |
|---|
| 52 |
|
|---|
| 53 |
conflicts: see errors.conflict. |
|---|
| 54 |
""" |
|---|
| 55 |
pass |
|---|
| 56 |
|
|---|
| 57 |
def log(self, message): |
|---|
| 58 |
"""Default logger (writes to stdout). Feel free to replace.""" |
|---|
| 59 |
if isinstance(message, unicode): |
|---|
| 60 |
print message.encode('utf8') |
|---|
| 61 |
else: |
|---|
| 62 |
print message |
|---|
| 63 |
|
|---|
| 64 |
def new_sandbox(self): |
|---|
| 65 |
"""Return a new sandbox object bound to self.""" |
|---|
| 66 |
return sandboxes.Sandbox(self) |
|---|
| 67 |
|
|---|
| 68 |
|
|---|
| 69 |
|
|---|
| 70 |
def register(self, cls): |
|---|
| 71 |
"""Assert that Units of class 'cls' will be handled.""" |
|---|
| 72 |
try: |
|---|
| 73 |
|
|---|
| 74 |
nodename = self.db.name |
|---|
| 75 |
except AttributeError: |
|---|
| 76 |
nodename = self.__class__.__name__ |
|---|
| 77 |
|
|---|
| 78 |
if self.logflags & logflags.REGISTER: |
|---|
| 79 |
self.log(logflags.REGISTER.message(nodename, cls)) |
|---|
| 80 |
|
|---|
| 81 |
self.classes.add(cls) |
|---|
| 82 |
|
|---|
| 83 |
for ua in cls._associations.itervalues(): |
|---|
| 84 |
if getattr(ua, "register", True): |
|---|
| 85 |
self.associations.connect(cls, ua.farClass) |
|---|
| 86 |
|
|---|
| 87 |
def register_all(self, globals): |
|---|
| 88 |
"""Register each subclass of Unit in the given globals.""" |
|---|
| 89 |
seen = {} |
|---|
| 90 |
for obj in globals.itervalues(): |
|---|
| 91 |
if isinstance(obj, type) and issubclass(obj, dejavu.Unit): |
|---|
| 92 |
self.register(obj) |
|---|
| 93 |
seen[obj] = None |
|---|
| 94 |
return seen.keys() |
|---|
| 95 |
|
|---|
| 96 |
def class_by_name(self, classname): |
|---|
| 97 |
"""Return the class object for the given classname.""" |
|---|
| 98 |
for cls in self.classes: |
|---|
| 99 |
if cls.__name__ == classname: |
|---|
| 100 |
return cls |
|---|
| 101 |
raise KeyError("No registered class found for '%s'." % classname) |
|---|
| 102 |
|
|---|
| 103 |
def map(self, classes, conflicts='error'): |
|---|
| 104 |
"""Map classes to internal storage. |
|---|
| 105 |
|
|---|
| 106 |
conflicts: see errors.conflict. |
|---|
| 107 |
""" |
|---|
| 108 |
for cls in classes: |
|---|
| 109 |
if not self.has_storage(cls): |
|---|
| 110 |
if conflicts == 'repair': |
|---|
| 111 |
self.create_storage(cls) |
|---|
| 112 |
else: |
|---|
| 113 |
errors.conflict(conflicts, |
|---|
| 114 |
"%s: no storage found." % cls.__name__) |
|---|
| 115 |
|
|---|
| 116 |
def map_all(self, conflicts='error'): |
|---|
| 117 |
"""Map all registered classes to internal storage structures. |
|---|
| 118 |
|
|---|
| 119 |
Although classes can be mapped one at a time, in production it |
|---|
| 120 |
is often more useful to map all classes at application startup. |
|---|
| 121 |
Call this method to do so (but register all classes first). |
|---|
| 122 |
|
|---|
| 123 |
This method is idempotent, but that doesn't mean cheap. Try not |
|---|
| 124 |
to call it very often (once at app startup is usually enough). |
|---|
| 125 |
|
|---|
| 126 |
conflicts: see errors.conflict. |
|---|
| 127 |
""" |
|---|
| 128 |
self.map(self.classes, conflicts=conflicts) |
|---|
| 129 |
|
|---|
| 130 |
def create_database(self, conflicts='error'): |
|---|
| 131 |
"""Create internal structures for the entire database. |
|---|
| 132 |
|
|---|
| 133 |
conflicts: see errors.conflict. |
|---|
| 134 |
|
|---|
| 135 |
This method will NOT create storage for each class, nor will |
|---|
| 136 |
it create any dependent properties or indexes. |
|---|
| 137 |
""" |
|---|
| 138 |
raise NotImplementedError("%s has no create_database method." |
|---|
| 139 |
% self.__class__) |
|---|
| 140 |
|
|---|
| 141 |
def drop_database(self, conflicts='error'): |
|---|
| 142 |
"""Destroy internal structures for the entire database. |
|---|
| 143 |
|
|---|
| 144 |
conflicts: see errors.conflict. |
|---|
| 145 |
|
|---|
| 146 |
This method will also drop storage for each class, including |
|---|
| 147 |
all properties and indexes. |
|---|
| 148 |
""" |
|---|
| 149 |
raise NotImplementedError("%s has no drop_database method." |
|---|
| 150 |
% self.__class__) |
|---|
| 151 |
|
|---|
| 152 |
def create_storage(self, cls, conflicts='error'): |
|---|
| 153 |
"""Create internal structures for the given class. |
|---|
| 154 |
|
|---|
| 155 |
conflicts: see errors.conflict. |
|---|
| 156 |
|
|---|
| 157 |
This method will also create all dependent properties and indexes. |
|---|
| 158 |
""" |
|---|
| 159 |
raise NotImplementedError("%s has no create_storage method." |
|---|
| 160 |
% self.__class__) |
|---|
| 161 |
|
|---|
| 162 |
def has_storage(self, cls): |
|---|
| 163 |
"""If storage structures exist for the given class, return True.""" |
|---|
| 164 |
raise NotImplementedError("%s has no has_storage method." |
|---|
| 165 |
% self.__class__) |
|---|
| 166 |
|
|---|
| 167 |
def drop_storage(self, cls, conflicts='error'): |
|---|
| 168 |
"""Destroy internal structures for the given class. |
|---|
| 169 |
|
|---|
| 170 |
conflicts: see errors.conflict. |
|---|
| 171 |
|
|---|
| 172 |
This method will also drop all dependent properties and indexes. |
|---|
| 173 |
""" |
|---|
| 174 |
raise NotImplementedError("%s has no drop_storage method." |
|---|
| 175 |
% self.__class__) |
|---|
| 176 |
|
|---|
| 177 |
def add_property(self, cls, name, conflicts='error'): |
|---|
| 178 |
"""Add internal structures for the given property. |
|---|
| 179 |
|
|---|
| 180 |
conflicts: see errors.conflict. |
|---|
| 181 |
""" |
|---|
| 182 |
raise NotImplementedError("%s has no add_property method." |
|---|
| 183 |
% self.__class__) |
|---|
| 184 |
|
|---|
| 185 |
def has_property(self, cls, name): |
|---|
| 186 |
"""If storage structures exist for the given property, return True.""" |
|---|
| 187 |
raise NotImplementedError("%s has no has_property method." |
|---|
| 188 |
% self.__class__) |
|---|
| 189 |
|
|---|
| 190 |
def drop_property(self, cls, name, conflicts='error'): |
|---|
| 191 |
"""Destroy internal structures for the given property. |
|---|
| 192 |
|
|---|
| 193 |
conflicts: see errors.conflict. |
|---|
| 194 |
""" |
|---|
| 195 |
raise NotImplementedError("%s has no drop_property method." |
|---|
| 196 |
% self.__class__) |
|---|
| 197 |
|
|---|
| 198 |
def rename_property(self, cls, oldname, newname, conflicts='error'): |
|---|
| 199 |
"""Rename internal structures for the given property. |
|---|
| 200 |
|
|---|
| 201 |
conflicts: see errors.conflict. |
|---|
| 202 |
""" |
|---|
| 203 |
raise NotImplementedError("%s has no rename_property method." |
|---|
| 204 |
% self.__class__) |
|---|
| 205 |
|
|---|
| 206 |
def add_index(self, cls, name, conflicts='error'): |
|---|
| 207 |
"""Add an index to the given property. |
|---|
| 208 |
|
|---|
| 209 |
conflicts: see errors.conflict. |
|---|
| 210 |
""" |
|---|
| 211 |
raise NotImplementedError("%s has no add_index method." |
|---|
| 212 |
% self.__class__) |
|---|
| 213 |
|
|---|
| 214 |
def has_index(self, cls, name): |
|---|
| 215 |
"""If an index exists for the given property, return True.""" |
|---|
| 216 |
raise NotImplementedError("%s has no has_index method." |
|---|
| 217 |
% self.__class__) |
|---|
| 218 |
|
|---|
| 219 |
def drop_index(self, cls, name, conflicts='error'): |
|---|
| 220 |
"""Destroy any index on the given property. |
|---|
| 221 |
|
|---|
| 222 |
conflicts: see errors.conflict. |
|---|
| 223 |
""" |
|---|
| 224 |
raise NotImplementedError("%s has no drop_index method." |
|---|
| 225 |
% self.__class__) |
|---|
| 226 |
|
|---|
| 227 |
|
|---|
| 228 |
|
|---|
| 229 |
|
|---|
| 230 |
def reserve(self, unit): |
|---|
| 231 |
"""Reserve storage space for the Unit. |
|---|
| 232 |
|
|---|
| 233 |
The store should call unit.cleanse() if it saves the whole unit |
|---|
| 234 |
state on this call. |
|---|
| 235 |
""" |
|---|
| 236 |
raise NotImplementedError |
|---|
| 237 |
|
|---|
| 238 |
def save(self, unit, forceSave=False): |
|---|
| 239 |
"""Store the unit's property values.""" |
|---|
| 240 |
raise NotImplementedError |
|---|
| 241 |
|
|---|
| 242 |
def destroy(self, unit): |
|---|
| 243 |
"""Delete the unit.""" |
|---|
| 244 |
raise NotImplementedError |
|---|
| 245 |
|
|---|
| 246 |
def xrecall(self, classes, expr=None, order=None, limit=None, offset=None): |
|---|
| 247 |
"""Return an iterable of Units.""" |
|---|
| 248 |
if isinstance(classes, dejavu.UnitJoin): |
|---|
| 249 |
for unitrow in self._xmultirecall(classes, expr, order=order, |
|---|
| 250 |
limit=limit, offset=offset): |
|---|
| 251 |
yield unitrow |
|---|
| 252 |
return |
|---|
| 253 |
|
|---|
| 254 |
raise NotImplementedError |
|---|
| 255 |
|
|---|
| 256 |
def recall(self, classes, expr=None, order=None, limit=None, offset=None): |
|---|
| 257 |
"""Return a sequence of Unit instances which satisfy the expression.""" |
|---|
| 258 |
return [x for x in self.xrecall(classes, expr, order=order, |
|---|
| 259 |
limit=limit, offset=offset)] |
|---|
| 260 |
|
|---|
| 261 |
def unit(self, cls, **kwargs): |
|---|
| 262 |
"""A single Unit which matches the given kwargs, else None. |
|---|
| 263 |
|
|---|
| 264 |
The first Unit matching the kwargs is returned; if no Units match, |
|---|
| 265 |
None is returned. |
|---|
| 266 |
""" |
|---|
| 267 |
try: |
|---|
| 268 |
return self.xrecall(cls, logic.filter(**kwargs), limit=1).next() |
|---|
| 269 |
except StopIteration: |
|---|
| 270 |
return None |
|---|
| 271 |
|
|---|
| 272 |
def _sort_func(self, order): |
|---|
| 273 |
"""Return a function (for use with list.sort) from the given order.""" |
|---|
| 274 |
if order is None: |
|---|
| 275 |
return None |
|---|
| 276 |
elif isinstance(order, types.FunctionType): |
|---|
| 277 |
order = logic.Expression(order) |
|---|
| 278 |
return OrderDeparser(order).sort_func() |
|---|
| 279 |
elif isinstance(order, logic.Expression): |
|---|
| 280 |
return OrderDeparser(order).sort_func() |
|---|
| 281 |
elif isinstance(order, (list, tuple)): |
|---|
| 282 |
triples = [(0, attr.split(" ", 1)[0], attr.endswith(" DESC")) |
|---|
| 283 |
for attr in order] |
|---|
| 284 |
def sort_func(x, y): |
|---|
| 285 |
for index, attr, descending in triples: |
|---|
| 286 |
xval = getattr(x[index], attr) |
|---|
| 287 |
if xval is None: |
|---|
| 288 |
diff = -1 |
|---|
| 289 |
else: |
|---|
| 290 |
yval = getattr(y[index], attr) |
|---|
| 291 |
if yval is None: |
|---|
| 292 |
diff = 1 |
|---|
| 293 |
else: |
|---|
| 294 |
diff = cmp(xval, yval) |
|---|
| 295 |
if descending: |
|---|
| 296 |
diff = -diff |
|---|
| 297 |
if diff != 0: |
|---|
| 298 |
return diff |
|---|
| 299 |
return 0 |
|---|
| 300 |
return sort_func |
|---|
| 301 |
else: |
|---|
| 302 |
raise TypeError("The 'order' value %r is not one of the allowed " |
|---|
| 303 |
"types (list, lambda, None, or Expression)." % |
|---|
| 304 |
order) |
|---|
| 305 |
|
|---|
| 306 |
def _paginate(self, data, order=None, limit=None, offset=None, single=False): |
|---|
| 307 |
"""Manually apply ORDER, LIMIT, and OFFSET operators to a unit stream. |
|---|
| 308 |
|
|---|
| 309 |
data: an iterable of sequences of Unit instances. For example, |
|---|
| 310 |
[(ThingA(), ThingB()), ...] |
|---|
| 311 |
single: if False (the default), yield sequences of units. If True, |
|---|
| 312 |
yield single Units. |
|---|
| 313 |
|
|---|
| 314 |
This is a helper function for those Storage Managers which must |
|---|
| 315 |
provide their own implementation of ORDER, LIMIT, and OFFSET |
|---|
| 316 |
operators. If possible, faster native backends should be used. |
|---|
| 317 |
""" |
|---|
| 318 |
if order: |
|---|
| 319 |
|
|---|
| 320 |
data = list(data) |
|---|
| 321 |
data.sort(self._sort_func(order)) |
|---|
| 322 |
data = iter(data) |
|---|
| 323 |
elif offset: |
|---|
| 324 |
raise TypeError("Order argument expected when offset is provided.") |
|---|
| 325 |
|
|---|
| 326 |
try: |
|---|
| 327 |
for x in xrange(offset or 0): |
|---|
| 328 |
data.next() |
|---|
| 329 |
|
|---|
| 330 |
if limit is not None: |
|---|
| 331 |
for x in xrange(limit): |
|---|
| 332 |
if single: |
|---|
| 333 |
yield data.next()[0] |
|---|
| 334 |
else: |
|---|
| 335 |
yield data.next() |
|---|
| 336 |
else: |
|---|
| 337 |
for unitrow in data: |
|---|
| 338 |
if single: |
|---|
| 339 |
yield unitrow[0] |
|---|
| 340 |
else: |
|---|
| 341 |
yield unitrow |
|---|
| 342 |
except StopIteration: |
|---|
| 343 |
return |
|---|
| 344 |
|
|---|
| 345 |
|
|---|
| 346 |
|
|---|
| 347 |
|
|---|
| 348 |
|
|---|
| 349 |
|
|---|
| 350 |
|
|---|
| 351 |
|
|---|
| 352 |
|
|---|
| 353 |
def _combine(self, unitjoin, filters): |
|---|
| 354 |
"""Return (flat) rows of Unit objects for the given (recursive) join.""" |
|---|
| 355 |
cls1, cls2 = unitjoin.class1, unitjoin.class2 |
|---|
| 356 |
|
|---|
| 357 |
if isinstance(cls1, dejavu.UnitJoin): |
|---|
| 358 |
table1 = self._combine(cls1, filters) |
|---|
| 359 |
classlist1 = iter(cls1) |
|---|
| 360 |
else: |
|---|
| 361 |
table1 = [[x] for x in self.recall(cls1, filters[cls1])] |
|---|
| 362 |
classlist1 = [cls1] |
|---|
| 363 |
|
|---|
| 364 |
if isinstance(cls2, dejavu.UnitJoin): |
|---|
| 365 |
table2 = self._combine(cls2, filters) |
|---|
| 366 |
classlist2 = iter(cls2) |
|---|
| 367 |
else: |
|---|
| 368 |
table2 = [[x] for x in self.recall(cls2, filters[cls2])] |
|---|
| 369 |
classlist2 = [cls2] |
|---|
| 370 |
|
|---|
| 371 |
|
|---|
| 372 |
ua = None |
|---|
| 373 |
for indexA, clsA in enumerate(classlist1): |
|---|
| 374 |
for indexB, clsB in enumerate(classlist2): |
|---|
| 375 |
path = unitjoin.path or clsB.__name__ |
|---|
| 376 |
ua = clsA._associations.get(path, None) |
|---|
| 377 |
if ua: |
|---|
| 378 |
nearKey, farKey = ua.nearKey, ua.farKey |
|---|
| 379 |
break |
|---|
| 380 |
path = unitjoin.path or clsA.__name__ |
|---|
| 381 |
ua = clsB._associations.get(path, None) |
|---|
| 382 |
if ua: |
|---|
| 383 |
nearKey, farKey = ua.farKey, ua.nearKey |
|---|
| 384 |
break |
|---|
| 385 |
if ua: break |
|---|
| 386 |
if ua is None: |
|---|
| 387 |
msg = ("No association found between %s and %s." % (cls1, cls2)) |
|---|
| 388 |
raise errors.AssociationError(msg) |
|---|
| 389 |
|
|---|
| 390 |
|
|---|
| 391 |
if unitjoin.leftbiased is None: |
|---|
| 392 |
|
|---|
| 393 |
|
|---|
| 394 |
table2 = list(table2) |
|---|
| 395 |
for row1 in table1: |
|---|
| 396 |
nearVal = getattr(row1[indexA], nearKey) |
|---|
| 397 |
for row2 in table2: |
|---|
| 398 |
|
|---|
| 399 |
farVal = getattr(row2[indexB], farKey) |
|---|
| 400 |
if nearVal == farVal: |
|---|
| 401 |
yield row1 + row2 |
|---|
| 402 |
elif unitjoin.leftbiased is True: |
|---|
| 403 |
|
|---|
| 404 |
|
|---|
| 405 |
table2 = list(table2) |
|---|
| 406 |
for row1 in table1: |
|---|
| 407 |
nearVal = getattr(row1[indexA], nearKey) |
|---|
| 408 |
found = False |
|---|
| 409 |
for row2 in table2: |
|---|
| 410 |
|
|---|
| 411 |
farVal = getattr(row2[indexB], farKey) |
|---|
| 412 |
if nearVal == farVal: |
|---|
| 413 |
yield row1 + row2 |
|---|
| 414 |
found = True |
|---|
| 415 |
if not found: |
|---|
| 416 |
|
|---|
| 417 |
yield row1 + [unit.__class__() for unit in row2] |
|---|
| 418 |
else: |
|---|
| 419 |
|
|---|
| 420 |
|
|---|
| 421 |
table1 = list(table1) |
|---|
| 422 |
for row2 in table2: |
|---|
| 423 |
unitB = row2[indexB] |
|---|
| 424 |
farVal = getattr(unitB, farKey) |
|---|
| 425 |
found = False |
|---|
| 426 |
for row1 in table1: |
|---|
| 427 |
|
|---|
| 428 |
nearVal = getattr(row1[indexA], nearKey) |
|---|
| 429 |
if nearVal == farVal: |
|---|
| 430 |
yield row1 + row2 |
|---|
| 431 |
found = True |
|---|
| 432 |
if not found: |
|---|
| 433 |
|
|---|
| 434 |
yield [unit.__class__() for unit in row1] + row2 |
|---|
| 435 |
|
|---|
| 436 |
def _xmultirecall(self, classes, expr=None, order=None, limit=None, offset=None): |
|---|
| 437 |
"""Yield lists of units of the given classes which match expr.""" |
|---|
| 438 |
if not isinstance(expr, logic.Expression): |
|---|
| 439 |
expr = logic.Expression(expr) |
|---|
| 440 |
|
|---|
| 441 |
|
|---|
| 442 |
|
|---|
| 443 |
filters = dict([(cls, None) for cls in classes]) |
|---|
| 444 |
|
|---|
| 445 |
def _combine_inner(): |
|---|
| 446 |
for unitrow in self._combine(classes, filters): |
|---|
| 447 |
if expr(*unitrow): |
|---|
| 448 |
yield unitrow |
|---|
| 449 |
return self._paginate(_combine_inner(), order, limit, offset) |
|---|
| 450 |
|
|---|
| 451 |
def _multirecall(self, classes, expr=None, order=None, limit=None, offset=None): |
|---|
| 452 |
"""Return lists of units which satisfy the expression.""" |
|---|
| 453 |
return [t for t in self._xmultirecall(classes, expr=expr, order=order, |
|---|
| 454 |
limit=limit, offset=offset)] |
|---|
| 455 |
|
|---|
| 456 |
def xview(self, query, order=None, limit=None, offset=None, distinct=False): |
|---|
| 457 |
"""Yield property tuples for the given query.""" |
|---|
| 458 |
if not isinstance(query, dejavu.Query): |
|---|
| 459 |
query = dejavu.Query(*query) |
|---|
| 460 |
|
|---|
| 461 |
if self.logflags & logflags.VIEW: |
|---|
| 462 |
self.log(logflags.VIEW.message(query, distinct)) |
|---|
| 463 |
|
|---|
| 464 |
expr = query.restriction |
|---|
| 465 |
|
|---|
| 466 |
seen = {} |
|---|
| 467 |
|
|---|
| 468 |
|
|---|
| 469 |
|
|---|
| 470 |
if isinstance(query.relation, dejavu.UnitJoin): |
|---|
| 471 |
filters = dict([(cls, None) for cls in query.relation]) |
|---|
| 472 |
data = self._combine(query.relation, filters) |
|---|
| 473 |
if order: |
|---|
| 474 |
data = [unit for unit in data] |
|---|
| 475 |
data.sort(dejavu.sort(order)) |
|---|
| 476 |
data = iter(data) |
|---|
| 477 |
|
|---|
| 478 |
if isinstance(query.attributes, logic.Expression): |
|---|
| 479 |
def puller(): |
|---|
| 480 |
for unitrow in data: |
|---|
| 481 |
if expr is None or expr(*unitrow): |
|---|
| 482 |
datarow = tuple(query.attributes(*unitrow)) |
|---|
| 483 |
if distinct: |
|---|
| 484 |
if datarow not in seen: |
|---|
| 485 |
yield datarow |
|---|
| 486 |
seen[datarow] = None |
|---|
| 487 |
else: |
|---|
| 488 |
yield datarow |
|---|
| 489 |
elif query.attributes is None: |
|---|
| 490 |
|
|---|
| 491 |
choke |
|---|
| 492 |
else: |
|---|
| 493 |
def puller(): |
|---|
| 494 |
for unitrow in data: |
|---|
| 495 |
if expr is None or expr(*unitrow): |
|---|
| 496 |
datarow = [] |
|---|
| 497 |
for i, attrs in enumerate(query.attributes): |
|---|
| 498 |
unit = unitrow[i] |
|---|
| 499 |
if attrs is None: |
|---|
| 500 |
|
|---|
| 501 |
choke |
|---|
| 502 |
else: |
|---|
| 503 |
for attr in attrs: |
|---|
| 504 |
datarow.append(getattr(unit, attr)) |
|---|
| 505 |
datarow = tuple(datarow) |
|---|
| 506 |
if distinct: |
|---|
| 507 |
if datarow not in seen: |
|---|
| 508 |
yield datarow |
|---|
| 509 |
seen[datarow] = None |
|---|
| 510 |
else: |
|---|
| 511 |
yield datarow |
|---|
| 512 |
else: |
|---|
| 513 |
data = self.recall(query.relation, expr) |
|---|
| 514 |
if order: |
|---|
| 515 |
data.sort(dejavu.sort(order)) |
|---|
| 516 |
data = iter(data) |
|---|
| 517 |
|
|---|
| 518 |
if isinstance(query.attributes, logic.Expression): |
|---|
| 519 |
def puller(): |
|---|
| 520 |
for unit in data: |
|---|
| 521 |
if expr is None or expr(unit): |
|---|
| 522 |
datarow = tuple(query.attributes(unit)) |
|---|
| 523 |
if distinct: |
|---|
| 524 |
if datarow not in seen: |
|---|
| 525 |
yield datarow |
|---|
| 526 |
seen[datarow] = None |
|---|
| 527 |
else: |
|---|
| 528 |
yield datarow |
|---|
| 529 |
elif query.attributes is None: |
|---|
| 530 |
|
|---|
| 531 |
choke |
|---|
| 532 |
else: |
|---|
| 533 |
def puller(): |
|---|
| 534 |
for unit in data: |
|---|
| 535 |
if expr is None or expr(unit): |
|---|
| 536 |
|
|---|
| 537 |
datarow = tuple([getattr(unit, attr) |
|---|
| 538 |
for attr in query.attributes]) |
|---|
| 539 |
if distinct: |
|---|
| 540 |
if datarow not in seen: |
|---|
| 541 |
yield datarow |
|---|
| 542 |
seen[datarow] = None |
|---|
| 543 |
else: |
|---|
| 544 |
yield datarow |
|---|
| 545 |
|
|---|
| 546 |
ordered_data = puller() |
|---|
| 547 |
try: |
|---|
| 548 |
for x in xrange(offset or 0): |
|---|
| 549 |
ordered_data.next() |
|---|
| 550 |
|
|---|
| 551 |
if limit: |
|---|
| 552 |
for x in xrange(limit): |
|---|
| 553 |
yield ordered_data.next() |
|---|
| 554 |
else: |
|---|
| 555 |
for unit in ordered_data: |
|---|
| 556 |
yield unit |
|---|
| 557 |
except StopIteration: |
|---|
| 558 |
return |
|---|
| 559 |
|
|---|
| 560 |
def view(self, query, order=None, limit=None, offset=None, distinct=False): |
|---|
| 561 |
"""Return tuples of attribute values for the given query.""" |
|---|
| 562 |
return [x for x in self.xview(query, order=order, limit=limit, |
|---|
| 563 |
offset=offset, distinct=distinct)] |
|---|
| 564 |
|
|---|
| 565 |
def count(self, cls, expr=None): |
|---|
| 566 |
"""Number of Units of the given cls which match the given expr.""" |
|---|
| 567 |
if cls.identifiers: |
|---|
| 568 |
uniq = cls.identifiers |
|---|
| 569 |
else: |
|---|
| 570 |
uniq = cls._properties.keys() |
|---|
| 571 |
return len(self.view((cls, uniq, expr), distinct=True)) |
|---|
| 572 |
|
|---|
| 573 |
def range(self, cls, attr, expr=None): |
|---|
| 574 |
"""Distinct, non-None attr values (ordered and continuous, if possible). |
|---|
| 575 |
|
|---|
| 576 |
If the given attribute is a known discrete, ordered type |
|---|
| 577 |
(like int, long, datetime.date), this returns the closed interval: |
|---|
| 578 |
|
|---|
| 579 |
[min(attr), ..., max(attr)] |
|---|
| 580 |
|
|---|
| 581 |
That is, all possible values will be output between min and max, |
|---|
| 582 |
even if they do not appear in the dataset. |
|---|
| 583 |
|
|---|
| 584 |
If the given attribute is not reasonably discrete (e.g., str, |
|---|
| 585 |
unicode, or float) then all distinct, non-None values are returned |
|---|
| 586 |
(sorted, if possible). |
|---|
| 587 |
""" |
|---|
| 588 |
query = dejavu.Query(cls, [attr], expr) |
|---|
| 589 |
existing = [x[0] for x in self.xview(query, distinct=True) |
|---|
| 590 |
if x is not None] |
|---|
| 591 |
if not existing: |
|---|
| 592 |
return [] |
|---|
| 593 |
|
|---|
| 594 |
attr_type = getattr(cls, attr).type |
|---|
| 595 |
if issubclass(attr_type, (int, long)): |
|---|
| 596 |
return range(min(existing), max(existing) + 1) |
|---|
| 597 |
else: |
|---|
| 598 |
if issubclass(attr_type, datetime.date): |
|---|
| 599 |
def date_gen(): |
|---|
| 600 |
start, end = min(existing), max(existing) |
|---|
| 601 |
for d in range((end + 1) - start): |
|---|
| 602 |
yield start + datetime.timedelta(d) |
|---|
| 603 |
return date_gen() |
|---|
| 604 |
|
|---|
| 605 |
try: |
|---|
| 606 |
existing.sort() |
|---|
| 607 |
except TypeError: |
|---|
| 608 |
pass |
|---|
| 609 |
|
|---|
| 610 |
return existing |
|---|
| 611 |
|
|---|
| 612 |
def sum(self, cls, attr, expr=None): |
|---|
| 613 |
"""Sum of all non-None values for the given cls.attr.""" |
|---|
| 614 |
expr = logic.Expression(lambda x: getattr(x, attr) != None) + expr |
|---|
| 615 |
return sum([row[0] for row in |
|---|
| 616 |
self.xview(dejavu.Query(cls, (attr,), expr))]) |
|---|
| 617 |
|
|---|
| 618 |
|
|---|
| 619 |
|
|---|
| 620 |
|
|---|
| 621 |
|
|---|
| 622 |
start = None |
|---|
| 623 |
rollback = None |
|---|
| 624 |
commit = None |
|---|
| 625 |
|
|---|
| 626 |
|
|---|
| 627 |
class OrderDeparser(astwalk.ASTDeparser): |
|---|
| 628 |
"""Produce a sort function from a supplied logic.Expression object. |
|---|
| 629 |
|
|---|
| 630 |
Each positional argument in the Expression's function signature will be |
|---|
| 631 |
mapped to 'columns' in the list of lists being sorted. |
|---|
| 632 |
""" |
|---|
| 633 |
|
|---|
| 634 |
def __init__(self, expr): |
|---|
| 635 |
self.expr = expr |
|---|
| 636 |
astwalk.ASTDeparser.__init__(self, expr.ast) |
|---|
| 637 |
|
|---|
| 638 |
def sort_func(self): |
|---|
| 639 |
"""Walk self and return a function (for use with list.sort).""" |
|---|
| 640 |
root = self.ast.root |
|---|
| 641 |
if not isinstance(root, (astwalk.ast.Tuple, astwalk.ast.List)): |
|---|
| 642 |
raise ValueError("Attribute AST roots must be Tuple or List, " |
|---|
| 643 |
"not %s" % root.__class__.__name__) |
|---|
| 644 |
triples = [self.walk(term) for term in root.getChildren()] |
|---|
| 645 |
|
|---|
| 646 |
def sort_func(x, y): |
|---|
| 647 |
for index, attr, descending in triples: |
|---|
| 648 |
xval = getattr(x[index], attr) |
|---|
| 649 |
if xval is None: |
|---|
| 650 |
diff = -1 |
|---|
| 651 |
else: |
|---|
| 652 |
yval = getattr(y[index], attr) |
|---|
| 653 |
if yval is None: |
|---|
| 654 |
diff = 1 |
|---|
| 655 |
else: |
|---|
| 656 |
diff = cmp(xval, yval) |
|---|
| 657 |
if descending: |
|---|
| 658 |
diff = -diff |
|---|
| 659 |
if diff != 0: |
|---|
| 660 |
return diff |
|---|
| 661 |
return 0 |
|---|
| 662 |
return sort_func |
|---|
| 663 |
|
|---|
| 664 |
def walk(self, node): |
|---|
| 665 |
"""Walk the AST and return a string of code.""" |
|---|
| 666 |
nodetype = node.__class__.__name__ |
|---|
| 667 |
method = getattr(self, "visit_" + nodetype) |
|---|
| 668 |
args = node.getChildren() |
|---|
| 669 |
if self.verbose: |
|---|
| 670 |
self.debug(nodetype, args) |
|---|
| 671 |
return method(*args) |
|---|
| 672 |
|
|---|
| 673 |
def visit_Name(self, name): |
|---|
| 674 |
if name in self.ast.args: |
|---|
| 675 |
|
|---|
| 676 |
|
|---|
| 677 |
return self.ast.args.index(name) |
|---|
| 678 |
else: |
|---|
| 679 |
|
|---|
| 680 |
|
|---|
| 681 |
raise TypeError("Keyword args not allowed in order expressions.") |
|---|
| 682 |
|
|---|
| 683 |
def visit_Getattr(self, expr, attrname): |
|---|
| 684 |
expr = self.walk(expr) |
|---|
| 685 |
if isinstance(expr, int): |
|---|
| 686 |
|
|---|
| 687 |
|
|---|
| 688 |
|
|---|
| 689 |
return [expr, attrname, False] |
|---|
| 690 |
else: |
|---|
| 691 |
raise TypeError("%r.%r does not reference a positional argument." % |
|---|
| 692 |
(expr, attrname)) |
|---|
| 693 |
|
|---|
| 694 |
def visit_Const(self, value): |
|---|
| 695 |
return value |
|---|
| 696 |
|
|---|
| 697 |
def visit_CallFunc(self, func, *args): |
|---|
| 698 |
|
|---|
| 699 |
dstar_args = args[-1] |
|---|
| 700 |
star_args = args[-2] |
|---|
| 701 |
|
|---|
| 702 |
posargs = [] |
|---|
| 703 |
kwargs = {} |
|---|
| 704 |
for arg in args[:-2]: |
|---|
| 705 |
if isinstance(arg, astwalk.ast.Keyword): |
|---|
| 706 |
kwargs[arg.name] = self.walk(arg.value) |
|---|
| 707 |
else: |
|---|
| 708 |
posargs.append(self.walk(arg)) |
|---|
| 709 |
|
|---|
| 710 |
func = self.walk(func) |
|---|
| 711 |
|
|---|
| 712 |
|
|---|
| 713 |
if logic.builtins.get(func.__name__, None) is func: |
|---|
| 714 |
dispatch = getattr(self, "builtins_" + func.__name__, None) |
|---|
| 715 |
if dispatch: |
|---|
| 716 |
return dispatch(*posargs) |
|---|
| 717 |
|
|---|
| 718 |
funcname = func.__module__ + "_" + func.__name__ |
|---|
| 719 |
funcname = funcname.replace(".", "_") |
|---|
| 720 |
if funcname.startswith("_"): |
|---|
| 721 |
funcname = "func" + funcname |
|---|
| 722 |
dispatch = getattr(self, funcname, None) |
|---|
| 723 |
if dispatch: |
|---|
| 724 |
return dispatch(*posargs) |
|---|
| 725 |
|
|---|
| 726 |
raise CannotRepresent(func) |
|---|
| 727 |
|
|---|
| 728 |
|
|---|
| 729 |
|
|---|
| 730 |
def func__builtin___reversed(self, x): |
|---|
| 731 |
|
|---|
| 732 |
|
|---|
| 733 |
x[2] = True |
|---|
| 734 |
return x |
|---|
| 735 |
|
|---|
| 736 |
builtins_reversed = func__builtin___reversed |
|---|
| 737 |
|
|---|
| 738 |
|
|---|
| 739 |
|
|---|
| 740 |
class ProxyStorage(StorageManager): |
|---|
| 741 |
"""A Storage Manager which passes calls to another Storage Manager. |
|---|
| 742 |
|
|---|
| 743 |
database_scope: if True (the default), create_database and drop_database |
|---|
| 744 |
calls are passed on to self.nextstore. Set this to False when using |
|---|
| 745 |
a Proxy in a cyclic storage graph, where another StorageManager will |
|---|
| 746 |
create the proxied database. For example, if you vertically parition |
|---|
| 747 |
a set of classes so that some classes are cached and some are not: |
|---|
| 748 |
|
|---|
| 749 |
VerticalPartitioner |
|---|
| 750 |
| \ |
|---|
| 751 |
| ObjectCache--RAMStorage |
|---|
| 752 |
| / |
|---|
| 753 |
Master |
|---|
| 754 |
|
|---|
| 755 |
...then set this value to False so that the "Master" StorageManager |
|---|
| 756 |
receives only one create_database call. |
|---|
| 757 |
|
|---|
| 758 |
Future versions of Dejavu may grow "table_scope" or other attributes |
|---|
| 759 |
which work similarly against create/drop_storage/property. |
|---|
| 760 |
""" |
|---|
| 761 |
|
|---|
| 762 |
def __init__(self, allOptions={}): |
|---|
| 763 |
StorageManager.__init__(self, allOptions) |
|---|
| 764 |
self.nextstore = allOptions.get('Next Store') |
|---|
| 765 |
self.database_scope = allOptions.get('database_scope', True) |
|---|
| 766 |
|
|---|
| 767 |
def unit(self, cls, **kwargs): |
|---|
| 768 |
"""A single Unit which matches the given kwargs, else None. |
|---|
| 769 |
|
|---|
| 770 |
The first Unit matching the kwargs is returned; if no Units match, |
|---|
| 771 |
None is returned. |
|---|
| 772 |
""" |
|---|
| 773 |
return self.nextstore.unit(cls, **kwargs) |
|---|
| 774 |
|
|---|
| 775 |
def xrecall(self, classes, expr=None, order=None, limit=None, offset=None): |
|---|
| 776 |
"""Return an iterable of Units.""" |
|---|
| 777 |
if isinstance(classes, dejavu.UnitJoin): |
|---|
| 778 |
for unitrow in self._xmultirecall(classes, expr, order=order, |
|---|
| 779 |
limit=limit, offset=offset): |
|---|
| 780 |
yield unitrow |
|---|
| 781 |
return |
|---|
| 782 |
|
|---|
| 783 |
cls = classes |
|---|
| 784 |
if self.logflags & logflags.RECALL: |
|---|
| 785 |
self.log(logflags.RECALL.message(cls, expr)) |
|---|
| 786 |
for unit in self.nextstore.xrecall(cls, expr, order=order, |
|---|
| 787 |
limit=limit, offset=offset): |
|---|
| 788 |
yield unit |
|---|
| 789 |
|
|---|
| 790 |
def save(self, unit, forceSave=False): |
|---|
| 791 |
"""Store the unit.""" |
|---|
| 792 |
if self.logflags & logflags.SAVE: |
|---|
| 793 |
self.log(logflags.SAVE.message(unit, forceSave)) |
|---|
| 794 |
self.nextstore.save(unit, forceSave) |
|---|
| 795 |
|
|---|
| 796 |
def destroy(self, unit): |
|---|
| 797 |
"""Delete the unit.""" |
|---|
| 798 |
if self.logflags & logflags.DESTROY: |
|---|
| 799 |
self.log(logflags.DESTROY.message(unit)) |
|---|
| 800 |
self.nextstore.destroy(unit) |
|---|
| 801 |
|
|---|
| 802 |
def reserve(self, unit): |
|---|
| 803 |
"""Reserve storage space for the Unit.""" |
|---|
| 804 |
self.nextstore.reserve(unit) |
|---|
| 805 |
|
|---|
| 806 |
|
|---|
| 807 |
|
|---|
| 808 |
if self.logflags & logflags.RESERVE: |
|---|
| 809 |
self.log(logflags.RESERVE.message(unit)) |
|---|
| 810 |
|
|---|
| 811 |
def xview(self, query, order=None, limit=None, offset=None, distinct=False): |
|---|
| 812 |
"""Yield property tuples for the given query.""" |
|---|
| 813 |
if not isinstance(query, dejavu.Query): |
|---|
| 814 |
query = dejavu.Query(*query) |
|---|
| 815 |
|
|---|
| 816 |
if self.logflags & logflags.VIEW: |
|---|
| 817 |
self.log(logflags.VIEW.message(query, distinct)) |
|---|
| 818 |
return self.nextstore.xview(query, order=order, limit=limit, |
|---|
| 819 |
offset=offset, distinct=distinct) |
|---|
| 820 |
|
|---|
| 821 |
def _xmultirecall(self, classes, expr=None, order=None, limit=None, offset=None): |
|---|
| 822 |
"""Full inner join units from each class.""" |
|---|
| 823 |
if self.logflags & logflags.RECALL: |
|---|
| 824 |
self.log(logflags.RECALL.message(classes, expr)) |
|---|
| 825 |
return self.nextstore._xmultirecall(classes, expr, order, limit, offset) |
|---|
| 826 |
|
|---|
| 827 |
|
|---|
| 828 |
|
|---|
| 829 |
def map(self, classes, conflicts='error'): |
|---|
| 830 |
"""Map classes to internal storage. |
|---|
| 831 |
|
|---|
| 832 |
conflict: see errors.conflict. |
|---|
| 833 |
""" |
|---|
| 834 |
self.nextstore.map(classes, conflicts=conflicts) |
|---|
| 835 |
|
|---|
| 836 |
def create_database(self, conflicts='error'): |
|---|
| 837 |
"""Create internal structures for the entire database. |
|---|
| 838 |
|
|---|
| 839 |
conflicts: see errors.conflict. |
|---|
| 840 |
|
|---|
| 841 |
This method will NOT create storage for each class, nor will |
|---|
| 842 |
it create any dependent properties or indexes. |
|---|
| 843 |
""" |
|---|
| 844 |
if self.logflags & logflags.DDL: |
|---|
| 845 |
self.log(logflags.DDL.message("create database")) |
|---|
| 846 |
if self.database_scope: |
|---|
| 847 |
self.nextstore.create_database(conflicts=conflicts) |
|---|
| 848 |
|
|---|
| 849 |
def drop_database(self, conflicts='error'): |
|---|
| 850 |
"""Destroy internal structures for the entire database. |
|---|
| 851 |
|
|---|
| 852 |
conflicts: see errors.conflict. |
|---|
| 853 |
|
|---|
| 854 |
This method will also drop storage for each class, including |
|---|
| 855 |
all properties and indexes. |
|---|
| 856 |
""" |
|---|
| 857 |
if self.logflags & logflags.DDL: |
|---|
| 858 |
self.log(logflags.DDL.message("drop database")) |
|---|
| 859 |
if self.database_scope: |
|---|
| 860 |
self.nextstore.drop_database(conflicts=conflicts) |
|---|
| 861 |
|
|---|
| 862 |
def create_storage(self, cls, conflicts='error'): |
|---|
| 863 |
"""Create internal structures for the given class. |
|---|
| 864 |
|
|---|
| 865 |
conflicts: see errors.conflict. |
|---|
| 866 |
|
|---|
| 867 |
This method will also create all dependent properties and indexes. |
|---|
| 868 |
""" |
|---|
| 869 |
if self.logflags & logflags.DDL: |
|---|
| 870 |
self.log(logflags.DDL.message("create storage %r" % cls)) |
|---|
| 871 |
self.nextstore.create_storage(cls) |
|---|
| 872 |
|
|---|
| 873 |
def has_storage(self, cls): |
|---|
| 874 |
"""If storage structures exist for the given class, return True.""" |
|---|
| 875 |
return self.nextstore.has_storage(cls) |
|---|
| 876 |
|
|---|
| 877 |
def drop_storage(self, cls, conflicts='error'): |
|---|
| 878 |
"""Destroy internal structures for the given class. |
|---|
| 879 |
|
|---|
| 880 |
conflicts: see errors.conflict. |
|---|
| 881 |
|
|---|
| 882 |
This method will also drop all dependent properties and indexes. |
|---|
| 883 |
""" |
|---|
| 884 |
if self.logflags & logflags.DDL: |
|---|
| 885 |
self.log(logflags.DDL.message("drop storage %r" % cls)) |
|---|
| 886 |
self.nextstore.drop_storage(cls, conflicts=conflicts) |
|---|
| 887 |
|
|---|
| 888 |
def add_property(self, cls, name, conflicts='error'): |
|---|
| 889 |
"""Add internal structures for the given property. |
|---|
| 890 |
|
|---|
| 891 |
conflicts: see errors.conflict. |
|---|
| 892 |
""" |
|---|
| 893 |
if self.logflags & logflags.DDL: |
|---|
| 894 |
self.log(logflags.DDL.message("add property %r %r" % (cls, name))) |
|---|
| 895 |
self.nextstore.add_property(cls, name, conflicts=conflicts) |
|---|
| 896 |
|
|---|
| 897 |
def has_property(self, cls, name): |
|---|
| 898 |
"""If storage structures exist for the given property, return True.""" |
|---|
| 899 |
return self.nextstore.has_property(cls, name) |
|---|
| 900 |
|
|---|
| 901 |
def drop_property(self, cls, name, conflicts='error'): |
|---|
| 902 |
"""Destroy internal structures for the given property. |
|---|
| 903 |
|
|---|
| 904 |
conflicts: see errors.conflict. |
|---|
| 905 |
""" |
|---|
| 906 |
if self.logflags & logflags.DDL: |
|---|
| 907 |
self.log(logflags.DDL.message("drop property %r %r" % (cls, name))) |
|---|
| 908 |
self.nextstore.drop_property(cls, name, conflicts=conflicts) |
|---|
| 909 |
|
|---|
| 910 |
def rename_property(self, cls, oldname, newname, conflicts='error'): |
|---|
| 911 |
"""Rename internal structures for the given property. |
|---|
| 912 |
|
|---|
| 913 |
conflicts: see errors.conflict. |
|---|
| 914 |
""" |
|---|
| 915 |
if self.logflags & logflags.DDL: |
|---|
| 916 |
self.log(logflags.DDL.message("rename property %r from %r to %r" % |
|---|
| 917 |
(cls, oldname, newname))) |
|---|
| 918 |
self.nextstore.rename_property(cls, oldname, newname, conflicts=conflicts) |
|---|
| 919 |
|
|---|
| 920 |
def shutdown(self, conflicts='error'): |
|---|
| 921 |
"""Shut down all connections to internal storage. |
|---|
| 922 |
|
|---|
| 923 |
conflicts: see errors.conflict. |
|---|
| 924 |
""" |
|---|
| 925 |
self.nextstore.shutdown(conflicts=conflicts) |
|---|
| 926 |
|
|---|
| 927 |
def add_index(self, cls, name, conflicts='error'): |
|---|
| 928 |
"""Add an index to the given property. |
|---|
| 929 |
|
|---|
| 930 |
conflicts: see errors.conflict. |
|---|
| 931 |
""" |
|---|
| 932 |
self.nextstore.add_index(cls, name, conflicts=conflicts) |
|---|
| 933 |
|
|---|
| 934 |
def has_index(self, cls, name): |
|---|
| 935 |
"""If an index exists for the given property, return True.""" |
|---|
| 936 |
return self.nextstore.has_index(cls, name) |
|---|
| 937 |
|
|---|
| 938 |
def drop_index(self, cls, name, conflicts='error'): |
|---|
| 939 |
"""Destroy any index on the given property. |
|---|
| 940 |
|
|---|
| 941 |
conflicts: see errors.conflict. |
|---|
| 942 |
""" |
|---|
| 943 |
self.nextstore.drop_index(cls, name, conflicts=conflicts) |
|---|
| 944 |
|
|---|
| 945 |
def start(self, isolation=None): |
|---|
| 946 |
if self.nextstore.start: |
|---|
| 947 |
self.nextstore.start(isolation) |
|---|
| 948 |
|
|---|
| 949 |
def rollback(self): |
|---|
| 950 |
if self.nextstore.rollback: |
|---|
| 951 |
self.nextstore.rollback() |
|---|
| 952 |
|
|---|
| 953 |
def commit(self): |
|---|
| 954 |
if self.nextstore.commit: |
|---|
| 955 |
self.nextstore.commit() |
|---|
| 956 |
|
|---|
| 957 |
|
|---|
| 958 |
class Version(object): |
|---|
| 959 |
|
|---|
| 960 |
def __init__(self, atoms): |
|---|
| 961 |
if isinstance(atoms, (int, float)): |
|---|
| 962 |
atoms = str(atoms) |
|---|
| 963 |
if isinstance(atoms, basestring): |
|---|
| 964 |
import re |
|---|
| 965 |
self.atoms = re.split(r'\W', atoms) |
|---|
| 966 |
else: |
|---|
| 967 |
self.atoms = [str(x) for x in atoms] |
|---|
| 968 |
|
|---|
| 969 |
def __str__(self): |
|---|
| 970 |
return ".".join([str(x) for x in self.atoms]) |
|---|
| 971 |
|
|---|
| 972 |
def __cmp__(self, other): |
|---|
| 973 |
cls = self.__class__ |
|---|
| 974 |
if not isinstance(other, cls): |
|---|
| 975 |
|
|---|
| 976 |
other = cls(other) |
|---|
| 977 |
|
|---|
| 978 |
index = 0 |
|---|
| 979 |
while index < len(self.atoms) and index < len(other.atoms): |
|---|
| 980 |
mine, theirs = self.atoms[index], other.atoms[index] |
|---|
| 981 |
if mine.isdigit() and theirs.isdigit(): |
|---|
| 982 |
mine, theirs = int(mine), int(theirs) |
|---|
| 983 |
if mine < theirs: |
|---|
| 984 |
return -1 |
|---|
| 985 |
if mine > theirs: |
|---|
| 986 |
return 1 |
|---|
| 987 |
index += 1 |
|---|
| 988 |
if index < len(other.atoms): |
|---|
| 989 |
return -1 |
|---|
| 990 |
if index < len(self.atoms): |
|---|
| 991 |
return 1 |
|---|
| 992 |
return 0 |
|---|
| 993 |
|
|---|
| 994 |
|
|---|
| 995 |
managers = { |
|---|
| 996 |
"aged": "dejavu.storage.caching.AgedCache", |
|---|
| 997 |
"cache": "dejavu.storage.caching.ObjectCache", |
|---|
| 998 |
"caching": "dejavu.storage.caching.ObjectCache", |
|---|
| 999 |
"burned": "dejavu.storage.caching.BurnedCache", |
|---|
| 1000 |
"proxy": ProxyStorage, |
|---|
| 1001 |
|
|---|
| 1002 |
"access": "dejavu.storage.storeado.StorageManagerADO_MSAccess", |
|---|
| 1003 |
"msaccess": "dejavu.storage.storeado.StorageManagerADO_MSAccess", |
|---|
| 1004 |
|
|---|
| 1005 |
"firebird": "dejavu.storage.storefirebird.StorageManagerFirebird", |
|---|
| 1006 |
"mysql": "dejavu.storage.storemysql.StorageManagerMySQL", |
|---|
| 1007 |
|
|---|
| 1008 |
"postgres": "dejavu.storage.storepypgsql.StorageManagerPgSQL", |
|---|
| 1009 |
"postgresql": "dejavu.storage.storepypgsql.StorageManagerPgSQL", |
|---|
| 1010 |
"pypgsql": "dejavu.storage.storepypgsql.StorageManagerPgSQL", |
|---|
| 1011 |
|
|---|
| 1012 |
"psycopg": "dejavu.storage.storepsycopg.StorageManagerPsycoPg", |
|---|
| 1013 |
"psycopg2": "dejavu.storage.storepsycopg.StorageManagerPsycoPg", |
|---|
| 1014 |
|
|---|
| 1015 |
"ram": "dejavu.storage.storeram.RAMStorage", |
|---|
| 1016 |
"shelve": "dejavu.storage.storeshelve.StorageManagerShelve", |
|---|
| 1017 |
"sqlite": "dejavu.storage.storesqlite.StorageManagerSQLite", |
|---|
| 1018 |
|
|---|
| 1019 |
"sqlserver": "dejavu.storage.storeado.StorageManagerADO_SQLServer", |
|---|
| 1020 |
"mssql": "dejavu.storage.storeado.StorageManagerADO_SQLServer", |
|---|
| 1021 |
|
|---|
| 1022 |
"folders": "dejavu.storage.storefs.StorageManagerFolders", |
|---|
| 1023 |
|
|---|
| 1024 |
"json": "dejavu.storage.storejson.StorageManagerJSON", |
|---|
| 1025 |
|
|---|
| 1026 |
"memcache": "dejavu.storage.storememcached.MemcachedStorageManager", |
|---|
| 1027 |
"memcached": "dejavu.storage.storememcached.MemcachedStorageManager", |
|---|
| 1028 |
} |
|---|
| 1029 |
|
|---|
| 1030 |
|
|---|
| 1031 |
def resolve(store, options=None): |
|---|
| 1032 |
"""Return a StorageManager for the given name, classname, class, or SM.""" |
|---|
| 1033 |
if isinstance(store, basestring): |
|---|
| 1034 |
if store in managers: |
|---|
| 1035 |
store = managers[store] |
|---|
| 1036 |
|
|---|
| 1037 |
if isinstance(store, basestring): |
|---|
| 1038 |
store = xray.classes(store)(options or {}) |
|---|
| 1039 |
else: |
|---|
| 1040 |
import types |
|---|
| 1041 |
if isinstance(store, (type, types.ClassType)): |
|---|
| 1042 |
store = store(options or {}) |
|---|
| 1043 |
|
|---|
| 1044 |
return store |
|---|
| 1045 |
|
|---|