`0001"""
0002sqlobject.sqlbuilder
0003--------------------
0004
0005:author: Ian Bicking <ianb@colorstudy.com>
0006
0007Builds SQL expressions from normal Python expressions.
0008
0009Disclaimer
0010----------
0011
0012This program is free software; you can redistribute it and/or modify
0013it under the terms of the GNU Lesser General Public License as
0014published by the Free Software Foundation; either version 2.1 of the
0015License, or (at your option any later version.
0016
0017This program is distributed in the hope that it will be useful,
0018but WITHOUT ANY WARRANTY; without even the implied warranty of
0019MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0020GNU General Public License for more details.
0021
0022You should have received a copy of the GNU Lesser General Public
0023License along with this program; if not, write to the Free Software
0024Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
0025USA.
0026
0027Instructions
0028------------
0029
0030To begin a SQL expression, you must use some sort of SQL object -- a
0031field, table, or SQL statement (``SELECT``, ``INSERT``, etc.) You can
0032then use normal operators, with the exception of: `and`, `or`, `not`,
0033and `in`. You can use the `AND`, `OR`, `NOT`, and `IN` functions
0034instead, or you can also use `&`, `|`, and `~` for `and`, `or`, and
0035`not` respectively (however -- the precidence for these operators
0036doesn't work as you would want, so you must use many parenthesis).
0037
0038To create a sql field, table, or constant/function, use the namespaces
0039`table`, `const`, and `func`. For instance, ``table.address`` refers
0040to the ``address`` table, and ``table.address.state`` refers to the
0041``state`` field in the address table. ``const.NULL`` is the ``NULL``
0042SQL constant, and ``func.NOW()`` is the ``NOW()`` function call
0043(`const` and `func` are actually identicle, but the two names are
0044provided for clarity). Once you create this object, expressions
0045formed with it will produce SQL statements.
0046
0047The ``sqlrepr(obj)`` function gets the SQL representation of these
0048objects, as well as the proper SQL representation of basic Python
0049types (None==NULL).
0050
0051There are a number of DB-specific SQL features that this does not
0052implement. There are a bunch of normal ANSI features also not present.
0053
0054See the bottom of this module for some examples, and run it (i.e.
0055``python sql.py``) to see the results of those examples.
0056
0057"""
0058
0059########################################
0060## Constants
0061########################################
0062
0063import fnmatch
0064import operator
0065import re
0066import threading
0067import types
0068import weakref
0069
0070import classregistry
0071from converters import registerConverter, sqlrepr, quote_str, unquote_str
0072
0073
0074class VersionError(Exception):
0075 pass
0076class NoDefault:
0077 pass
0078
0079
0080class SQLObjectState(object):
0081 def __init__(self, soObject, connection=None):
0082 self.soObject = weakref.proxy(soObject)
0083 self.connection = connection
0084
0085
0086safeSQLRE = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_\.]*$')
0087def sqlIdentifier(obj):
0088 # some db drivers return unicode column names
0089 return isinstance(obj, basestring) and bool(safeSQLRE.search(obj.strip()))
0090
0091
0092def execute(expr, executor):
0093 if hasattr(expr, 'execute'):
0094 return expr.execute(executor)
0095 else:
0096 return expr
0097
0098
0099def _str_or_sqlrepr(expr, db):
0100 if isinstance(expr, basestring):
0101 return expr
0102 return sqlrepr(expr, db)
0103
0104########################################
0105## Expression generation
0106########################################
0107
0108class SQLExpression:
0109 def __add__(self, other):
0110 return SQLOp("+", self, other)
0111 def __radd__(self, other):
0112 return SQLOp("+", other, self)
0113 def __sub__(self, other):
0114 return SQLOp("-", self, other)
0115 def __rsub__(self, other):
0116 return SQLOp("-", other, self)
0117 def __mul__(self, other):
0118 return SQLOp("*", self, other)
0119 def __rmul__(self, other):
0120 return SQLOp("*", other, self)
0121 def __div__(self, other):
0122 return SQLOp("/", self, other)
0123 def __rdiv__(self, other):
0124 return SQLOp("/", other, self)
0125 def __pos__(self):
0126 return SQLPrefix("+", self)
0127 def __neg__(self):
0128 return SQLPrefix("-", self)
0129 def __pow__(self, other):
0130 return SQLConstant("POW")(self, other)
0131 def __rpow__(self, other):
0132 return SQLConstant("POW")(other, self)
0133 def __abs__(self):
0134 return SQLConstant("ABS")(self)
0135 def __mod__(self, other):
0136 return SQLModulo(self, other)
0137 def __rmod__(self, other):
0138 return SQLConstant("MOD")(other, self)
0139
0140 def __lt__(self, other):
0141 return SQLOp("<", self, other)
0142 def __le__(self, other):
0143 return SQLOp("<=", self, other)
0144 def __gt__(self, other):
0145 return SQLOp(">", self, other)
0146 def __ge__(self, other):
0147 return SQLOp(">=", self, other)
0148 def __eq__(self, other):
0149 if other is None:
0150 return ISNULL(self)
0151 else:
0152 return SQLOp("=", self, other)
0153 def __ne__(self, other):
0154 if other is None:
0155 return ISNOTNULL(self)
0156 else:
0157 return SQLOp("<>", self, other)
0158
0159 def __and__(self, other):
0160 return SQLOp("AND", self, other)
0161 def __rand__(self, other):
0162 return SQLOp("AND", other, self)
0163 def __or__(self, other):
0164 return SQLOp("OR", self, other)
0165 def __ror__(self, other):
0166 return SQLOp("OR", other, self)
0167 def __invert__(self):
0168 return SQLPrefix("NOT", self)
0169
0170 def __call__(self, *args):
0171 return SQLCall(self, args)
0172
0173 def __repr__(self):
0174 try:
0175 return self.__sqlrepr__(None)
0176 except AssertionError:
0177 return '<%s %s>' % (
0178 self.__class__.__name__, hex(id(self))[2:])
0179
0180 def __str__(self):
0181 return repr(self)
0182
0183 def __cmp__(self, other):
0184 raise VersionError, "Python 2.1+ required"
0185 def __rcmp__(self, other):
0186 raise VersionError, "Python 2.1+ required"
0187
0188 def startswith(self, s):
0189 return STARTSWITH(self, s)
0190 def endswith(self, s):
0191 return ENDSWITH(self, s)
0192 def contains(self, s):
0193 return CONTAINSSTRING(self, s)
0194
0195 def components(self):
0196 return []
0197
0198 def tablesUsed(self, db):
0199 return self.tablesUsedSet(db)
0200 def tablesUsedSet(self, db):
0201 tables = set()
0202 for table in self.tablesUsedImmediate():
0203 if hasattr(table, '__sqlrepr__'):
0204 table = sqlrepr(table, db)
0205 tables.add(table)
0206 for component in self.components():
0207 tables.update(tablesUsedSet(component, db))
0208 return tables
0209 def tablesUsedImmediate(self):
0210 return []
0211
0212#######################################
0213# Converter for SQLExpression instances
0214#######################################
0215
0216def SQLExprConverter(value, db):
0217 return value.__sqlrepr__()
0218
0219registerConverter(SQLExpression, SQLExprConverter)
0220
0221def tablesUsedSet(obj, db):
0222 if hasattr(obj, "tablesUsedSet"):
0223 return obj.tablesUsedSet(db)
0224 else:
0225 return {}
0226
0227operatorMap = {
0228 "+": operator.add,
0229 "/": operator.div,
0230 "-": operator.sub,
0231 "*": operator.mul,
0232 "<": operator.lt,
0233 "<=": operator.le,
0234 "=": operator.eq,
0235 "!=": operator.ne,
0236 ">=": operator.ge,
0237 ">": operator.gt,
0238 "IN": operator.contains,
0239 "IS": operator.eq,
0240 }
0241
0242class SQLOp(SQLExpression):
0243 def __init__(self, op, expr1, expr2):
0244 self.op = op.upper()
0245 self.expr1 = expr1
0246 self.expr2 = expr2
0247 def __sqlrepr__(self, db):
0248 s1 = sqlrepr(self.expr1, db)
0249 s2 = sqlrepr(self.expr2, db)
0250 if s1[0] != '(' and s1 != 'NULL':
0251 s1 = '(' + s1 + ')'
0252 if s2[0] != '(' and s2 != 'NULL':
0253 s2 = '(' + s2 + ')'
0254 return "(%s %s %s)" % (s1, self.op, s2)
0255 def components(self):
0256 return [self.expr1, self.expr2]
0257 def execute(self, executor):
0258 if self.op == "AND":
0259 return execute(self.expr1, executor) and execute(self.expr2, executor)
0261 elif self.op == "OR":
0262 return execute(self.expr1, executor) or execute(self.expr2, executor)
0264 else:
0265 return operatorMap[self.op.upper()](execute(self.expr1, executor),
0266 execute(self.expr2, executor))
0267
0268class SQLModulo(SQLOp):
0269 def __init__(self, expr1, expr2):
0270 SQLOp.__init__(self, '%', expr1, expr2)
0271 def __sqlrepr__(self, db):
0272 if db == 'sqlite':
0273 return SQLOp.__sqlrepr__(self, db)
0274 s1 = sqlrepr(self.expr1, db)
0275 s2 = sqlrepr(self.expr2, db)
0276 return "MOD(%s, %s)" % (s1, s2)
0277
0278registerConverter(SQLOp, SQLExprConverter)
0279registerConverter(SQLModulo, SQLExprConverter)
0280
0281class SQLCall(SQLExpression):
0282 def __init__(self, expr, args):
0283 self.expr = expr
0284 self.args = args
0285 def __sqlrepr__(self, db):
0286 return "%s%s" % (sqlrepr(self.expr, db), sqlrepr(self.args, db))
0287 def components(self):
0288 return [self.expr] + list(self.args)
0289 def execute(self, executor):
0290 raise ValueError, "I don't yet know how to locally execute functions"
0291
0292registerConverter(SQLCall, SQLExprConverter)
0293
0294class SQLPrefix(SQLExpression):
0295 def __init__(self, prefix, expr):
0296 self.prefix = prefix
0297 self.expr = expr
0298 def __sqlrepr__(self, db):
0299 return "%s %s" % (self.prefix, sqlrepr(self.expr, db))
0300 def components(self):
0301 return [self.expr]
0302 def execute(self, executor):
0303 expr = execute(self.expr, executor)
0304 if prefix == "+":
0305 return expr
0306 elif prefix == "-":
0307 return -expr
0308 elif prefix.upper() == "NOT":
0309 return not expr
0310
0311registerConverter(SQLPrefix, SQLExprConverter)
0312
0313class SQLConstant(SQLExpression):
0314 def __init__(self, const):
0315 self.const = const
0316 def __sqlrepr__(self, db):
0317 return self.const
0318 def execute(self, executor):
0319 raise ValueError, "I don't yet know how to execute SQL constants"
0320
0321registerConverter(SQLConstant, SQLExprConverter)
0322
0323class SQLTrueClauseClass(SQLExpression):
0324 def __sqlrepr__(self, db):
0325 return "1 = 1"
0326 def execute(self, executor):
0327 return 1
0328
0329SQLTrueClause = SQLTrueClauseClass()
0330
0331registerConverter(SQLTrueClauseClass, SQLExprConverter)
0332
0333########################################
0334## Namespaces
0335########################################
0336
0337class Field(SQLExpression):
0338 def __init__(self, tableName, fieldName):
0339 self.tableName = tableName
0340 self.fieldName = fieldName
0341 def __sqlrepr__(self, db):
0342 return self.tableName + "." + self.fieldName
0343 def tablesUsedImmediate(self):
0344 return [self.tableName]
0345 def execute(self, executor):
0346 return executor.field(self.tableName, self.fieldName)
0347
0348class SQLObjectField(Field):
0349 def __init__(self, tableName, fieldName, original, soClass, column):
0350 Field.__init__(self, tableName, fieldName)
0351 self.original = original
0352 self.soClass = soClass
0353 self.column = column
0354 def _from_python(self, value):
0355 column = self.column
0356 if not isinstance(value, SQLExpression) and column and column.from_python:
0357 value = column.from_python(value, SQLObjectState(self.soClass))
0358 return value
0359 def __eq__(self, other):
0360 if other is None:
0361 return ISNULL(self)
0362 other = self._from_python(other)
0363 return SQLOp('=', self, other)
0364 def __ne__(self, other):
0365 if other is None:
0366 return ISNOTNULL(self)
0367 other = self._from_python(other)
0368 return SQLOp('<>', self, other)
0369 def startswith(self, s):
0370 s = self._from_python(s)
0371 return STARTSWITH(self, s)
0372 def endswith(self, s):
0373 s = self._from_python(s)
0374 return ENDSWITH(self, s)
0375 def contains(self, s):
0376 s = self._from_python(s)
0377 return CONTAINSSTRING(self, s)
0378
0379registerConverter(SQLObjectField, SQLExprConverter)
0380
0381
0382class Table(SQLExpression):
0383 FieldClass = Field
0384
0385 def __init__(self, tableName):
0386 self.tableName = tableName
0387 def __getattr__(self, attr):
0388 if attr.startswith('__'):
0389 raise AttributeError
0390 return self.FieldClass(self.tableName, attr)
0391 def __sqlrepr__(self, db):
0392 return _str_or_sqlrepr(self.tableName, db)
0393 def execute(self, executor):
0394 raise ValueError, "Tables don't have values"
0395
0396class SQLObjectTable(Table):
0397 FieldClass = SQLObjectField
0398
0399 def __init__(self, soClass):
0400 self.soClass = soClass
0401 assert soClass.sqlmeta.table, (
0402 "Bad table name in class %r: %r"
0403 % (soClass, soClass.sqlmeta.table))
0404 Table.__init__(self, soClass.sqlmeta.table)
0405
0406 def __getattr__(self, attr):
0407 if attr.startswith('__'):
0408 raise AttributeError
0409 if attr == 'id':
0410 return self._getattrFromID(attr)
0411 elif attr in self.soClass.sqlmeta.columns:
0412 column = self.soClass.sqlmeta.columns[attr]
0413 return self._getattrFromColumn(column, attr)
0414 elif attr+'ID' in [k for (k, v) in self.soClass.sqlmeta.columns.items() if v.foreignKey]:
0415 attr += 'ID'
0416 column = self.soClass.sqlmeta.columns[attr]
0417 return self._getattrFromColumn(column, attr)
0418 else:
0419 raise AttributeError("%s instance has no attribute '%s'" % (self.soClass.__name__, attr))
0420
0421 def _getattrFromID(self, attr):
0422 return self.FieldClass(self.tableName, self.soClass.sqlmeta.idName, attr, self.soClass, None)
0423
0424 def _getattrFromColumn(self, column, attr):
0425 return self.FieldClass(self.tableName, column.dbName, attr, self.soClass, column)
0426
0427class SQLObjectTableWithJoins(SQLObjectTable):
0428
0429 def __getattr__(self, attr):
0430 if attr+'ID' in [k for (k, v) in self.soClass.sqlmeta.columns.items() if v.foreignKey]:
0431 column = self.soClass.sqlmeta.columns[attr+'ID']
0432 return self._getattrFromForeignKey(column, attr)
0433 elif attr in [x.joinMethodName for x in self.soClass.sqlmeta.joins]:
0434 join = [x for x in self.soClass.sqlmeta.joins if x.joinMethodName == attr][0]
0435 return self._getattrFromJoin(join, attr)
0436 else:
0437 return SQLObjectTable.__getattr__(self, attr)
0438
0439 def _getattrFromForeignKey(self, column, attr):
0440 ret = getattr(self, column.name) == getattr(self.soClass, '_SO_class_'+column.foreignKey).q.id
0442 return ret
0443
0444 def _getattrFromJoin(self, join, attr):
0445 if hasattr(join, 'otherColumn'):
0446 return AND(join.otherClass.q.id == Field(join.intermediateTable, join.otherColumn),
0447 Field(join.intermediateTable, join.joinColumn) == self.soClass.q.id)
0448 else:
0449 return getattr(join.otherClass.q, join.joinColumn)==self.soClass.q.id
0450
0451class TableSpace:
0452 TableClass = Table
0453
0454 def __getattr__(self, attr):
0455 if attr.startswith('__'):
0456 raise AttributeError
0457 return self.TableClass(attr)
0458
0459class ConstantSpace:
0460 def __getattr__(self, attr):
0461 if attr.startswith('__'):
0462 raise AttributeError
0463 return SQLConstant(attr)
0464
0465
0466########################################
0467## Table aliases
0468########################################
0469
0470class AliasField(Field):
0471 def __init__(self, tableName, fieldName, alias, aliasTable):
0472 Field.__init__(self, tableName, fieldName)
0473 self.alias = alias
0474 self.aliasTable = aliasTable
0475
0476 def __sqlrepr__(self, db):
0477 fieldName = self.fieldName
0478 if isinstance(fieldName, SQLExpression):
0479 fieldName = sqlrepr(fieldName, db)
0480 return self.alias + "." + fieldName
0481
0482 def tablesUsedImmediate(self):
0483 return [self.aliasTable]
0484
0485class AliasTable(Table):
0486 as_string = '' # set it to "AS" if your database requires it
0487 FieldClass = AliasField
0488
0489 _alias_lock = threading.Lock()
0490 _alias_counter = 0
0491
0492 def __init__(self, table, alias=None):
0493 if hasattr(table, "sqlmeta"):
0494 tableName = SQLConstant(table.sqlmeta.table)
0495 elif isinstance(table, (Select, Union)):
0496 assert alias is not None, "Alias name cannot be constructed from Select instances, please provide 'alias' kw."
0497 tableName = Subquery('', table)
0498 table = None
0499 else:
0500 tableName = SQLConstant(table)
0501 table = None
0502 Table.__init__(self, tableName)
0503 self.table = table
0504 if alias is None:
0505 self._alias_lock.acquire()
0506 try:
0507 AliasTable._alias_counter += 1
0508 alias = "%s_alias%d" % (tableName, AliasTable._alias_counter)
0509 finally:
0510 self._alias_lock.release()
0511 self.alias = alias
0512
0513 def __getattr__(self, attr):
0514 if attr.startswith('__'):
0515 raise AttributeError
0516 if self.table:
0517 attr = getattr(self.table.q, attr).fieldName
0518 return self.FieldClass(self.tableName, attr, self.alias, self)
0519
0520 def __sqlrepr__(self, db):
0521 return "%s %s %s" % (sqlrepr(self.tableName, db), self.as_string, self.alias)
0522
0523class Alias(SQLExpression):
0524 def __init__(self, table, alias=None):
0525 self.q = AliasTable(table, alias)
0526
0527 def __sqlrepr__(self, db):
0528 return sqlrepr(self.q, db)
0529
0530 def components(self):
0531 return [self.q]
0532
0533
0534class Union(SQLExpression):
0535 def __init__(self, *tables):
0536 tabs = []
0537 for t in tables:
0538 if not isinstance(t, SQLExpression) and hasattr(t, 'sqlmeta'):
0539 t = t.sqlmeta.table
0540 if isinstance(t, Alias):
0541 t = t.q
0542 if isinstance(t, Table):
0543 t = t.tableName
0544 if not isinstance(t, SQLExpression):
0545 t = SQLConstant(t)
0546 tabs.append(t)
0547 self.tables = tabs
0548
0549 def __sqlrepr__(self, db):
0550 return " UNION ".join([str(sqlrepr(t, db)) for t in self.tables])
0551
0552########################################
0553## SQL Statements
0554########################################
0555
0556class Select(SQLExpression):
0557 def __init__(self, items=NoDefault, where=NoDefault, groupBy=NoDefault,
0558 having=NoDefault, orderBy=NoDefault, limit=NoDefault,
0559 join=NoDefault, lazyColumns=False, distinct=False,
0560 start=0, end=None, reversed=False, forUpdate=False,
0561 clause=NoDefault, staticTables=NoDefault, distinctOn=NoDefault):
0562 self.ops = {}
0563 if not isinstance(items, (list, tuple, types.GeneratorType)):
0564 items = [items]
0565 if clause is NoDefault and where is not NoDefault:
0566 clause = where
0567 if staticTables is NoDefault:
0568 staticTables = []
0569 self.ops['items'] = items
0570 self.ops['clause'] = clause
0571 self.ops['groupBy'] = groupBy
0572 self.ops['having'] = having
0573 self.ops['orderBy'] = orderBy
0574 self.ops['limit'] = limit
0575 self.ops['join'] = join
0576 self.ops['lazyColumns'] = lazyColumns
0577 self.ops['distinct'] = distinct
0578 self.ops['distinctOn'] = distinctOn
0579 self.ops['start'] = start
0580 self.ops['end'] = end
0581 self.ops['reversed'] = reversed
0582 self.ops['forUpdate'] = forUpdate
0583 self.ops['staticTables'] = staticTables
0584
0585 def clone(self, **newOps):
0586 ops = self.ops.copy()
0587 ops.update(newOps)
0588 return self.__class__(**ops)
0589
0590 def newItems(self, items):
0591 return self.clone(items=items)
0592
0593 def newClause(self, new_clause):
0594 return self.clone(clause=new_clause)
0595
0596 def orderBy(self, orderBy):
0597 return self.clone(orderBy=orderBy)
0598
0599 def unlimited(self):
0600 return self.clone(limit=NoDefault, start=0, end=None)
0601
0602 def limit(self, limit):
0603 self.clone(limit=limit)
0604
0605 def lazyColumns(self, value):
0606 return self.clone(lazyColumns=value)
0607
0608 def reversed(self):
0609 return self.clone(reversed=not self.ops.get('reversed', False))
0610
0611 def distinct(self):
0612 return self.clone(distinct=True)
0613
0614 def filter(self, filter_clause):
0615 if filter_clause is None:
0616 # None doesn't filter anything, it's just a no-op:
0617 return self
0618 clause = self.ops['clause']
0619 if isinstance(clause, basestring):
0620 clause = SQLConstant('(%s)' % clause)
0621 return self.newClause(AND(clause, filter_clause))
0622
0623 def __sqlrepr__(self, db):
0624
0625 select = "SELECT"
0626 if self.ops['distinct']:
0627 select += " DISTINCT"
0628 if self.ops['distinctOn'] is not NoDefault:
0629 select += " ON(%s)" % _str_or_sqlrepr(self.ops['distinctOn'], db)
0630 if not self.ops['lazyColumns']:
0631 select += " %s" % ", ".join([str(_str_or_sqlrepr(v, db)) for v in self.ops['items']])
0632 else:
0633 select += " %s" % _str_or_sqlrepr(self.ops['items'][0], db)
0634
0635 join = []
0636 join_str = ''
0637 if self.ops['join'] is not NoDefault and self.ops['join'] is not None:
0638 _join = self.ops['join']
0639 if isinstance(_join, str):
0640 join_str = " " + _join
0641 elif isinstance(_join, SQLJoin):
0642 join.append(_join)
0643 else:
0644 join.extend(_join)
0645 tables = set()
0646 for x in self.ops['staticTables']:
0647 if isinstance(x, SQLExpression):
0648 x = sqlrepr(x, db)
0649 tables.add(x)
0650 things = list(self.ops['items']) + join
0651 if self.ops['clause'] is not NoDefault:
0652 things.append(self.ops['clause'])
0653 for thing in things:
0654 if isinstance(thing, SQLExpression):
0655 tables.update(tablesUsedSet(thing, db))
0656 for j in join:
0657 t1 = _str_or_sqlrepr(j.table1, db)
0658 if t1 in tables: tables.remove(t1)
0659 t2 = _str_or_sqlrepr(j.table2, db)
0660 if t2 in tables: tables.remove(t2)
0661 if tables:
0662 select += " FROM %s" % ", ".join(tables)
0663 elif join:
0664 select += " FROM"
0665 tablesYet = tables
0666 for j in join:
0667 if tablesYet and j.table1:
0668 sep = ", "
0669 else:
0670 sep = " "
0671 select += sep + sqlrepr(j, db)
0672 tablesYet = True
0673
0674 if join_str:
0675 select += join_str
0676
0677 if self.ops['clause'] is not NoDefault:
0678 select += " WHERE %s" % _str_or_sqlrepr(self.ops['clause'], db)
0679 if self.ops['groupBy'] is not NoDefault:
0680 groupBy = _str_or_sqlrepr(self.ops['groupBy'], db)
0681 if isinstance(self.ops['groupBy'], (list, tuple)):
0682 groupBy = groupBy[1:-1] # Remove parens
0683 select += " GROUP BY %s" % groupBy
0684 if self.ops['having'] is not NoDefault:
0685 select += " HAVING %s" % _str_or_sqlrepr(self.ops['having'], db)
0686 if self.ops['orderBy'] is not NoDefault and self.ops['orderBy'] is not None:
0687 orderBy = self.ops['orderBy']
0688 if self.ops['reversed']:
0689 reverser = DESC
0690 else:
0691 reverser = lambda x: x
0692 if isinstance(orderBy, (list, tuple)):
0693 select += " ORDER BY %s" % ", ".join([_str_or_sqlrepr(reverser(x), db) for x in orderBy])
0694 else:
0695 select += " ORDER BY %s" % _str_or_sqlrepr(reverser(orderBy), db)
0696 start, end = self.ops['start'], self.ops['end']
0697 if self.ops['limit'] is not NoDefault:
0698 end = start + self.ops['limit']
0699 if start or end:
0700 from dbconnection import dbConnectionForScheme
0701 select = dbConnectionForScheme(db)._queryAddLimitOffset(select, start, end)
0702 if self.ops['forUpdate']:
0703 select += " FOR UPDATE"
0704 return select
0705
0706registerConverter(Select, SQLExprConverter)
0707
0708class Insert(SQLExpression):
0709 def __init__(self, table, valueList=None, values=None, template=NoDefault):
0710 self.template = template
0711 self.table = table
0712 if valueList:
0713 if values:
0714 raise TypeError, "You may only give valueList *or* values"
0715 self.valueList = valueList
0716 else:
0717 self.valueList = [values]
0718 def __sqlrepr__(self, db):
0719 if not self.valueList:
0720 return ''
0721 insert = "INSERT INTO %s" % self.table
0722 allowNonDict = True
0723 template = self.template
0724 if (template is NoDefault) and isinstance(self.valueList[0], dict):
0725 template = self.valueList[0].keys()
0726 allowNonDict = False
0727 if template is not NoDefault:
0728 insert += " (%s)" % ", ".join(template)
0729 insert += " VALUES "
0730 listToJoin = []
0731 listToJoin_app = listToJoin.append
0732 for value in self.valueList:
0733 if isinstance(value, dict):
0734 if template is NoDefault:
0735 raise TypeError, "You can't mix non-dictionaries with dictionaries in an INSERT if you don't provide a template (%s)" % repr(value)
0736 value = dictToList(template, value)
0737 elif not allowNonDict:
0738 raise TypeError, "You can't mix non-dictionaries with dictionaries in an INSERT if you don't provide a template (%s)" % repr(value)
0739 listToJoin_app("(%s)" % ", ".join([sqlrepr(v, db) for v in value]))
0740 insert = "%s%s" % (insert, ", ".join(listToJoin))
0741 return insert
0742
0743registerConverter(Insert, SQLExprConverter)
0744
0745def dictToList(template, dict):
0746 list = []
0747 for key in template:
0748 list.append(dict[key])
0749 if len(dict.keys()) > len(template):
0750 raise TypeError, "Extra entries in dictionary that aren't asked for in template (template=%s, dict=%s)" % (repr(template), repr(dict))
0751 return list
0752
0753class Update(SQLExpression):
0754 def __init__(self, table, values, template=NoDefault, where=NoDefault):
0755 self.table = table
0756 self.values = values
0757 self.template = template
0758 self.whereClause = where
0759 def __sqlrepr__(self, db):
0760 update = "%s %s" % (self.sqlName(), self.table)
0761 update += " SET"
0762 first = True
0763 if self.template is not NoDefault:
0764 for i in range(len(self.template)):
0765 if first:
0766 first = False
0767 else:
0768 update += ","
0769 update += " %s=%s" % (self.template[i], sqlrepr(self.values[i], db))
0770 else:
0771 for key, value in self.values.items():
0772 if first:
0773 first = False
0774 else:
0775 update += ","
0776 update += " %s=%s" % (key, sqlrepr(value, db))
0777 if self.whereClause is not NoDefault:
0778 update += " WHERE %s" % _str_or_sqlrepr(self.whereClause, db)
0779 return update
0780 def sqlName(self):
0781 return "UPDATE"
0782
0783registerConverter(Update, SQLExprConverter)
0784
0785class Delete(SQLExpression):
0786 """To be safe, this will signal an error if there is no where clause,
0787 unless you pass in where=None to the constructor."""
0788 def __init__(self, table, where=NoDefault):
0789 self.table = table
0790 if where is NoDefault:
0791 raise TypeError, "You must give a where clause or pass in None to indicate no where clause"
0792 self.whereClause = where
0793 def __sqlrepr__(self, db):
0794 whereClause = self.whereClause
0795 if whereClause is None:
0796 return "DELETE FROM %s" % self.table
0797 whereClause = _str_or_sqlrepr(whereClause, db)
0798 return "DELETE FROM %s WHERE %s" % (self.table, whereClause)
0799
0800registerConverter(Delete, SQLExprConverter)
0801
0802class Replace(Update):
0803 def sqlName(self):
0804 return "REPLACE"
0805
0806registerConverter(Replace, SQLExprConverter)
0807
0808########################################
0809## SQL Builtins
0810########################################
0811
0812class DESC(SQLExpression):
0813
0814 def __init__(self, expr):
0815 self.expr = expr
0816
0817 def __sqlrepr__(self, db):
0818 if isinstance(self.expr, DESC):
0819 return sqlrepr(self.expr.expr, db)
0820 return '%s DESC' % sqlrepr(self.expr, db)
0821
0822def AND(*ops):
0823 if not ops:
0824 return None
0825 op1 = ops[0]
0826 ops = ops[1:]
0827 if ops:
0828 return SQLOp("AND", op1, AND(*ops))
0829 else:
0830 return op1
0831
0832def OR(*ops):
0833 if not ops:
0834 return None
0835 op1 = ops[0]
0836 ops = ops[1:]
0837 if ops:
0838 return SQLOp("OR", op1, OR(*ops))
0839 else:
0840 return op1
0841
0842def NOT(op):
0843 return SQLPrefix("NOT", op)
0844
0845def _IN(item, list):
0846 return SQLOp("IN", item, list)
0847
0848def IN(item, list):
0849 from sresults import SelectResults # Import here to avoid circular import
0850 if isinstance(list, SelectResults):
0851 query = list.queryForSelect()
0852 query.ops['items'] = [list.sourceClass.q.id]
0853 list = query
0854 if isinstance(list, Select):
0855 return INSubquery(item, list)
0856 else:
0857 return _IN(item, list)
0858
0859def NOTIN(item, list):
0860 if isinstance(list, Select):
0861 return NOTINSubquery(item, list)
0862 else:
0863 return NOT(_IN(item, list))
0864
0865def STARTSWITH(expr, pattern):
0866 return LIKE(expr, _LikeQuoted(pattern) + '%', escape='\\')
0867
0868def ENDSWITH(expr, pattern):
0869 return LIKE(expr, '%' + _LikeQuoted(pattern), escape='\\')
0870
0871def CONTAINSSTRING(expr, pattern):
0872 return LIKE(expr, '%' + _LikeQuoted(pattern) + '%', escape='\\')
0873
0874def ISNULL(expr):
0875 return SQLOp("IS", expr, None)
0876
0877def ISNOTNULL(expr):
0878 return SQLOp("IS NOT", expr, None)
0879
0880class ColumnAS(SQLOp):
0881 ''' Just like SQLOp('AS', expr, name) except without the parentheses '''
0882 def __init__(self, expr, name):
0883 if isinstance(name, basestring):
0884 name = SQLConstant(name)
0885 SQLOp.__init__(self, 'AS', expr, name)
0886 def __sqlrepr__(self, db):
0887 return "%s %s %s" % (sqlrepr(self.expr1, db), self.op, sqlrepr(self.expr2, db))
0888
0889class _LikeQuoted:
0890 # It assumes prefix and postfix are strings; usually just a percent sign.
0891
0892 # @@: I'm not sure what the quoting rules really are for all the
0893 # databases
0894
0895 def __init__(self, expr):
0896 self.expr = expr
0897 self.prefix = ''
0898 self.postfix = ''
0899
0900 def __radd__(self, s):
0901 self.prefix = s + self.prefix
0902 return self
0903
0904 def __add__(self, s):
0905 self.postfix += s
0906 return self
0907
0908 def __sqlrepr__(self, db):
0909 s = self.expr
0910 if isinstance(s, SQLExpression):
0911 values = []
0912 if self.prefix:
0913 values.append(quote_str(self.prefix, db))
0914 s = _quote_like_special(sqlrepr(s, db), db)
0915 values.append(s)
0916 if self.postfix:
0917 values.append(quote_str(self.postfix, db))
0918 if db == "mysql":
0919 return "CONCAT(%s)" % ", ".join(values)
0920 else:
0921 return " || ".join(values)
0922 elif isinstance(s, basestring):
0923 s = _quote_like_special(unquote_str(sqlrepr(s, db)), db)
0924 return quote_str("%s%s%s" % (self.prefix, s, self.postfix), db)
0925 else:
0926 raise TypeError, "expected str, unicode or SQLExpression, got %s" % type(s)
0927
0928def _quote_like_special(s, db):
0929 if db in ('postgres', 'rdbhost'):
0930 escape = r'\\'
0931 else:
0932 escape = '\\'
0933 s = s.replace('\\', r'\\').replace('%', escape+'%').replace('_', escape+'_')
0934 return s
0935
0936########################################
0937## SQL JOINs
0938########################################
0939
0940class SQLJoin(SQLExpression):
0941 def __init__(self, table1, table2, op=','):
0942 if hasattr(table1, 'sqlmeta'):
0943 table1 = table1.sqlmeta.table
0944 if hasattr(table2, 'sqlmeta'):
0945 table2 = table2.sqlmeta.table
0946 if isinstance(table1, str):
0947 table1 = SQLConstant(table1)
0948 if isinstance(table2, str):
0949 table2 = SQLConstant(table2)
0950 self.table1 = table1
0951 self.table2 = table2
0952 self.op = op
0953
0954 def __sqlrepr__(self, db):
0955 if self.table1:
0956 return "%s%s %s" % (sqlrepr(self.table1, db), self.op, sqlrepr(self.table2, db))
0957 else:
0958 return "%s %s" % (self.op, sqlrepr(self.table2, db))
0959
0960registerConverter(SQLJoin, SQLExprConverter)
0961
0962def JOIN(table1, table2):
0963 return SQLJoin(table1, table2, " JOIN")
0964
0965def INNERJOIN(table1, table2):
0966 return SQLJoin(table1, table2, " INNER JOIN")
0967
0968def CROSSJOIN(table1, table2):
0969 return SQLJoin(table1, table2, " CROSS JOIN")
0970
0971def STRAIGHTJOIN(table1, table2):
0972 return SQLJoin(table1, table2, " STRAIGHT JOIN")
0973
0974def LEFTJOIN(table1, table2):
0975 return SQLJoin(table1, table2, " LEFT JOIN")
0976
0977def LEFTOUTERJOIN(table1, table2):
0978 return SQLJoin(table1, table2, " LEFT OUTER JOIN")
0979
0980def NATURALJOIN(table1, table2):
0981 return SQLJoin(table1, table2, " NATURAL JOIN")
0982
0983def NATURALLEFTJOIN(table1, table2):
0984 return SQLJoin(table1, table2, " NATURAL LEFT JOIN")
0985
0986def NATURALLEFTOUTERJOIN(table1, table2):
0987 return SQLJoin(table1, table2, " NATURAL LEFT OUTER JOIN")
0988
0989def RIGHTJOIN(table1, table2):
0990 return SQLJoin(table1, table2, " RIGHT JOIN")
0991
0992def RIGHTOUTERJOIN(table1, table2):
0993 return SQLJoin(table1, table2, " RIGHT OUTER JOIN")
0994
0995def NATURALRIGHTJOIN(table1, table2):
0996 return SQLJoin(table1, table2, " NATURAL RIGHT JOIN")
0997
0998def NATURALRIGHTOUTERJOIN(table1, table2):
0999 return SQLJoin(table1, table2, " NATURAL RIGHT OUTER JOIN")
1000
1001def FULLJOIN(table1, table2):
1002 return SQLJoin(table1, table2, " FULL JOIN")
1003
1004def FULLOUTERJOIN(table1, table2):
1005 return SQLJoin(table1, table2, " FULL OUTER JOIN")
1006
1007def NATURALFULLJOIN(table1, table2):
1008 return SQLJoin(table1, table2, " NATURAL FULL JOIN")
1009
1010def NATURALFULLOUTERJOIN(table1, table2):
1011 return SQLJoin(table1, table2, " NATURAL FULL OUTER JOIN")
1012
1013class SQLJoinConditional(SQLJoin):
1014 """Conditional JOIN"""
1015 def __init__(self, table1, table2, op, on_condition=None, using_columns=None):
1016 """For condition you must give on_condition or using_columns but not both
1017
1018 on_condition can be a string or SQLExpression, for example
1019 Table1.q.col1 == Table2.q.col2
1020 using_columns can be a string or a list of columns, e.g.
1021 (Table1.q.col1, Table2.q.col2)
1022 """
1023 if not on_condition and not using_columns:
1024 raise TypeError, "You must give ON condition or USING columns"
1025 if on_condition and using_columns:
1026 raise TypeError, "You must give ON condition or USING columns but not both"
1027 SQLJoin.__init__(self, table1, table2, op)
1028 self.on_condition = on_condition
1029 self.using_columns = using_columns
1030
1031 def __sqlrepr__(self, db):
1032 if self.on_condition:
1033 on_condition = self.on_condition
1034 if hasattr(on_condition, "__sqlrepr__"):
1035 on_condition = sqlrepr(on_condition, db)
1036 join = "%s %s ON %s" % (self.op, sqlrepr(self.table2, db), on_condition)
1037 if self.table1:
1038 join = "%s %s" % (sqlrepr(self.table1, db), join)
1039 return join
1040 elif self.using_columns:
1041 using_columns = []
1042 for col in self.using_columns:
1043 if hasattr(col, "__sqlrepr__"):
1044 col = sqlrepr(col, db)
1045 using_columns.append(col)
1046 using_columns = ", ".join(using_columns)
1047 join = "%s %s USING (%s)" % (self.op, sqlrepr(self.table2, db), using_columns)
1048 if self.table1:
1049 join = "%s %s" % (sqlrepr(self.table1, db), join)
1050 return join
1051 else:
1052 RuntimeError, "Impossible error"
1053
1054registerConverter(SQLJoinConditional, SQLExprConverter)
1055
1056def INNERJOINConditional(table1, table2, on_condition=None, using_columns=None):
1057 return SQLJoinConditional(table1, table2, "INNER JOIN", on_condition, using_columns)
1058
1059def LEFTJOINConditional(table1, table2, on_condition=None, using_columns=None):
1060 return SQLJoinConditional(table1, table2, "LEFT JOIN", on_condition, using_columns)
1061
1062def LEFTOUTERJOINConditional(table1, table2, on_condition=None, using_columns=None):
1063 return SQLJoinConditional(table1, table2, "LEFT OUTER JOIN", on_condition, using_columns)
1064
1065def RIGHTJOINConditional(table1, table2, on_condition=None, using_columns=None):
1066 return SQLJoinConditional(table1, table2, "RIGHT JOIN", on_condition, using_columns)
1067
1068def RIGHTOUTERJOINConditional(table1, table2, on_condition=None, using_columns=None):
1069 return SQLJoinConditional(table1, table2, "RIGHT OUTER JOIN", on_condition, using_columns)
1070
1071def FULLJOINConditional(table1, table2, on_condition=None, using_columns=None):
1072 return SQLJoinConditional(table1, table2, "FULL JOIN", on_condition, using_columns)
1073
1074def FULLOUTERJOINConditional(table1, table2, on_condition=None, using_columns=None):
1075 return SQLJoinConditional(table1, table2, "FULL OUTER JOIN", on_condition, using_columns)
1076
1077class SQLJoinOn(SQLJoinConditional):
1078 """Conditional JOIN ON"""
1079 def __init__(self, table1, table2, op, on_condition):
1080 SQLJoinConditional.__init__(self, table1, table2, op, on_condition)
1081
1082registerConverter(SQLJoinOn, SQLExprConverter)
1083
1084class SQLJoinUsing(SQLJoinConditional):
1085 """Conditional JOIN USING"""
1086 def __init__(self, table1, table2, op, using_columns):
1087 SQLJoinConditional.__init__(self, table1, table2, op, None, using_columns)
1088
1089registerConverter(SQLJoinUsing, SQLExprConverter)
1090
1091def INNERJOINOn(table1, table2, on_condition):
1092 return SQLJoinOn(table1, table2, "INNER JOIN", on_condition)
1093
1094def LEFTJOINOn(table1, table2, on_condition):
1095 return SQLJoinOn(table1, table2, "LEFT JOIN", on_condition)
1096
1097def LEFTOUTERJOINOn(table1, table2, on_condition):
1098 return SQLJoinOn(table1, table2, "LEFT OUTER JOIN", on_condition)
1099
1100def RIGHTJOINOn(table1, table2, on_condition):
1101 return SQLJoinOn(table1, table2, "RIGHT JOIN", on_condition)
1102
1103def RIGHTOUTERJOINOn(table1, table2, on_condition):
1104 return SQLJoinOn(table1, table2, "RIGHT OUTER JOIN", on_condition)
1105
1106def FULLJOINOn(table1, table2, on_condition):
1107 return SQLJoinOn(table1, table2, "FULL JOIN", on_condition)
1108
1109def FULLOUTERJOINOn(table1, table2, on_condition):
1110 return SQLJoinOn(table1, table2, "FULL OUTER JOIN", on_condition)
1111
1112def INNERJOINUsing(table1, table2, using_columns):
1113 return SQLJoinUsing(table1, table2, "INNER JOIN", using_columns)
1114
1115def LEFTJOINUsing(table1, table2, using_columns):
1116 return SQLJoinUsing(table1, table2, "LEFT JOIN", using_columns)
1117
1118def LEFTOUTERJOINUsing(table1, table2, using_columns):
1119 return SQLJoinUsing(table1, table2, "LEFT OUTER JOIN", using_columns)
1120
1121def RIGHTJOINUsing(table1, table2, using_columns):
1122 return SQLJoinUsing(table1, table2, "RIGHT JOIN", using_columns)
1123
1124def RIGHTOUTERJOINUsing(table1, table2, using_columns):
1125 return SQLJoinUsing(table1, table2, "RIGHT OUTER JOIN", using_columns)
1126
1127def FULLJOINUsing(table1, table2, using_columns):
1128 return SQLJoinUsing(table1, table2, "FULL JOIN", using_columns)
1129
1130def FULLOUTERJOINUsing(table1, table2, using_columns):
1131 return SQLJoinUsing(table1, table2, "FULL OUTER JOIN", using_columns)
1132
1133
1134########################################
1135## Subqueries (subselects)
1136########################################
1137
1138class OuterField(SQLObjectField):
1139 def tablesUsedImmediate(self):
1140 return []
1141
1142class OuterTable(SQLObjectTable):
1143 FieldClass = OuterField
1144
1145class Outer:
1146 def __init__(self, table):
1147 self.q = OuterTable(table)
1148
1149
1150class LIKE(SQLExpression):
1151 op = "LIKE"
1152
1153 def __init__(self, expr, string, escape=None):
1154 self.expr = expr
1155 self.string = string
1156 self.escape = escape
1157 def __sqlrepr__(self, db):
1158 escape = self.escape
1159 like = "%s %s (%s)" % (sqlrepr(self.expr, db), self.op, sqlrepr(self.string, db))
1160 if escape is None:
1161 return "(%s)" % like
1162 else:
1163 return "(%s ESCAPE %s)" % (like, sqlrepr(escape, db))
1164 def components(self):
1165 return [self.expr, self.string]
1166 def execute(self, executor):
1167 if not hasattr(self, '_regex'):
1168 # @@: Crude, not entirely accurate
1169 dest = self.string
1170 dest = dest.replace("%%", "\001")
1171 dest = dest.replace("*", "\002")
1172 dest = dest.replace("%", "*")
1173 dest = dest.replace("\001", "%")
1174 dest = dest.replace("\002", "[*]")
1175 self._regex = re.compile(fnmatch.translate(dest), re.I)
1176 return self._regex.search(execute(self.expr, executor))
1177
1178class RLIKE(LIKE):
1179 op = "RLIKE"
1180
1181 op_db = {
1182 'firebird': 'RLIKE',
1183 'maxdb': 'RLIKE',
1184 'mysql': 'RLIKE',
1185 'postgres': '~',
1186 'rdbhost': '~',
1187 'sqlite': 'REGEXP'
1188 }
1189
1190 def _get_op(self, db):
1191 return self.op_db.get(db, 'LIKE')
1192 def __sqlrepr__(self, db):
1193 return "(%s %s (%s))" % (
1194 sqlrepr(self.expr, db), self._get_op(db), sqlrepr(self.string, db)
1195 )
1196 def execute(self, executor):
1197 self.op = self._get_op(self.db)
1198 return LIKE.execute(self, executor)
1199
1200
1201class INSubquery(SQLExpression):
1202 op = "IN"
1203
1204 def __init__(self, item, subquery):
1205 self.item = item
1206 self.subquery = subquery
1207 def components(self):
1208 return [self.item]
1209 def __sqlrepr__(self, db):
1210 return "%s %s (%s)" % (sqlrepr(self.item, db), self.op, sqlrepr(self.subquery, db))
1211
1212class NOTINSubquery(INSubquery):
1213 op = "NOT IN"
1214
1215
1216class Subquery(SQLExpression):
1217 def __init__(self, op, subquery):
1218 self.op = op
1219 self.subquery = subquery
1220
1221 def __sqlrepr__(self, db):
1222 return "%s (%s)" % (self.op, sqlrepr(self.subquery, db))
1223
1224def EXISTS(subquery):
1225 return Subquery("EXISTS", subquery)
1226
1227def NOTEXISTS(subquery):
1228 return Subquery("NOT EXISTS", subquery)
1229
1230def SOME(subquery):
1231 return Subquery("SOME", subquery)
1232
1233def ANY(subquery):
1234 return Subquery("ANY", subquery)
1235
1236def ALL(subquery):
1237 return Subquery("ALL", subquery)
1238
1239####
1240
1241class ImportProxyField(SQLObjectField):
1242 def tablesUsedImmediate(self):
1243 return [str(self.tableName)]
1244
1245class ImportProxy(SQLExpression):
1246 '''Class to be used in column definitions that rely on other tables that might
1247 not yet be in a classregistry.
1248 '''
1249 FieldClass = ImportProxyField
1250 def __init__(self, clsName, registry=None):
1251 self.tableName = _DelayClass(self, clsName)
1252 self.sqlmeta = _Delay_proxy(table=_DelayClass(self, clsName))
1253 self.q = self
1254 self.soClass = None
1255 classregistry.registry(registry).addClassCallback(clsName, lambda foreign, me: setattr(me, 'soClass', foreign), self)
1256
1257 def __nonzero__(self):
1258 return True
1259
1260 def __getattr__(self, attr):
1261 if self.soClass is None:
1262 return _Delay(self, attr)
1263 return getattr(self.soClass.q, attr)
1264
1265class _Delay(SQLExpression):
1266 def __init__(self, proxy, attr):
1267 self.attr = attr
1268 self.proxy = proxy
1269
1270 def __sqlrepr__(self, db):
1271 if self.proxy.soClass is None:
1272 return '_DELAYED_' + self.attr
1273 val = self._resolve()
1274 if isinstance(val, SQLExpression):
1275 val = sqlrepr(val, db)
1276 return val
1277
1278 def tablesUsedImmediate(self):
1279 return getattr(self._resolve(), 'tablesUsedImmediate', lambda: [])()
1280
1281 def components(self):
1282 return getattr(self._resolve(), 'components', lambda: [])()
1283
1284 def _resolve(self):
1285 return getattr(self.proxy, self.attr)
1286
1287 # For AliasTable etc
1288 def fieldName(self):
1289 class _aliasFieldName(SQLExpression):
1290 def __init__(self, proxy):
1291 self.proxy = proxy
1292 def __sqlrepr__(self, db):
1293 return self.proxy._resolve().fieldName
1294 return _aliasFieldName(self)
1295 fieldName = property(fieldName)
1296
1297class _DelayClass(_Delay):
1298 def _resolve(self):
1299 return self.proxy.soClass.sqlmeta.table
1300
1301class _Delay_proxy(object):
1302 def __init__(self, **kw):
1303 self.__dict__.update(kw)
1304
1305######
1306
1307
1308########################################
1309## Global initializations
1310########################################
1311
1312table = TableSpace()
1313const = ConstantSpace()
1314func = const
1315
1316########################################
1317## Testing
1318########################################
1319
1320if __name__ == "__main__":
1321 tests = """
1322>>> AND(table.address.name == "Ian Bicking", table.address.zip > 30000)
1323>>> table.address.name
1324>>> AND(LIKE(table.address.name, "this"), IN(table.address.zip, [100, 200, 300]))
1325>>> Select([table.address.name, table.address.state], where=LIKE(table.address.name, "%ian%"))
1326>>> Select([table.user.name], where=AND(table.user.state == table.states.abbrev))
1327>>> Insert(table.address, [{"name": "BOB", "address": "3049 N. 18th St."}, {"name": "TIM", "address": "409 S. 10th St."}])
1328>>> Insert(table.address, [("BOB", "3049 N. 18th St."), ("TIM", "409 S. 10th St.")], template=('name', 'address'))
1329>>> Delete(table.address, where="BOB"==table.address.name)
1330>>> Update(table.address, {"lastModified": const.NOW()})
1331>>> Replace(table.address, [("BOB", "3049 N. 18th St."), ("TIM", "409 S. 10th St.")], template=('name', 'address'))
1332"""
1333 for expr in tests.split('\n'):
1334 if not expr.strip(): continue
1335 if expr.startswith('>>> '):
1336 expr = expr[4:]`