diff options
| author | Rob Hudson | 2011-08-09 10:26:42 -0700 |
|---|---|---|
| committer | Rob Hudson | 2011-08-09 10:26:42 -0700 |
| commit | 6e3d9da54cffb41461c6778c55b0461410a9655c (patch) | |
| tree | 1def7369528baac4757493eefb846319c0364323 /debug_toolbar/utils/sqlparse/sql.py | |
| parent | fee623f56f1429888b1b31ff56889debeda8edfb (diff) | |
| download | django-debug-toolbar-6e3d9da54cffb41461c6778c55b0461410a9655c.tar.bz2 | |
Updated sqlparse to v0.1.3
Diffstat (limited to 'debug_toolbar/utils/sqlparse/sql.py')
| -rw-r--r-- | debug_toolbar/utils/sqlparse/sql.py | 136 |
1 files changed, 104 insertions, 32 deletions
diff --git a/debug_toolbar/utils/sqlparse/sql.py b/debug_toolbar/utils/sqlparse/sql.py index 5bbb977..55bf804 100644 --- a/debug_toolbar/utils/sqlparse/sql.py +++ b/debug_toolbar/utils/sqlparse/sql.py @@ -3,7 +3,6 @@ """This module contains classes representing syntactical elements of SQL.""" import re -import types from debug_toolbar.utils.sqlparse import tokens as T @@ -16,14 +15,15 @@ class Token(object): the type of the token. """ - __slots__ = ('value', 'ttype',) + __slots__ = ('value', 'ttype', 'parent') def __init__(self, ttype, value): self.value = value self.ttype = ttype + self.parent = None def __str__(self): - return unicode(self).encode('latin-1') + return unicode(self).encode('utf-8') def __repr__(self): short = self._get_repr_value() @@ -43,7 +43,7 @@ class Token(object): def _get_repr_value(self): raw = unicode(self) if len(raw) > 7: - short = raw[:6]+u'...' + short = raw[:6] + u'...' else: short = raw return re.sub('\s+', ' ', short) @@ -59,12 +59,12 @@ class Token(object): type. *values* is a list of possible values for this token. The values are OR'ed together so if only one of the values matches ``True`` - is returned. Except for keyword tokens the comparsion is + is returned. Except for keyword tokens the comparison is case-sensitive. For convenience it's ok to pass in a single string. If *regex* is ``True`` (default is ``False``) the given values are treated as regular expressions. """ - type_matched = self.ttype in ttype + type_matched = self.ttype is ttype if not type_matched or values is None: return type_matched if isinstance(values, basestring): @@ -79,7 +79,7 @@ class Token(object): return True return False else: - if self.ttype is T.Keyword: + if self.ttype in T.Keyword: values = set([v.upper() for v in values]) return self.value.upper() in values else: @@ -93,6 +93,32 @@ class Token(object): """Return ``True`` if this token is a whitespace token.""" return self.ttype and self.ttype in T.Whitespace + def within(self, group_cls): + """Returns ``True`` if this token is within *group_cls*. + + Use this method for example to check if an identifier is within + a function: ``t.within(sql.Function)``. + """ + parent = self.parent + while parent: + if isinstance(parent, group_cls): + return True + parent = parent.parent + return False + + def is_child_of(self, other): + """Returns ``True`` if this token is a direct child of *other*.""" + return self.parent == other + + def has_ancestor(self, other): + """Returns ``True`` if *other* is in this tokens ancestry.""" + parent = self.parent + while parent: + if parent == other: + return True + parent = parent.parent + return False + class TokenList(Token): """A group of tokens. @@ -113,24 +139,24 @@ class TokenList(Token): return ''.join(unicode(x) for x in self.flatten()) def __str__(self): - return unicode(self).encode('latin-1') + return unicode(self).encode('utf-8') def _get_repr_name(self): return self.__class__.__name__ - ## def _pprint_tree(self, max_depth=None, depth=0): - ## """Pretty-print the object tree.""" - ## indent = ' '*(depth*2) - ## for token in self.tokens: - ## if token.is_group(): - ## pre = ' | ' - ## else: - ## pre = ' | ' - ## print '%s%s%s \'%s\'' % (indent, pre, token._get_repr_name(), - ## token._get_repr_value()) - ## if (token.is_group() and max_depth is not None - ## and depth < max_depth): - ## token._pprint_tree(max_depth, depth+1) + def _pprint_tree(self, max_depth=None, depth=0): + """Pretty-print the object tree.""" + indent = ' '*(depth*2) + for idx, token in enumerate(self.tokens): + if token.is_group(): + pre = ' +-' + else: + pre = ' | ' + print '%s%s%d %s \'%s\'' % (indent, pre, idx, + token._get_repr_name(), + token._get_repr_value()) + if (token.is_group() and (max_depth is None or depth < max_depth)): + token._pprint_tree(max_depth, depth+1) def flatten(self): """Generator yielding ungrouped tokens. @@ -150,6 +176,10 @@ class TokenList(Token): def get_sublists(self): return [x for x in self.tokens if isinstance(x, TokenList)] + @property + def _groupable_tokens(self): + return self.tokens + def token_first(self, ignore_whitespace=True): """Returns the first child token. @@ -190,7 +220,7 @@ class TokenList(Token): def token_next_match(self, idx, ttype, value, regex=False): """Returns next token where it's ``match`` method returns ``True``.""" - if type(idx) != types.IntType: + if not isinstance(idx, int): idx = self.token_index(idx) for token in self.tokens[idx:]: if token.match(ttype, value, regex): @@ -202,8 +232,8 @@ class TokenList(Token): passed = False for func in funcs: if func(token): - passed = True - break + passed = True + break if not passed: return token return None @@ -241,7 +271,7 @@ class TokenList(Token): return None if not isinstance(idx, int): idx = self.token_index(idx) - while idx < len(self.tokens)-1: + while idx < len(self.tokens) - 1: idx += 1 if self.tokens[idx].is_whitespace() and skip_ws: continue @@ -257,18 +287,27 @@ class TokenList(Token): If *exclude_end* is ``True`` (default is ``False``) the end token is included too. """ + # FIXME(andi): rename exclude_end to inlcude_end if exclude_end: offset = 0 else: offset = 1 - return self.tokens[self.token_index(start):self.token_index(end)+offset] + end_idx = self.token_index(end) + offset + start_idx = self.token_index(start) + return self.tokens[start_idx:end_idx] - def group_tokens(self, grp_cls, tokens): + def group_tokens(self, grp_cls, tokens, ignore_ws=False): """Replace tokens by an instance of *grp_cls*.""" idx = self.token_index(tokens[0]) + if ignore_ws: + while tokens and tokens[-1].is_whitespace(): + tokens = tokens[:-1] for t in tokens: self.tokens.remove(t) grp = grp_cls(tokens) + for token in tokens: + token.parent = grp + grp.parent = self self.tokens.insert(idx, grp) return grp @@ -290,7 +329,11 @@ class Statement(TokenList): isn't a DML or DDL keyword "UNKNOWN" is returned. """ first_token = self.token_first() - if first_token.ttype in (T.Keyword.DML, T.Keyword.DDL): + if first_token is None: + # An "empty" statement that either has not tokens at all + # or only whitespace tokens. + return 'UNKNOWN' + elif first_token.ttype in (T.Keyword.DML, T.Keyword.DDL): return first_token.value.upper() else: return 'UNKNOWN' @@ -397,27 +440,36 @@ class Parenthesis(TokenList): """Tokens between parenthesis.""" __slots__ = ('value', 'ttype', 'tokens') + @property + def _groupable_tokens(self): + return self.tokens[1:-1] + class Assignment(TokenList): """An assignment like 'var := val;'""" __slots__ = ('value', 'ttype', 'tokens') + class If(TokenList): """An 'if' clause with possible 'else if' or 'else' parts.""" __slots__ = ('value', 'ttype', 'tokens') + class For(TokenList): """A 'FOR' loop.""" __slots__ = ('value', 'ttype', 'tokens') -class Comparsion(TokenList): - """A comparsion used for example in WHERE clauses.""" + +class Comparison(TokenList): + """A comparison used for example in WHERE clauses.""" __slots__ = ('value', 'ttype', 'tokens') + class Comment(TokenList): """A comment.""" __slots__ = ('value', 'ttype', 'tokens') + class Where(TokenList): """A WHERE clause.""" __slots__ = ('value', 'ttype', 'tokens') @@ -434,9 +486,12 @@ class Case(TokenList): If an ELSE exists condition is None. """ ret = [] - in_condition = in_value = False + in_value = False + in_condition = True for token in self.tokens: - if token.match(T.Keyword, 'WHEN'): + if token.match(T.Keyword, 'CASE'): + continue + elif token.match(T.Keyword, 'WHEN'): ret.append(([], [])) in_condition = True in_value = False @@ -450,8 +505,25 @@ class Case(TokenList): elif token.match(T.Keyword, 'END'): in_condition = False in_value = False + if (in_condition or in_value) and not ret: + # First condition withou preceding WHEN + ret.append(([], [])) if in_condition: ret[-1][0].append(token) elif in_value: ret[-1][1].append(token) return ret + + +class Function(TokenList): + """A function or procedure call.""" + + __slots__ = ('value', 'ttype', 'tokens') + + def get_parameters(self): + """Return a list of parameters.""" + parenthesis = self.tokens[-1] + for t in parenthesis.tokens: + if isinstance(t, IdentifierList): + return t.get_identifiers() + return [] |
