]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
added "late WHERE" compilation to SELECT, adds where criterion based on extra bind...
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 21 Dec 2005 02:36:54 +0000 (02:36 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 21 Dec 2005 02:36:54 +0000 (02:36 +0000)
at compilation/execution time

doc/build/content/sqlconstruction.myt
lib/sqlalchemy/ansisql.py
lib/sqlalchemy/sql.py
test/select.py

index bbf47bda051d319c68f3c70f381b2f11125debc0..59856bafbb2575fd4aae815137c597afec4f0657 100644 (file)
@@ -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'}                
 </&>
 </&>            
+
+        <P>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:</p>
+        <&|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" &>
             <p>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 '+', '-', '*', '/'.</p>
             <&|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'))
             </&>
             </&>
index 795a2545b0cf934335f569b3c2e1a17d8d181c46..d8d2662ba5f18ee855a248bf5e33372d69f20304 100644 (file)
@@ -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:
 
index ab762cb39e5496243788d2a215dbe2203e71d42f..a634767eaa0648d988dc668996cea8cd5e6e7cd1 100644 (file)
@@ -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:
index 4e2a7cae328ea0238ff335449e36bdcabbcf9d80..366ce3c8f9805ca79bb55d0faee30c14bb96955e 100644 (file)
@@ -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):