Skip to content

Commit

Permalink
Merge pull request #12 from uranusjr/subquery
Browse files Browse the repository at this point in the history
Support subqueries
  • Loading branch information
moskytw committed Oct 5, 2013
2 parents ba0d88b + 75d88cb commit 8f1da98
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 6 deletions.
6 changes: 6 additions & 0 deletions mosql/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ def select(table, where=None, select=None, **clauses_args):
>>> print select('person', select=raw('count(*)'), group_by=('age', ))
SELECT count(*) FROM "person" GROUP BY "age"
Queries built by MoSQL can be used as subqueries inside another query:
>>> subquery = select('person', select=('last_name',))
>>> print select('person', where={'first_name': paren(subquery)})
SELECT * FROM "person" WHERE "first_name" = (SELECT "last_name" FROM "person")
.. warning ::
You have responsibility to ensure the security if you use :class:`mosql.util.raw`.
Expand Down
28 changes: 22 additions & 6 deletions mosql/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,18 @@ def escape_identifier(s):

std_escape_identifier = escape_identifier

class raw(str):
class _query(str):
'''A qualifier that MoSQL uses internally to distinguish strings generated
by MoSQL itself, and those you provided through arguments. You can treat
this type safely as :class:`str`.
.. warning ::
You generally don't need to, maybe even want to avoid instanciate
instancese of this type.
'''
pass

class raw(_query):
'''The qualifier function do noting when the input is an instance of this
class. This is a subclass of built-in `str` type.
Expand Down Expand Up @@ -203,7 +214,8 @@ def qualifier_wrapper(x):
elif _is_iterable_not_str(x):
return [item if isinstance(item, raw) else f(item) for item in x]
else:
return f(x)
r = f(x)
return r if isinstance(r, _query) else _query(r)

return qualifier_wrapper

Expand Down Expand Up @@ -256,6 +268,8 @@ def value(x):

if x is None:
return 'NULL'
elif isinstance(x, _query):
return x
else:
handler = _type_handler_map.get(type(x))
if handler:
Expand Down Expand Up @@ -314,8 +328,10 @@ def identifier(s):
"table_name"."column_name" DESC
'''

if delimit_identifier is None:
if isinstance(s, _query):
return s
elif delimit_identifier is None:
return _query(s)
elif s.find('.') == -1 and s.find(' ') == -1:
return delimit_identifier(escape_identifier(s))
else:
Expand All @@ -339,7 +355,7 @@ def identifier(s):
raise OptionError(op)
r += ' '+op

return r
return _query(r)

@qualifier
def paren(s):
Expand Down Expand Up @@ -434,7 +450,7 @@ def _build_condition(x, key_qualify=identifier, value_qualifier=value):

op = ''

if not isinstance(k, raw):
if not isinstance(k, _query):

# split the op out
space_pos = k.find(' ')
Expand Down Expand Up @@ -733,7 +749,7 @@ def format(self, clause_args):
if arg is not None:
pieces.append(clause.format(arg))

return ' '.join(pieces)
return _query(' '.join(pieces))

def __repr__(self):
return 'Statement(%r)' % self.clauses
Expand Down

0 comments on commit 8f1da98

Please sign in to comment.