11import re
22from copy import copy , deepcopy
3- from typing import List
3+ from typing import List , Optional
44
55from mindsdb_sql_parser .ast .base import ASTNode
66from mindsdb_sql_parser .utils import indent
@@ -27,19 +27,28 @@ def path_str_to_parts(path_str: str):
2727}
2828
2929
30- def get_reserved_words ():
31- from mindsdb_sql_parser .lexer import MindsDBLexer
30+ _reserved_keywords : set [str ] = None
3231
33- reserved = RESERVED_KEYWORDS
34- for word in MindsDBLexer .tokens :
35- if '_' not in word :
36- # exclude combinations
37- reserved .add (word )
38- return reserved
32+
33+ def get_reserved_words () -> set [str ]:
34+ global _reserved_keywords
35+
36+ if _reserved_keywords is None :
37+ from mindsdb_sql_parser .lexer import MindsDBLexer
38+
39+ _reserved_keywords = RESERVED_KEYWORDS
40+ for word in MindsDBLexer .tokens :
41+ if '_' not in word :
42+ # exclude combinations
43+ _reserved_keywords .add (word )
44+ return _reserved_keywords
3945
4046
4147class Identifier (ASTNode ):
42- def __init__ (self , path_str = None , parts = None , is_outer = False , with_rollup = False , * args , ** kwargs ):
48+ def __init__ (
49+ self , path_str = None , parts = None , is_outer = False , with_rollup = False ,
50+ is_quoted : Optional [List [bool ]] = None , * args , ** kwargs
51+ ):
4352 super ().__init__ (* args , ** kwargs )
4453 assert path_str or parts , "Either path_str or parts must be provided for an Identifier"
4554 assert not (path_str and parts ), "Provide either path_str or parts, but not both"
@@ -48,7 +57,7 @@ def __init__(self, path_str=None, parts=None, is_outer=False, with_rollup=False,
4857
4958 if path_str and not parts :
5059 parts , is_quoted = path_str_to_parts (path_str )
51- else :
60+ elif is_quoted is None :
5261 is_quoted = [False ] * len (parts )
5362 assert isinstance (parts , list )
5463 self .parts = parts
@@ -63,22 +72,26 @@ def from_path_str(self, value, *args, **kwargs):
6372 parts , _ = path_str_to_parts (value )
6473 return Identifier (parts = parts , * args , ** kwargs )
6574
66- def parts_to_str (self ):
67- out_parts = []
75+ def append (self , other : "Identifier" ) -> None :
76+ self .parts += other .parts
77+ self .is_quoted += other .is_quoted
78+
79+ def iter_parts_str (self ):
6880 reserved_words = get_reserved_words ()
69- for part in self .parts :
81+ for part , is_quoted in zip ( self .parts , self . is_quoted ) :
7082 if isinstance (part , Star ):
7183 part = str (part )
7284 else :
7385 if (
74- not no_wrap_identifier_regex . fullmatch ( part )
75- or
76- part .upper () in reserved_words
86+ is_quoted
87+ or not no_wrap_identifier_regex . fullmatch ( part )
88+ or part .upper () in reserved_words
7789 ):
7890 part = f'`{ part } `'
91+ yield part
7992
80- out_parts . append ( part )
81- return '.' .join (out_parts )
93+ def parts_to_str ( self ):
94+ return '.' .join (self . iter_parts_str () )
8295
8396 def to_tree (self , * args , level = 0 , ** kwargs ):
8497 alias_str = f', alias={ self .alias .to_tree ()} ' if self .alias else ''
0 commit comments