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:]