Skip to content

Commit fd589fd

Browse files
authored
Merge pull request #13 from mindsdb/fix-oracle
Update for oracle TPC DS/H benchmarks
2 parents ead588f + 8a049b9 commit fd589fd

File tree

7 files changed

+81
-7
lines changed

7 files changed

+81
-7
lines changed

mindsdb_sql_parser/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
__title__ = 'mindsdb_sql_parser'
22
__package_name__ = 'mindsdb_sql_parser'
3-
__version__ = '0.5.0'
3+
__version__ = '0.6.0'
44
__description__ = "Mindsdb SQL parser"
55
__email__ = "[email protected]"
66
__author__ = 'MindsDB Inc'

mindsdb_sql_parser/ast/create.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99

1010

1111
class TableColumn():
12-
def __init__(self, name, type='integer', length=None, default=None,
12+
def __init__(self, name, type='integer', length=None, length2=None, default=None,
1313
is_primary_key=False, nullable=None):
1414
self.name = name
1515
self.type = type
1616
self.is_primary_key = is_primary_key
1717
self.default = default
1818
self.length = length
19+
self.length2 = length2
1920
self.nullable = nullable
2021

2122
def __eq__(self, other):
@@ -97,7 +98,10 @@ def get_string(self, *args, **kwargs):
9798
else:
9899
type = str(col.type)
99100
if col.length is not None:
100-
type = f'{type}({col.length})'
101+
len_str = str(col.length)
102+
if col.length2 is not None:
103+
len_str += f", {col.length2}"
104+
type = f'{type}({len_str})'
101105
col_str = f'{col.name} {type}'
102106
if col.nullable is True:
103107
col_str += ' NULL'

mindsdb_sql_parser/ast/select/identifier.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def get_reserved_words():
3939

4040

4141
class Identifier(ASTNode):
42-
def __init__(self, path_str=None, parts=None, *args, **kwargs):
42+
def __init__(self, path_str=None, parts=None, is_outer=False, *args, **kwargs):
4343
super().__init__(*args, **kwargs)
4444
assert path_str or parts, "Either path_str or parts must be provided for an Identifier"
4545
assert not (path_str and parts), "Provide either path_str or parts, but not both"
@@ -54,6 +54,8 @@ def __init__(self, path_str=None, parts=None, *args, **kwargs):
5454
self.parts = parts
5555
# parts which were quoted
5656
self.is_quoted: List[bool] = is_quoted
57+
# used to define type of implicit join in oracle
58+
self.is_outer: bool = is_outer
5759

5860
@classmethod
5961
def from_path_str(self, value, *args, **kwargs):

mindsdb_sql_parser/lexer.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ class MindsDBLexer(Lexer):
5353
GROUP_BY, HAVING, ORDER_BY,
5454
STAR, FOR, UPDATE,
5555

56-
JOIN, INNER, OUTER, CROSS, LEFT, RIGHT, ON, ASOF, LATERAL,
56+
JOIN, INNER, OUTER, CROSS, LEFT, RIGHT, ON, ASOF, LATERAL, IS_OUTER,
5757

5858
UNION, ALL, INTERSECT, EXCEPT,
5959

60+
FETCH_FIRST, ROWS_ONLY,
61+
6062
# CASE
6163
CASE, ELSE, END, THEN, WHEN,
6264

@@ -165,7 +167,7 @@ class MindsDBLexer(Lexer):
165167
STATUS = r'\bSTATUS\b'
166168
GLOBAL = r'\bGLOBAL\b'
167169
PROCEDURE = r'\bPROCEDURE\b'
168-
PRIMARY_KEY = r'\bPRIMARY[_|\s]KEY\b'
170+
PRIMARY_KEY = r'\bPRIMARY[\s]KEY\b'
169171
DEFAULT = r'\bDEFAULT\b'
170172
FUNCTION = r'\bFUNCTION\b'
171173
INDEX = r'\bINDEX\b'
@@ -228,6 +230,9 @@ class MindsDBLexer(Lexer):
228230
STAR = r'\*'
229231
FOR = r'\bFOR\b'
230232
UPDATE = r'\bUPDATE\b'
233+
# FETCH FIRST and FETCH NEXT are the same
234+
FETCH_FIRST = r'\bFETCH[\s](FIRST|NEXT)\b'
235+
ROWS_ONLY = r'\bROWS[\s]ONLY\b'
231236

232237
JOIN = r'\bJOIN\b'
233238
INNER = r'\bINNER\b'
@@ -237,6 +242,7 @@ class MindsDBLexer(Lexer):
237242
RIGHT = r'\bRIGHT\b'
238243
ASOF = r'\bASOF\b'
239244
LATERAL = r'\bLATERAL\b'
245+
IS_OUTER = r'\(\+\)'
240246

241247
# UNION
242248

mindsdb_sql_parser/parser.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ def drop_table(self, p):
713713
'id id DEFAULT id',
714714
'id id PRIMARY_KEY',
715715
'id id LPAREN INTEGER RPAREN',
716+
'id id LPAREN INTEGER COMMA INTEGER RPAREN',
716717
'id id LPAREN INTEGER RPAREN DEFAULT id',
717718
'PRIMARY_KEY LPAREN column_list RPAREN',
718719
)
@@ -730,10 +731,18 @@ def table_column(self, p):
730731
elif hasattr(p, 'PRIMARY_KEY'):
731732
is_primary_key = True
732733

734+
length, length2 = None, None
735+
if hasattr(p, 'INTEGER'):
736+
length = p.INTEGER
737+
elif hasattr(p, 'INTEGER0'):
738+
length = p.INTEGER0
739+
length2 = p.INTEGER1
740+
733741
return TableColumn(
734742
name=p[0],
735743
type=p[1],
736-
length=getattr(p, 'INTEGER', None),
744+
length=length,
745+
length2=length2,
737746
default=default,
738747
is_primary_key=is_primary_key
739748
)
@@ -1100,6 +1109,7 @@ def select(self, p):
11001109
return select
11011110

11021111
@_('select LIMIT constant')
1112+
@_('select FETCH_FIRST constant ROWS_ONLY')
11031113
def select(self, p):
11041114
select = p.select
11051115
ensure_select_keyword_order(select, 'LIMIT')
@@ -1765,6 +1775,12 @@ def identifier(self, p):
17651775
def string(self, p):
17661776
return p[0]
17671777

1778+
@_('identifier IS_OUTER')
1779+
def identifier(self, p):
1780+
value = p[0]
1781+
value.is_outer = True
1782+
return value
1783+
17681784
@_('id', 'dquote_string')
17691785
def identifier(self, p):
17701786
value = p[0]

tests/test_base_sql/test_create.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ def test_create(self):
137137
location_id INT,
138138
num INT,
139139
name TEXT,
140+
balance decimal(5,2),
141+
links NUMBER(10, 0) NOT NULL,
140142
PRIMARY KEY (location_id, num)
141143
)
142144
'''
@@ -148,6 +150,8 @@ def test_create(self):
148150
TableColumn(name='location_id', type='INT', is_primary_key=True),
149151
TableColumn(name='num', type='INT', is_primary_key=True),
150152
TableColumn(name='name', type='TEXT'),
153+
TableColumn(name='balance', type='decimal', length=5, length2=2),
154+
TableColumn(name='links', type='NUMBER', nullable=False, length=10, length2=0),
151155
]
152156
)
153157

tests/test_mindsdb/test_oracle.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from mindsdb_sql_parser import parse_sql
2+
from mindsdb_sql_parser.ast import *
3+
from mindsdb_sql_parser.utils import JoinType
4+
5+
6+
class TestOracle:
7+
def test_left_outer_join_render_skip(self):
8+
sql = "SELECT * FROM customer, orders "\
9+
"WHERE c_custkey = o_custkey(+)"
10+
11+
ast = parse_sql(sql)
12+
expected_ast = Select(
13+
targets=[Star()],
14+
from_table=Join(left=Identifier('customer'),
15+
right=Identifier('orders'),
16+
join_type=JoinType.INNER_JOIN,
17+
implicit=True),
18+
where=BinaryOperation('=', args=[
19+
Identifier('c_custkey'), Identifier('o_custkey', is_outer=True)
20+
]),
21+
)
22+
23+
# don't render (+)
24+
assert str(ast).lower() == sql.replace('(+)', '').lower()
25+
assert str(ast) == str(expected_ast)
26+
assert ast.to_tree() == expected_ast.to_tree()
27+
28+
assert ast.where.args[1].is_outer is True
29+
30+
def test_limit(self):
31+
for keyword in ('NEXT', 'FIRST'):
32+
sql = f"SELECT * FROM customer FETCH {keyword} 10 ROWS ONLY"
33+
34+
ast = parse_sql(sql)
35+
expected_ast = Select(
36+
targets=[Star()],
37+
from_table=Identifier('customer'),
38+
limit=Constant(10)
39+
)
40+
41+
assert str(ast) == str(expected_ast)
42+
assert ast.to_tree() == expected_ast.to_tree()

0 commit comments

Comments
 (0)