summaryrefslogtreecommitdiff
path: root/sqlparse
diff options
context:
space:
mode:
authorAndi Albrecht <albrecht.andi@gmail.com>2015-02-21 08:15:09 +0100
committerAndi Albrecht <albrecht.andi@gmail.com>2015-02-21 08:15:09 +0100
commit9cec0cde3818005d70b0473f3c99241f5df68394 (patch)
tree195c683a37221e307c600980c00dcddc9a2b76da /sqlparse
parent71af186659923dfe8721c551d5dbf4db7c4854d9 (diff)
parent77e0789aea8918a2fbbc6f20196cd0bcdecccf52 (diff)
downloadsqlparse-9cec0cde3818005d70b0473f3c99241f5df68394.tar.gz
Merge master into v0.2.0 branch.
Diffstat (limited to 'sqlparse')
-rw-r--r--sqlparse/engine/grouping.py7
-rw-r--r--sqlparse/filters.py6
-rw-r--r--sqlparse/lexer.py10
-rw-r--r--sqlparse/sql.py90
-rw-r--r--sqlparse/tokens.py1
5 files changed, 73 insertions, 41 deletions
diff --git a/sqlparse/engine/grouping.py b/sqlparse/engine/grouping.py
index 6aa9e18..e9d77e6 100644
--- a/sqlparse/engine/grouping.py
+++ b/sqlparse/engine/grouping.py
@@ -150,11 +150,14 @@ def group_identifier(tlist):
# TODO: Usage of Wildcard token is ambivalent here.
x = itertools.cycle((
lambda y: (y.match(T.Punctuation, '.')
- or y.ttype is T.Operator
- or y.ttype is T.Wildcard),
+ or y.ttype in (T.Operator,
+ T.Wildcard,
+ T.ArrayIndex,
+ T.Name)),
lambda y: (y.ttype in (T.String.Symbol,
T.Name,
T.Wildcard,
+ T.ArrayIndex,
T.Literal.String.Single,
T.Literal.Number.Integer,
T.Literal.Number.Float)
diff --git a/sqlparse/filters.py b/sqlparse/filters.py
index 697f22b..7e82f26 100644
--- a/sqlparse/filters.py
+++ b/sqlparse/filters.py
@@ -271,7 +271,11 @@ class StripWhitespaceFilter:
[self.process(stack, sgroup, depth + 1)
for sgroup in stmt.get_sublists()]
self._stripws(stmt)
- if depth == 0 and stmt.tokens[-1].is_whitespace():
+ if (
+ depth == 0
+ and stmt.tokens
+ and stmt.tokens[-1].is_whitespace()
+ ):
stmt.tokens.pop(-1)
diff --git a/sqlparse/lexer.py b/sqlparse/lexer.py
index ac50442..223d70c 100644
--- a/sqlparse/lexer.py
+++ b/sqlparse/lexer.py
@@ -172,12 +172,14 @@ class Lexer(compat.with_metaclass(LexerMeta)):
(r"'(''|\\'|[^'])*'", tokens.String.Single),
# not a real string literal in ANSI SQL:
(r'(""|".*?[^\\]")', tokens.String.Symbol),
+ (r'(?<=[\w\]])(\[[^\]]*?\])', tokens.Punctuation.ArrayIndex),
(r'(\[[^\]]+\])', tokens.Name),
((r'((LEFT\s+|RIGHT\s+|FULL\s+)?(INNER\s+|OUTER\s+|STRAIGHT\s+)?'
r'|(CROSS\s+|NATURAL\s+)?)?JOIN\b'), tokens.Keyword),
(r'END(\s+IF|\s+LOOP)?\b', tokens.Keyword),
(r'NOT NULL\b', tokens.Keyword),
(r'CREATE(\s+OR\s+REPLACE)?\b', tokens.Keyword.DDL),
+ (r'DOUBLE\s+PRECISION\b', tokens.Name.Builtin),
(r'(?<=\.)[^\W\d_]\w*', tokens.Name),
(r'[^\W\d_]\w*', is_keyword),
(r'[;:()\[\],\.]', tokens.Punctuation),
@@ -291,7 +293,13 @@ class Lexer(compat.with_metaclass(LexerMeta)):
statestack.pop()
elif state == '#push':
statestack.append(statestack[-1])
- else:
+ elif (
+ # Ugly hack - multiline-comments
+ # are not stackable
+ state != 'multiline-comments'
+ or not statestack
+ or statestack[-1] != 'multiline-comments'
+ ):
statestack.append(state)
elif isinstance(new_state, int):
# pop
diff --git a/sqlparse/sql.py b/sqlparse/sql.py
index 17509eb..367204d 100644
--- a/sqlparse/sql.py
+++ b/sqlparse/sql.py
@@ -383,21 +383,17 @@ class TokenList(Token):
def get_alias(self):
"""Returns the alias for this identifier or ``None``."""
+
+ # "name AS alias"
kw = self.token_next_match(0, T.Keyword, 'AS')
if kw is not None:
- alias = self.token_next(self.token_index(kw))
- if alias is None:
- return None
- else:
- next_ = self.token_next_by_instance(0, Identifier)
- if next_ is None:
- next_ = self.token_next_by_type(0, T.String.Symbol)
- if next_ is None:
- return None
- alias = next_
- if isinstance(alias, Identifier):
- return alias.get_name()
- return self._remove_quotes(compat.text_type(alias))
+ return self._get_first_name(kw, keywords=True)
+
+ # "name alias" or "complicated column expression alias"
+ if len(self.tokens) > 2:
+ return self._get_first_name(reverse=True)
+
+ return None
def get_name(self):
"""Returns the name of this identifier.
@@ -415,18 +411,43 @@ class TokenList(Token):
"""Returns the real name (object name) of this identifier."""
# a.b
dot = self.token_next_match(0, T.Punctuation, '.')
+ if dot is not None:
+ return self._get_first_name(self.token_index(dot))
+
+ return self._get_first_name()
+
+ def get_parent_name(self):
+ """Return name of the parent object if any.
+
+ A parent object is identified by the first occuring dot.
+ """
+ dot = self.token_next_match(0, T.Punctuation, '.')
if dot is None:
- next_ = self.token_next_by_type(0, T.Name)
- if next_ is not None:
- return self._remove_quotes(next_.value)
return None
-
- next_ = self.token_next_by_type(self.token_index(dot),
- (T.Name, T.Wildcard, T.String.Symbol))
- if next_ is None: # invalid identifier, e.g. "a."
+ prev_ = self.token_prev(self.token_index(dot))
+ if prev_ is None: # something must be verry wrong here..
return None
- return self._remove_quotes(next_.value)
+ return self._remove_quotes(prev_.value)
+
+ def _get_first_name(self, idx=None, reverse=False, keywords=False):
+ """Returns the name of the first token with a name"""
+
+ if idx and not isinstance(idx, int):
+ idx = self.token_index(idx) + 1
+ tokens = self.tokens[idx:] if idx else self.tokens
+ tokens = reversed(tokens) if reverse else tokens
+ types = [T.Name, T.Wildcard, T.String.Symbol]
+
+ if keywords:
+ types.append(T.Keyword)
+
+ for tok in tokens:
+ if tok.ttype in types:
+ return self._remove_quotes(tok.value)
+ elif isinstance(tok, Identifier) or isinstance(tok, Function):
+ return tok.get_name()
+ return None
class Statement(TokenList):
"""Represents a SQL statement."""
@@ -460,19 +481,6 @@ class Identifier(TokenList):
__slots__ = ('value', 'ttype', 'tokens')
- def get_parent_name(self):
- """Return name of the parent object if any.
-
- A parent object is identified by the first occuring dot.
- """
- dot = self.token_next_match(0, T.Punctuation, '.')
- if dot is None:
- return None
- prev_ = self.token_prev(self.token_index(dot))
- if prev_ is None: # something must be verry wrong here..
- return None
- return self._remove_quotes(prev_.value)
-
def is_wildcard(self):
"""Return ``True`` if this identifier contains a wildcard."""
token = self.token_next_by_type(0, T.Wildcard)
@@ -495,6 +503,13 @@ class Identifier(TokenList):
return None
return ordering.value.upper()
+ def get_array_indices(self):
+ """Returns an iterator of index expressions as strings"""
+
+ # Use [1:-1] index to discard the square brackets
+ return (tok.value[1:-1] for tok in self.tokens
+ if tok.ttype in T.ArrayIndex)
+
class IdentifierList(TokenList):
"""A list of :class:`~sqlparse.sql.Identifier`\'s."""
@@ -622,9 +637,10 @@ class Function(TokenList):
for t in parenthesis.tokens:
if isinstance(t, IdentifierList):
return t.get_identifiers()
- elif (isinstance(t, (Identifier, Function))
- or t.ttype in T.Literal):
- return [t]
+ elif isinstance(t, Identifier) or \
+ isinstance(t, Function) or \
+ t.ttype in T.Literal:
+ return [t,]
return []
diff --git a/sqlparse/tokens.py b/sqlparse/tokens.py
index 53c31ce..5ad51a6 100644
--- a/sqlparse/tokens.py
+++ b/sqlparse/tokens.py
@@ -48,6 +48,7 @@ Literal = Token.Literal
String = Literal.String
Number = Literal.Number
Punctuation = Token.Punctuation
+ArrayIndex = Punctuation.ArrayIndex
Operator = Token.Operator
Comparison = Operator.Comparison
Wildcard = Token.Wildcard