Skip to content

Commit e972939

Browse files
vashekandialbrecht
authored andcommitted
support typed literals (if that's what they're called)
1 parent aa0b85e commit e972939

4 files changed

Lines changed: 71 additions & 6 deletions

File tree

sqlparse/engine/grouping.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,34 @@ def post(tlist, pidx, tidx, nidx):
101101
_group(tlist, sql.Identifier, match, valid, valid, post)
102102

103103

104+
def group_typed_literal(tlist):
105+
# definitely not complete, see e.g.:
106+
# https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/interval-literal-syntax
107+
# https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/interval-literals
108+
# https://www.postgresql.org/docs/9.1/datatype-datetime.html
109+
# https://www.postgresql.org/docs/9.1/functions-datetime.html
110+
def match(token):
111+
return token.match(*sql.TypedLiteral.M_OPEN)
112+
113+
def match_to_extend(token):
114+
return isinstance(token, sql.TypedLiteral)
115+
116+
def valid_prev(token):
117+
return token is not None
118+
119+
def valid_next(token):
120+
return token is not None and token.match(*sql.TypedLiteral.M_CLOSE)
121+
122+
def valid_final(token):
123+
return token is not None and token.match(*sql.TypedLiteral.M_EXTEND)
124+
125+
def post(tlist, pidx, tidx, nidx):
126+
return tidx, nidx
127+
128+
_group(tlist, sql.TypedLiteral, match, valid_prev, valid_next, post, extend=False)
129+
_group(tlist, sql.TypedLiteral, match_to_extend, valid_prev, valid_final, post, extend=True)
130+
131+
104132
def group_period(tlist):
105133
def match(token):
106134
return token.match(T.Punctuation, '.')
@@ -217,13 +245,14 @@ def post(tlist, pidx, tidx, nidx):
217245
def group_operator(tlist):
218246
ttypes = T_NUMERICAL + T_STRING + T_NAME
219247
sqlcls = (sql.SquareBrackets, sql.Parenthesis, sql.Function,
220-
sql.Identifier, sql.Operation)
248+
sql.Identifier, sql.Operation, sql.TypedLiteral)
221249

222250
def match(token):
223251
return imt(token, t=(T.Operator, T.Wildcard))
224252

225253
def valid(token):
226-
return imt(token, i=sqlcls, t=ttypes)
254+
return imt(token, i=sqlcls, t=ttypes) \
255+
or token.match(T.Keyword, ("CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP"))
227256

228257
def post(tlist, pidx, tidx, nidx):
229258
tlist[tidx].ttype = T.Operator
@@ -372,6 +401,7 @@ def group(stmt):
372401
group_order,
373402
group_typecasts,
374403
group_tzcasts,
404+
group_typed_literal,
375405
group_operator,
376406
group_comparison,
377407
group_as,

sqlparse/keywords.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def is_keyword(value):
194194
'CONVERSION': tokens.Keyword,
195195
'CONVERT': tokens.Keyword,
196196
'COPY': tokens.Keyword,
197-
'CORRESPONTING': tokens.Keyword,
197+
'CORRESPONDING': tokens.Keyword,
198198
'COUNT': tokens.Keyword,
199199
'CREATEDB': tokens.Keyword,
200200
'CREATEUSER': tokens.Keyword,
@@ -429,11 +429,11 @@ def is_keyword(value):
429429
'PARAMETER': tokens.Keyword,
430430
'PARAMETERS': tokens.Keyword,
431431
'PARAMETER_MODE': tokens.Keyword,
432-
'PARAMATER_NAME': tokens.Keyword,
433-
'PARAMATER_ORDINAL_POSITION': tokens.Keyword,
432+
'PARAMETER_NAME': tokens.Keyword,
433+
'PARAMETER_ORDINAL_POSITION': tokens.Keyword,
434434
'PARAMETER_SPECIFIC_CATALOG': tokens.Keyword,
435435
'PARAMETER_SPECIFIC_NAME': tokens.Keyword,
436-
'PARAMATER_SPECIFIC_SCHEMA': tokens.Keyword,
436+
'PARAMETER_SPECIFIC_SCHEMA': tokens.Keyword,
437437
'PARTIAL': tokens.Keyword,
438438
'PASCAL': tokens.Keyword,
439439
'PCTFREE': tokens.Keyword,

sqlparse/sql.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,13 @@ def get_identifiers(self):
489489
yield token
490490

491491

492+
class TypedLiteral(TokenList):
493+
"""A typed literal (?), such as "date '2001-09-28'" or "interval '2 hours'"."""
494+
M_OPEN = T.Name.Builtin, None
495+
M_CLOSE = T.String.Single, None
496+
M_EXTEND = T.Keyword, ("DAY", "MONTH", "YEAR", "HOUR", "MINUTE", "SECOND")
497+
498+
492499
class Parenthesis(TokenList):
493500
"""Tokens between parenthesis."""
494501
M_OPEN = T.Punctuation, '('

tests/test_grouping.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,34 @@ def test_grouping_assignment(s):
3333
assert isinstance(parsed.tokens[0], sql.Assignment)
3434

3535

36+
@pytest.mark.parametrize('s, a, b', [
37+
('select a from b where c < d + e', sql.Identifier, sql.Identifier),
38+
('select a from b where c < d + interval \'1 day\'', sql.Identifier, sql.TypedLiteral),
39+
('select a from b where c < d + interval \'6\' month', sql.Identifier, sql.TypedLiteral),
40+
('select a from b where c < current_timestamp - interval \'1 day\'', sql.Token, sql.TypedLiteral),
41+
])
42+
def test_compare_expr(s, a, b):
43+
parsed = sqlparse.parse(s)[0]
44+
assert str(parsed) == s
45+
assert isinstance(parsed.tokens[2], sql.Identifier)
46+
assert isinstance(parsed.tokens[6], sql.Identifier)
47+
assert isinstance(parsed.tokens[8], sql.Where)
48+
assert len(parsed.tokens) == 9
49+
where = parsed.tokens[8]
50+
assert isinstance(where.tokens[2], sql.Comparison)
51+
assert len(where.tokens) == 3
52+
comparison = where.tokens[2]
53+
assert isinstance(comparison.tokens[0], sql.Identifier)
54+
assert comparison.tokens[2].ttype is T.Operator.Comparison
55+
assert isinstance(comparison.tokens[4], sql.Operation)
56+
assert len(comparison.tokens) == 5
57+
operation = comparison.tokens[4]
58+
assert isinstance(operation.tokens[0], a)
59+
assert operation.tokens[2].ttype is T.Operator
60+
assert isinstance(operation.tokens[4], b)
61+
assert len(operation.tokens) == 5
62+
63+
3664
def test_grouping_identifiers():
3765
s = 'select foo.bar from "myscheme"."table" where fail. order'
3866
parsed = sqlparse.parse(s)[0]

0 commit comments

Comments
 (0)