From b4835c451720119b5664e012d0d7fdfaeab5c2a4 Mon Sep 17 00:00:00 2001 From: chuenchen309 <48723787+chuenchen309@users.noreply.github.com> Date: Sun, 19 Jul 2026 14:55:08 +0800 Subject: [PATCH] Fix get_real_name for names with more than two dotted parts (#332) get_real_name anchored on the first dot (via token_next_by), so it returned the second component of a fully-qualified name. For a 2-part name the first dot is also the last, so it was accidentally correct; for 3+ parts it returned an intermediate component: parse("db.schema.tbl.col")[0].tokens[0].get_real_name() # 'schema', should be 'col' The object name is the component after the last dot. Anchor on the last dot instead. get_parent_name still uses the first dot, as documented, so parent/real name now come from opposite ends as intended. --- sqlparse/sql.py | 7 +++++-- tests/test_parse.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/sqlparse/sql.py b/sqlparse/sql.py index 5489f055..ec44a6da 100644 --- a/sqlparse/sql.py +++ b/sqlparse/sql.py @@ -18,8 +18,11 @@ class NameAliasMixin: def get_real_name(self): """Returns the real name (object name) of this identifier.""" - # a.b - dot_idx, _ = self.token_next_by(m=(T.Punctuation, '.')) + # a.b.c -> real name is the component after the *last* dot + dot_idx = None + for idx, tok in enumerate(self.tokens): + if tok.match(T.Punctuation, '.'): + dot_idx = idx return self._get_first_name(dot_idx, real_name=True) def get_alias(self): diff --git a/tests/test_parse.py b/tests/test_parse.py index 34800cb7..67168410 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -466,6 +466,24 @@ def test_get_real_name(): assert 't' == stmts[0].tokens[2].get_alias() +def test_get_real_name_multi_part_dotted(): + # issue 332: for a fully-qualified name the real name is the component + # after the *last* dot, not an intermediate one. + ident = sqlparse.parse("db.schema.tbl.col")[0].tokens[0] + assert 'col' == ident.get_real_name() + assert 'col' == ident.get_name() + # the parent object still anchors on the first dot + assert 'db' == ident.get_parent_name() + + aliased = sqlparse.parse("x.y.z AS w")[0].tokens[0] + assert 'z' == aliased.get_real_name() + assert 'w' == aliased.get_alias() + + # two-part names and function calls stay correct + assert 'b' == sqlparse.parse("a.b")[0].tokens[0].get_real_name() + assert 'func' == sqlparse.parse("mydb.sch.func(1)")[0].tokens[0].get_real_name() + + def test_from_subquery(): # issue 446 s = 'from(select 1)'