From: Mike Bayer Date: Wed, 21 Dec 2005 02:36:54 +0000 (+0000) Subject: added "late WHERE" compilation to SELECT, adds where criterion based on extra bind... X-Git-Tag: rel_0_1_0~221 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=af281888089933ab4c909862b9a41f9ada2dc1a6;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git added "late WHERE" compilation to SELECT, adds where criterion based on extra bind parameters specified at compilation/execution time --- diff --git a/doc/build/content/sqlconstruction.myt b/doc/build/content/sqlconstruction.myt index bbf47bda05..59856bafbb 100644 --- a/doc/build/content/sqlconstruction.myt +++ b/doc/build/content/sqlconstruction.myt @@ -222,7 +222,10 @@ WHERE NOT (users.user_name = :users_user_name <&formatting.myt:poplink&>c = users.select(users.c.user_name.in_('jack', 'ed', 'fred')).execute() <&|formatting.myt:codepopper, link="sql" &> SELECT users.user_id, users.user_name, users.password -FROM users WHERE users.user_name IN ('jack', 'ed', 'fred') +FROM users WHERE users.user_name +IN (:users_user_name, :users_user_name_1, :users_user_name_2) +{'users_user_name': 'jack', 'users_user_name_1': 'ed', + 'users_user_name_2': 'fred'} # join users and addresses together @@ -250,15 +253,47 @@ AND users.user_name = :users_user_name {'users_user_name': 'fred'} + +

Select statements can also generate a WHERE clause based on the parameters you give it. If a given parameter, which matches the name of a column or its "label" (the combined tablename + "_" + column name), and does not already correspond to a bind parameter in the select object, it will be added as a comparison against that column. This is a shortcut to creating a full WHERE clause:

+ <&|formatting.myt:code&> + # specify a match for the "user_name" column + <&formatting.myt:poplink&>c = users.select().execute(user_name='ed') +<&|formatting.myt:codepopper, link="sql" &> +SELECT users.user_id, users.user_name, users.password +FROM users WHERE users.user_name = :users_user_name +{'users_user_name': 'ed'} + + # specify a full where clause for the "user_name" column, as well as a + # comparison for the "user_id" column + <&formatting.myt:poplink&>c = users.select(users.c.user_name=='ed').execute(user_id=10) +<&|formatting.myt:codepopper, link="sql" &> +SELECT users.user_id, users.user_name, users.password +FROM users WHERE users.user_name = :users_user_name AND users.user_id = :users_user_id +{'users_user_name': 'ed', 'users_user_id': 10} + + <&|doclib.myt:item, name="operators", description="Operators" &>

Supported column operators so far are all the numerical comparison operators, i.e. '==', '>', '>=', etc., as well as like(), startswith(), endswith(), and in(). Boolean operators include not_(), and_() and or_(), which also can be used inline via '~', '&', and '|'. Math operators are '+', '-', '*', '/'.

<&|formatting.myt:code &> + # "like" operator users.select(users.c.user_name.like('%ter')) + + # equality operator users.select(users.c.user_name == 'jane') + + # in opertator users.select(users.c.user_id.in_(1,2,3)) + + # and_, endswith, equality operators users.select(and_(addresses.c.street.endswith('green street'), addresses.c.zip=='11234')) + + # & operator subsituting for 'and_' users.select(addresses.c.street.endswith('green street') & (addresses.c.zip=='11234')) + + # + concatenation operator select([users.c.user_name + '_name']) + + # NOT operator users.select(~(addresses.c.street == 'Green Street')) diff --git a/lib/sqlalchemy/ansisql.py b/lib/sqlalchemy/ansisql.py index 795a2545b0..d8d2662ba5 100644 --- a/lib/sqlalchemy/ansisql.py +++ b/lib/sqlalchemy/ansisql.py @@ -269,11 +269,24 @@ class ANSICompiler(sql.Compiled): whereclause = select.whereclause - # TODO: look at our own parameters, see if they + # look at our own parameters, see if they # are all present in the form of BindParamClauses. if # not, then append to the above whereclause column conditions # matching those keys - + if self.parameters is not None: + revisit = False + for c in inner_columns: + if self.parameters.has_key(c.key) and not self.binds.has_key(c.key): + value = self.parameters[c.key] + elif self.parameters.has_key(c.label) and not self.binds.has_key(c.label): + value = self.parameters[c.label] + else: + continue + clause = c==value + clause.accept_visitor(self) + whereclause = sql.and_(clause, whereclause) + self.visit_compound(whereclause) + froms = [] for f in select.froms: diff --git a/lib/sqlalchemy/sql.py b/lib/sqlalchemy/sql.py index ab762cb39e..a634767eaa 100644 --- a/lib/sqlalchemy/sql.py +++ b/lib/sqlalchemy/sql.py @@ -1155,6 +1155,19 @@ class UpdateBase(ClauseElement): return parameters def get_colparams(self, parameters): + """this is used by the ANSICompiler to determine the VALUES or SET clause based on the arguments + specified to the execute() or compile() method of the INSERT or UPDATE clause: + + insert(mytable).execute(col1='foo', col2='bar') + mytable.update().execute(col2='foo', col3='bar') + + in the above examples, the insert() and update() methods have no "values" sent to them + at all, so compiling them with no arguments would yield an insert for all table columns, + or an update with no SET clauses. but the parameters sent indicate a set of per-compilation + arguments that result in a differently compiled INSERT or UPDATE object compared to the + original. The "values" parameter to the insert/update is figured as well if present, + but the incoming "parameters" sent here take precedence. + """ # case one: no parameters in the statement, no parameters in the # compiled params - just return binds for all the table columns if parameters is None and self.parameters is None: diff --git a/test/select.py b/test/select.py index 4e2a7cae32..366ce3c8f9 100644 --- a/test/select.py +++ b/test/select.py @@ -389,7 +389,15 @@ FROM mytable, myothertable WHERE mytable.myid = myothertable.otherid AND mytable self.runtest(select([table], table.c.id.in_(select([table2.c.id]))), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid IN (SELECT myothertable.otherid FROM myothertable)") + def testlateargs(self): + """tests that a SELECT clause will have extra "WHERE" clauses added to it at compile time if extra arguments + are sent""" + self.runtest(table.select(), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.name = :mytable_name AND mytable.myid = :mytable_myid", params={'id':'3', 'name':'jack'}) + + self.runtest(table.select(table.c.name=='jack'), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :mytable_myid AND mytable.name = :mytable_name", params={'id':'3'}) + + self.runtest(table.select(table.c.name=='jack'), "SELECT mytable.myid, mytable.name, mytable.description FROM mytable WHERE mytable.myid = :mytable_myid AND mytable.name = :mytable_name", params={'id':'3', 'name':'fred'}) class CRUDTest(SQLTest): def testinsert(self):