summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndi Albrecht <albrecht.andi@gmail.com>2015-02-21 07:55:30 +0100
committerAndi Albrecht <albrecht.andi@gmail.com>2015-02-21 07:55:30 +0100
commit2d72b7afcb6d736f5a92a373bbb7a3aa1abed49b (patch)
treee2934e930bfa70780f1fad670842e394903551a0
parent51871a8ffa0de99e98e2b24794458c9aa6b36689 (diff)
parent6f134c67b6c89c2985e29bd5bc4c809cc16c06a5 (diff)
downloadsqlparse-2d72b7afcb6d736f5a92a373bbb7a3aa1abed49b.tar.gz
Merge pull request #171 from darikg/alias_bugfix
Fix #167
-rw-r--r--sqlparse/engine/grouping.py7
-rw-r--r--sqlparse/sql.py76
-rw-r--r--tests/test_grouping.py51
3 files changed, 92 insertions, 42 deletions
diff --git a/sqlparse/engine/grouping.py b/sqlparse/engine/grouping.py
index 47d3c2e..9314b89 100644
--- a/sqlparse/engine/grouping.py
+++ b/sqlparse/engine/grouping.py
@@ -155,9 +155,10 @@ 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 is T.ArrayIndex),
+ or y.ttype in (T.Operator,
+ T.Wildcard,
+ T.ArrayIndex,
+ T.Name)),
lambda y: (y.ttype in (T.String.Symbol,
T.Name,
T.Wildcard,
diff --git a/sqlparse/sql.py b/sqlparse/sql.py
index c974b35..8492c5e 100644
--- a/sqlparse/sql.py
+++ b/sqlparse/sql.py
@@ -390,21 +390,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(unicode(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.
@@ -422,18 +418,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."""
@@ -467,19 +488,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)
diff --git a/tests/test_grouping.py b/tests/test_grouping.py
index 44a8072..c598258 100644
--- a/tests/test_grouping.py
+++ b/tests/test_grouping.py
@@ -15,11 +15,12 @@ class TestGrouping(TestCaseBase):
s = 'select (select (x3) x2) and (y2) bar'
parsed = sqlparse.parse(s)[0]
self.ndiffAssertEqual(s, str(parsed))
- self.assertEqual(len(parsed.tokens), 9)
+ self.assertEqual(len(parsed.tokens), 7)
self.assert_(isinstance(parsed.tokens[2], sql.Parenthesis))
- self.assert_(isinstance(parsed.tokens[-3], sql.Parenthesis))
- self.assertEqual(len(parsed.tokens[2].tokens), 7)
- self.assert_(isinstance(parsed.tokens[2].tokens[3], sql.Parenthesis))
+ self.assert_(isinstance(parsed.tokens[-1], sql.Identifier))
+ self.assertEqual(len(parsed.tokens[2].tokens), 5)
+ self.assert_(isinstance(parsed.tokens[2].tokens[3], sql.Identifier))
+ self.assert_(isinstance(parsed.tokens[2].tokens[3].tokens[0], sql.Parenthesis))
self.assertEqual(len(parsed.tokens[2].tokens[3].tokens), 3)
def test_comments(self):
@@ -145,7 +146,7 @@ class TestGrouping(TestCaseBase):
s = 'select x from (select y from foo where bar = 1) z'
p = sqlparse.parse(s)[0]
self.ndiffAssertEqual(s, unicode(p))
- self.assertTrue(isinstance(p.tokens[-3].tokens[-2], sql.Where))
+ self.assertTrue(isinstance(p.tokens[-1].tokens[0].tokens[-2], sql.Where))
def test_typecast(self):
s = 'select foo::integer from bar'
@@ -345,3 +346,43 @@ def test_nested_begin():
assert inner.tokens[0].value == 'BEGIN'
assert inner.tokens[-1].value == 'END'
assert isinstance(inner, sql.Begin)
+
+
+def test_aliased_column_without_as():
+ p = sqlparse.parse('foo bar')[0].tokens
+ assert len(p) == 1
+ assert p[0].get_real_name() == 'foo'
+ assert p[0].get_alias() == 'bar'
+
+ p = sqlparse.parse('foo.bar baz')[0].tokens[0]
+ assert p.get_parent_name() == 'foo'
+ assert p.get_real_name() == 'bar'
+ assert p.get_alias() == 'baz'
+
+
+def test_qualified_function():
+ p = sqlparse.parse('foo()')[0].tokens[0]
+ assert p.get_parent_name() is None
+ assert p.get_real_name() == 'foo'
+
+ p = sqlparse.parse('foo.bar()')[0].tokens[0]
+ assert p.get_parent_name() == 'foo'
+ assert p.get_real_name() == 'bar'
+
+
+def test_aliased_function_without_as():
+ p = sqlparse.parse('foo() bar')[0].tokens[0]
+ assert p.get_parent_name() is None
+ assert p.get_real_name() == 'foo'
+ assert p.get_alias() == 'bar'
+
+ p = sqlparse.parse('foo.bar() baz')[0].tokens[0]
+ assert p.get_parent_name() == 'foo'
+ assert p.get_real_name() == 'bar'
+ assert p.get_alias() == 'baz'
+
+
+def test_aliased_literal_without_as():
+ p = sqlparse.parse('1 foo')[0].tokens
+ assert len(p) == 1
+ assert p[0].get_alias() == 'foo' \ No newline at end of file