[Sqlalchemy-commits] [1095] sqlalchemy/trunk/test: got column onupdate working
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-03-05 20:32:06
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1095] sqlalchemy/trunk/test: got column onupdate working</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1095</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-03-05 14:31:44 -0600 (Sun, 05 Mar 2006)</dd> </dl> <h3>Log Message</h3> <pre>got column onupdate working improvement to Function so that they can more easily be called standalone without having to throw them into a select().</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentsqlconstructionmyt">sqlalchemy/trunk/doc/build/content/sqlconstruction.myt</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyansisqlpy">sqlalchemy/trunk/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesoraclepy">sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasespostgrespy">sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyschemapy">sqlalchemy/trunk/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestdefaultspy">sqlalchemy/trunk/test/defaults.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentsqlconstructionmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/sqlconstruction.myt (1094 => 1095)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-03-04 20:23:37 UTC (rev 1094) +++ sqlalchemy/trunk/doc/build/content/sqlconstruction.myt 2006-03-05 20:31:44 UTC (rev 1095) </span><span class="lines">@@ -341,7 +341,18 @@ </span><span class="cx"> </&> </span><span class="cx"> </span><span class="cx"> </&> </span><ins>+ <p>Functions also are callable as standalone values:</p> + <&|formatting.myt:code &> + # call the "now()" function + time = func.now(engine=myengine).scalar() + + # call myfunc(1,2,3) + myvalue = func.myfunc(1, 2, 3, engine=db).execute() + + # or call them off the engine + db.func.now().scalar() </ins><span class="cx"> </&> </span><ins>+ </&> </ins><span class="cx"> <&|doclib.myt:item, name="literals", description="Literals" &> </span><span class="cx"> <p>You can drop in a literal value anywhere there isnt a column to attach to via the <span class="codeline">literal</span> keyword:</p> </span><span class="cx"> <&|formatting.myt:code &> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ansisql.py (1094 => 1095)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-03-04 20:23:37 UTC (rev 1094) +++ sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-03-05 20:31:44 UTC (rev 1095) </span><span class="lines">@@ -15,6 +15,18 @@ </span><span class="cx"> from sqlalchemy.util import * </span><span class="cx"> import string, re </span><span class="cx"> </span><ins>+ANSI_FUNCS = HashSet([ +'CURRENT_TIME', +'CURRENT_TIMESTAMP', +'CURRENT_DATE', +'LOCAL_TIME', +'LOCAL_TIMESTAMP', +'CURRENT_USER', +'SESSION_USER', +'USER' +]) + + </ins><span class="cx"> def engine(**params): </span><span class="cx"> return ANSISQLEngine(**params) </span><span class="cx"> </span><span class="lines">@@ -57,6 +69,7 @@ </span><span class="cx"> self.select_stack = [] </span><span class="cx"> self.typemap = typemap or {} </span><span class="cx"> self.isinsert = False </span><ins>+ self.isupdate = False </ins><span class="cx"> self.bindtemplate = ":%s" </span><span class="cx"> if engine is not None: </span><span class="cx"> self.paramstyle = engine.paramstyle </span><span class="lines">@@ -89,7 +102,7 @@ </span><span class="cx"> self.strings[self.statement] = re.sub(match, getnum, self.strings[self.statement]) </span><span class="cx"> </span><span class="cx"> def get_from_text(self, obj): </span><del>- return self.froms[obj] </del><ins>+ return self.froms.get(obj, None) </ins><span class="cx"> </span><span class="cx"> def get_str(self, obj): </span><span class="cx"> return self.strings[obj] </span><span class="lines">@@ -158,6 +171,11 @@ </span><span class="cx"> else: </span><span class="cx"> return parameters </span><span class="cx"> </span><ins>+ def default_from(self): + """called when a SELECT statement has no froms, and no FROM clause is to be appended. + gives Oracle a chance to tack on a "FROM DUAL" to the string output. """ + return "" + </ins><span class="cx"> def visit_label(self, label): </span><span class="cx"> if len(self.select_stack): </span><span class="cx"> self.typemap.setdefault(label.name.lower(), label.obj.type) </span><span class="lines">@@ -211,7 +229,12 @@ </span><span class="cx"> self.strings[list] = string.join([self.get_str(c) for c in list.clauses], ', ') </span><span class="cx"> </span><span class="cx"> def visit_function(self, func): </span><del>- self.strings[func] = func.name + "(" + string.join([self.get_str(c) for c in func.clauses], ', ') + ")" </del><ins>+ if len(self.select_stack): + self.typemap.setdefault(func.name, func.type) + if func.name.upper() in ANSI_FUNCS and not len(func.clauses): + self.strings[func] = func.name + else: + self.strings[func] = func.name + "(" + string.join([self.get_str(c) for c in func.clauses], ', ') + ")" </ins><span class="cx"> </span><span class="cx"> def visit_compound_select(self, cs): </span><span class="cx"> text = string.join([self.get_str(c) for c in cs.selects], " " + cs.keyword + " ") </span><span class="lines">@@ -325,7 +348,9 @@ </span><span class="cx"> if len(froms): </span><span class="cx"> text += " \nFROM " </span><span class="cx"> text += string.join(froms, ', ') </span><del>- </del><ins>+ else: + text += self.default_from() + </ins><span class="cx"> if whereclause is not None: </span><span class="cx"> t = self.get_str(whereclause) </span><span class="cx"> if t: </span><span class="lines">@@ -384,21 +409,33 @@ </span><span class="cx"> </span><span class="cx"> def visit_insert_column_default(self, column, default): </span><span class="cx"> """called when visiting an Insert statement, for each column in the table that </span><del>- contains a ColumnDefault object.""" </del><ins>+ contains a ColumnDefault object. adds a blank 'placeholder' parameter so the + Insert gets compiled with this column's name in its column and VALUES clauses.""" </ins><span class="cx"> self.parameters.setdefault(column.key, None) </span><ins>+ + def visit_update_column_default(self, column, default): + """called when visiting an Update statement, for each column in the table that + contains a ColumnDefault object as an onupdate. adds a blank 'placeholder' parameter so the + Update gets compiled with this column's name as one of its SET clauses.""" + self.parameters.setdefault(column.key, None) </ins><span class="cx"> </span><span class="cx"> def visit_insert_sequence(self, column, sequence): </span><span class="cx"> """called when visiting an Insert statement, for each column in the table that </span><del>- contains a Sequence object.""" </del><ins>+ contains a Sequence object. Overridden by compilers that support sequences to place + a blank 'placeholder' parameter, so the Insert gets compiled with this column's + name in its column and VALUES clauses.""" </ins><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> def visit_insert_column(self, column): </span><span class="cx"> """called when visiting an Insert statement, for each column in the table </span><del>- that is a NULL insert into the table""" </del><ins>+ that is a NULL insert into the table. Overridden by compilers who disallow + NULL columns being set in an Insert where there is a default value on the column + (i.e. postgres), to remove the column from the parameter list.""" </ins><span class="cx"> pass </span><span class="cx"> </span><span class="cx"> def visit_insert(self, insert_stmt): </span><del>- # set up a call for the defaults and sequences inside the table </del><ins>+ # scan the table's columns for defaults that have to be pre-set for an INSERT + # add these columns to the parameter list via visit_insert_XXX methods </ins><span class="cx"> class DefaultVisitor(schema.SchemaVisitor): </span><span class="cx"> def visit_column(s, c): </span><span class="cx"> self.visit_insert_column(c) </span><span class="lines">@@ -424,6 +461,17 @@ </span><span class="cx"> self.strings[insert_stmt] = text </span><span class="cx"> </span><span class="cx"> def visit_update(self, update_stmt): </span><ins>+ # scan the table's columns for onupdates that have to be pre-set for an UPDATE + # add these columns to the parameter list via visit_update_XXX methods + class OnUpdateVisitor(schema.SchemaVisitor): + def visit_column_onupdate(s, cd): + self.visit_update_column_default(c, cd) + vis = OnUpdateVisitor() + for c in update_stmt.table.c: + if (isinstance(c, schema.SchemaItem) and (self.parameters is None or self.parameters.get(c.key, None) is None)): + c.accept_schema_visitor(vis) + + self.isupdate = True </ins><span class="cx"> colparams = self._get_colparams(update_stmt) </span><span class="cx"> def create_param(p): </span><span class="cx"> if isinstance(p, sql.BindParamClause): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py (1094 => 1095)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-04 20:23:37 UTC (rev 1094) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-03-05 20:31:44 UTC (rev 1095) </span><span class="lines">@@ -209,6 +209,11 @@ </span><span class="cx"> self._use_ansi = use_ansi </span><span class="cx"> ansisql.ANSICompiler.__init__(self, statement, parameters, engine=engine, **kwargs) </span><span class="cx"> </span><ins>+ def default_from(self): + """called when a SELECT statement has no froms, and no FROM clause is to be appended. + gives Oracle a chance to tack on a "FROM DUAL" to the string output. """ + return " FROM DUAL" + </ins><span class="cx"> def visit_join(self, join): </span><span class="cx"> if self._use_ansi: </span><span class="cx"> return ansisql.ANSICompiler.visit_join(self, join) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py (1094 => 1095)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py 2006-03-04 20:23:37 UTC (rev 1094) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py 2006-03-05 20:31:44 UTC (rev 1095) </span><span class="lines">@@ -103,16 +103,6 @@ </span><span class="cx"> def get_col_spec(self): </span><span class="cx"> return "BOOLEAN" </span><span class="cx"> </span><del>-ANSI_FUNCS = util.HashSet([ -'CURRENT_TIME', -'CURRENT_TIMESTAMP', -'CURRENT_DATE', -'LOCAL_TIME', -'LOCAL_TIMESTAMP', -'CURRENT_USER', -'SESSION_USER', -'USER' -]) </del><span class="cx"> </span><span class="cx"> pg2_colspecs = { </span><span class="cx"> sqltypes.Integer : PGInteger, </span><span class="lines">@@ -283,12 +273,6 @@ </span><span class="cx"> </span><span class="cx"> class PGCompiler(ansisql.ANSICompiler): </span><span class="cx"> </span><del>- def visit_function(self, func): - # PG has a bunch of funcs that explicitly need no parenthesis - if func.name.upper() in ANSI_FUNCS and not len(func.clauses): - self.strings[func] = func.name - else: - super(PGCompiler, self).visit_function(func) </del><span class="cx"> </span><span class="cx"> def visit_insert_column(self, column): </span><span class="cx"> # Postgres advises against OID usage and turns it off in 8.1, </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1094 => 1095)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-04 20:23:37 UTC (rev 1094) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-03-05 20:31:44 UTC (rev 1095) </span><span class="lines">@@ -135,6 +135,12 @@ </span><span class="cx"> else: </span><span class="cx"> return None </span><span class="cx"> </span><ins>+ def get_column_onupdate(self, column): + if column.onupdate is not None: + return column.onupdate.accept_schema_visitor(self) + else: + return None + </ins><span class="cx"> def visit_passive_default(self, default): </span><span class="cx"> """passive defaults by definition return None on the app side, </span><span class="cx"> and are post-fetched to get the DB-side value""" </span><span class="lines">@@ -147,7 +153,15 @@ </span><span class="cx"> def exec_default_sql(self, default): </span><span class="cx"> c = sql.select([default.arg], engine=self.engine).compile() </span><span class="cx"> return self.proxy(str(c), c.get_params()).fetchone()[0] </span><del>- </del><ins>+ + def visit_column_onupdate(self, onupdate): + if isinstance(onupdate.arg, sql.ClauseElement): + return self.exec_default_sql(onupdate) + elif callable(onupdate.arg): + return onupdate.arg() + else: + return onupdate.arg + </ins><span class="cx"> def visit_column_default(self, default): </span><span class="cx"> if isinstance(default.arg, sql.ClauseElement): </span><span class="cx"> return self.exec_default_sql(default) </span><span class="lines">@@ -245,6 +259,13 @@ </span><span class="cx"> typeobj = typeobj() </span><span class="cx"> return typeobj </span><span class="cx"> </span><ins>+ def _func(self): + class FunctionGateway(object): + def __getattr__(s, name): + return lambda *c, **kwargs: sql.Function(name, engine=self, *c, **kwargs) + return FunctionGateway() + func = property(_func) + </ins><span class="cx"> def text(self, text, *args, **kwargs): </span><span class="cx"> """returns a sql.text() object for performing literal queries.""" </span><span class="cx"> return sql.text(text, engine=self, *args, **kwargs) </span><span class="lines">@@ -426,6 +447,15 @@ </span><span class="cx"> self.context.tcount = None </span><span class="cx"> </span><span class="cx"> def _process_defaults(self, proxy, compiled, parameters, **kwargs): </span><ins>+ """INSERT and UPDATE statements, when compiled, may have additional columns added to their + VALUES and SET lists corresponding to column defaults/onupdates that are present on the + Table object (i.e. ColumnDefault, Sequence, PassiveDefault). This method pre-execs those + DefaultGenerator objects that require pre-execution and sets their values within the + parameter list, and flags the thread-local state about + PassiveDefault objects that may require post-fetching the row after it is inserted/updated. + This method relies upon logic within the ANSISQLCompiler in its visit_insert and + visit_update methods that add the appropriate column clauses to the statement when its + being compiled, so that these parameters can be bound to the statement.""" </ins><span class="cx"> if compiled is None: return </span><span class="cx"> if getattr(compiled, "isinsert", False): </span><span class="cx"> if isinstance(parameters, list): </span><span class="lines">@@ -454,7 +484,19 @@ </span><span class="cx"> self.context.last_inserted_ids = None </span><span class="cx"> else: </span><span class="cx"> self.context.last_inserted_ids = last_inserted_ids </span><del>- </del><ins>+ elif getattr(compiled, 'isupdate', False): + if isinstance(parameters, list): + plist = parameters + else: + plist = [parameters] + drunner = self.defaultrunner(proxy) + for param in plist: + for c in compiled.statement.table.c: + if c.onupdate is not None and (not param.has_key(c.name) or param[c.name] is None): + value = drunner.get_column_onupdate(c) + if value is not None: + param[c.name] = value + </ins><span class="cx"> def lastrow_has_defaults(self): </span><span class="cx"> return self.context.lastrow_has_defaults </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/schema.py (1094 => 1095)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-03-04 20:23:37 UTC (rev 1094) +++ sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-03-05 20:31:44 UTC (rev 1095) </span><span class="lines">@@ -364,6 +364,8 @@ </span><span class="cx"> then calls visit_column on the visitor.""" </span><span class="cx"> if self.default is not None: </span><span class="cx"> self.default.accept_schema_visitor(visitor) </span><ins>+ if self.onupdate is not None: + self.onupdate.accept_schema_visitor(visitor) </ins><span class="cx"> if self.foreign_key is not None: </span><span class="cx"> self.foreign_key.accept_schema_visitor(visitor) </span><span class="cx"> visitor.visit_column(self) </span><span class="lines">@@ -473,7 +475,10 @@ </span><span class="cx"> self.arg = arg </span><span class="cx"> def accept_schema_visitor(self, visitor): </span><span class="cx"> """calls the visit_column_default method on the given visitor.""" </span><del>- return visitor.visit_column_default(self) </del><ins>+ if self.for_update: + return visitor.visit_column_onupdate(self) + else: + return visitor.visit_column_default(self) </ins><span class="cx"> def __repr__(self): </span><span class="cx"> return "ColumnDefault(%s)" % repr(self.arg) </span><span class="cx"> </span><span class="lines">@@ -599,6 +604,9 @@ </span><span class="cx"> def visit_column_default(self, default): </span><span class="cx"> """visit a ColumnDefault.""" </span><span class="cx"> pass </span><ins>+ def visit_column_onupdate(self, onupdate): + """visit a ColumnDefault with the "for_update" flag set.""" + pass </ins><span class="cx"> def visit_sequence(self, sequence): </span><span class="cx"> """visit a Sequence.""" </span><span class="cx"> pass </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1094 => 1095)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-04 20:23:37 UTC (rev 1094) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-03-05 20:31:44 UTC (rev 1095) </span><span class="lines">@@ -762,6 +762,9 @@ </span><span class="cx"> def __init__(self, name, *clauses, **kwargs): </span><span class="cx"> self.name = name </span><span class="cx"> self.type = kwargs.get('type', sqltypes.NULLTYPE) </span><ins>+ self._engine = kwargs.get('engine', None) + if self._engine is not None: + self.type = self._engine.type_descriptor(self.type) </ins><span class="cx"> ClauseList.__init__(self, parens=True, *clauses) </span><span class="cx"> key = property(lambda self:self.name) </span><span class="cx"> def append(self, clause): </span><span class="lines">@@ -771,6 +774,8 @@ </span><span class="cx"> else: </span><span class="cx"> clause = BindParamClause(self.name, clause, shortname=self.name, type=None) </span><span class="cx"> self.clauses.append(clause) </span><ins>+ def _process_from_dict(self, data, asfrom): + data.setdefault(self, self) </ins><span class="cx"> def copy_container(self): </span><span class="cx"> clauses = [clause.copy_container() for clause in self.clauses] </span><span class="cx"> return Function(self.name, type=self.type, *clauses) </span><span class="lines">@@ -782,6 +787,10 @@ </span><span class="cx"> return BindParamClause(self.name, obj, shortname=self.name, type=self.type) </span><span class="cx"> def select(self): </span><span class="cx"> return select([self]) </span><ins>+ def scalar(self): + return select([self]).scalar() + def execute(self): + return select([self]).execute() </ins><span class="cx"> def _compare_type(self, obj): </span><span class="cx"> return self.type </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktestdefaultspy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/defaults.py (1094 => 1095)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/defaults.py 2006-03-04 20:23:37 UTC (rev 1094) +++ sqlalchemy/trunk/test/defaults.py 2006-03-05 20:31:44 UTC (rev 1095) </span><span class="lines">@@ -7,11 +7,11 @@ </span><span class="cx"> import sqlalchemy </span><span class="cx"> </span><span class="cx"> db = testbase.db </span><del>- </del><ins>+testbase.echo=False </ins><span class="cx"> class DefaultTest(PersistTest): </span><span class="cx"> </span><span class="cx"> def setUpAll(self): </span><del>- global t, f, ts </del><ins>+ global t, f, ts, currenttime </ins><span class="cx"> x = {'x':50} </span><span class="cx"> def mydefault(): </span><span class="cx"> x['x'] += 1 </span><span class="lines">@@ -22,18 +22,19 @@ </span><span class="cx"> </span><span class="cx"> # select "count(1)" from the DB which returns different results </span><span class="cx"> # on different DBs </span><ins>+ currenttime = db.func.current_date(type=Date); </ins><span class="cx"> if is_oracle: </span><del>- f = select([func.count(1) + 5], engine=db, from_obj=['DUAL']).scalar() - ts = select([func.sysdate()], engine=db, from_obj=['DUAL']).scalar() - def1 = func.sysdate() </del><ins>+ ts = db.func.sysdate().scalar() + f = select([func.count(1) + 5], engine=db).scalar() + def1 = currenttime </ins><span class="cx"> def2 = text("sysdate") </span><span class="cx"> deftype = Date </span><span class="cx"> elif use_function_defaults: </span><span class="cx"> f = select([func.count(1) + 5], engine=db).scalar() </span><del>- def1 = func.current_date() </del><ins>+ def1 = currenttime </ins><span class="cx"> def2 = text("current_date") </span><span class="cx"> deftype = Date </span><del>- ts = select([func.current_date()], engine=db).scalar() </del><ins>+ ts = db.func.current_date().scalar() </ins><span class="cx"> else: </span><span class="cx"> f = select([func.count(1) + 5], engine=db).scalar() </span><span class="cx"> def1 = def2 = "3" </span><span class="lines">@@ -45,20 +46,29 @@ </span><span class="cx"> Column('col1', Integer, primary_key=True, default=mydefault), </span><span class="cx"> </span><span class="cx"> # python literal </span><del>- Column('col2', String(20), default="imthedefault"), </del><ins>+ Column('col2', String(20), default="imthedefault", onupdate="im the update"), </ins><span class="cx"> </span><span class="cx"> # preexecute expression </span><del>- Column('col3', Integer, default=func.count(1) + 5), </del><ins>+ Column('col3', Integer, default=func.count(1) + 5, onupdate=func.count(1) + 14), </ins><span class="cx"> </span><span class="cx"> # SQL-side default from sql expression </span><span class="cx"> Column('col4', deftype, PassiveDefault(def1)), </span><span class="cx"> </span><span class="cx"> # SQL-side default from literal expression </span><del>- Column('col5', deftype, PassiveDefault(def2)) </del><ins>+ Column('col5', deftype, PassiveDefault(def2)), + + # preexecute + update timestamp + Column('col6', Date, default=currenttime, onupdate=currenttime) </ins><span class="cx"> ) </span><span class="cx"> t.create() </span><span class="cx"> </span><del>- def teststandalonedefaults(self): </del><ins>+ def tearDownAll(self): + t.drop() + + def tearDown(self): + t.delete().execute() + + def teststandalone(self): </ins><span class="cx"> x = t.c.col1.default.execute() </span><span class="cx"> y = t.c.col2.default.execute() </span><span class="cx"> z = t.c.col3.default.execute() </span><span class="lines">@@ -66,18 +76,27 @@ </span><span class="cx"> self.assert_(y == 'imthedefault') </span><span class="cx"> self.assert_(z == 6) </span><span class="cx"> </span><del>- def testinsertdefaults(self): </del><ins>+ def testinsert(self): </ins><span class="cx"> t.insert().execute() </span><span class="cx"> self.assert_(t.engine.lastrow_has_defaults()) </span><span class="cx"> t.insert().execute() </span><span class="cx"> t.insert().execute() </span><del>- </del><ins>+ + ctexec = currenttime.scalar() + self.echo("Currenttime "+ repr(ctexec)) </ins><span class="cx"> l = t.select().execute() </span><del>- self.assert_(l.fetchall() == [(51, 'imthedefault', f, ts, ts), (52, 'imthedefault', f, ts, ts), (53, 'imthedefault', f, ts, ts)]) </del><ins>+ self.assert_(l.fetchall() == [(51, 'imthedefault', f, ts, ts, ctexec), (52, 'imthedefault', f, ts, ts, ctexec), (53, 'imthedefault', f, ts, ts, ctexec)]) </ins><span class="cx"> </span><del>- def tearDownAll(self): - t.drop() - </del><ins>+ def testupdate(self): + t.insert().execute() + pk = t.engine.last_inserted_ids()[0] + t.update(t.c.col1==pk).execute(col4=None, col5=None) + ctexec = currenttime.scalar() + self.echo("Currenttime "+ repr(ctexec)) + l = t.select(t.c.col1==pk).execute() + l = l.fetchone() + self.assert_(l == (pk, 'im the update', 15, None, None, ctexec)) + </ins><span class="cx"> class SequenceTest(PersistTest): </span><span class="cx"> </span><span class="cx"> def setUpAll(self): </span></span></pre> </div> </div> </body> </html> |