Skip to content

Commit 398f179

Browse files
authored
Support variables in views (#77)
* dump @@ to string * del python 3.9 support * allow `using` in views
1 parent 8107d6b commit 398f179

13 files changed

Lines changed: 177 additions & 42 deletions

File tree

.github/workflows/pypi.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ jobs:
1313
contents: read
1414
if: github.actor != 'mindsdbadmin'
1515
steps:
16-
- uses: actions/checkout@v4
16+
- uses: actions/checkout@v6
1717
- name: Set up Python
18-
uses: actions/setup-python@v5.6.0
18+
uses: actions/setup-python@v6.1.0
1919
with:
2020
python-version: ${{ vars.CI_PYTHON_VERSION }}
2121
- name: Install dependencies

.github/workflows/test.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ jobs:
1313
strategy:
1414
matrix:
1515
os: [ubuntu-latest, windows-latest]
16-
python-version: ["3.9","3.10","3.11","3.12","3.13"]
16+
python-version: ["3.10","3.11","3.12","3.13"]
1717
steps:
18-
- uses: actions/checkout@v4
18+
- uses: actions/checkout@v6
1919
- name: Set up Python ${{ matrix.python-version }}
20-
uses: actions/setup-python@v5.6.0
20+
uses: actions/setup-python@v6.1.0
2121
with:
2222
python-version: ${{ matrix.python-version }}
2323
- name: Install dependencies
@@ -37,11 +37,11 @@ jobs:
3737
permissions:
3838
pull-requests: write
3939
steps:
40-
- uses: actions/checkout@v4
41-
- name: Set up Python 3.9
42-
uses: actions/setup-python@v5.6.0
40+
- uses: actions/checkout@v6
41+
- name: Set up Python 3.10
42+
uses: actions/setup-python@v6.1.0
4343
with:
44-
python-version: 3.9
44+
python-version: "3.10"
4545

4646
- name: Install dependencies
4747
run: |

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.13.2'
3+
__version__ = '0.13.3'
44
__description__ = "Mindsdb SQL parser"
55
__email__ = "jorge@mindsdb.com"
66
__author__ = 'MindsDB Inc'

mindsdb_sql_parser/ast/mindsdb/alter_view.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from mindsdb_sql_parser.ast.base import ASTNode
2-
from mindsdb_sql_parser.utils import indent
2+
from mindsdb_sql_parser.utils import indent, dump_using_dict
33
from mindsdb_sql_parser.ast.select.identifier import Identifier
44

55

@@ -12,6 +12,7 @@ def __init__(
1212
name: Identifier,
1313
query_str: str,
1414
from_table: Identifier = None,
15+
using: dict = None,
1516
*args,
1617
**kwargs
1718
):
@@ -20,11 +21,13 @@ def __init__(
2021
name: Identifier -- name of the view to alter.
2122
query_str: str -- the new query string for the view.
2223
from_table: Identifier -- optional table to alter the view from.
24+
using: dict -- optional USING parameters.
2325
"""
2426
super().__init__(*args, **kwargs)
2527
self.name = name
2628
self.query_str = query_str
2729
self.from_table = from_table
30+
self.using = using
2831

2932
def to_tree(self, *args, level=0, **kwargs):
3033
ind = indent(level)
@@ -33,17 +36,22 @@ def to_tree(self, *args, level=0, **kwargs):
3336
name_str = f'\n{ind1}name={self.name.to_string()},'
3437
from_table_str = f'\n{ind1}from_table=\n{self.from_table.to_tree(level=level+2)},' if self.from_table else ''
3538
query_str = f'\n{ind1}query="{self.query_str}"'
39+
using_str = f'\n{ind1}using={self.using},' if self.using else ''
3640

3741
out_str = f'{ind}AlterView(' \
3842
f'{name_str}' \
3943
f'{query_str}' \
4044
f'{from_table_str}' \
45+
f'{using_str}' \
4146
f'\n{ind})'
4247
return out_str
4348

4449
def get_string(self, *args, **kwargs):
4550
from_str = f' FROM {str(self.from_table)}' if self.from_table else ''
51+
using_str = ''
52+
if self.using:
53+
using_str = f'USING {dump_using_dict(self.using)}'
4654

47-
out_str = f'ALTER VIEW {self.name.to_string()}{from_str} AS ( {self.query_str} )'
55+
out_str = f'ALTER VIEW {self.name.to_string()}{from_str} AS ( {self.query_str} ){using_str}'
4856

4957
return out_str

mindsdb_sql_parser/ast/mindsdb/create_job.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import json
2-
import datetime as dt
3-
41
from mindsdb_sql_parser.ast.base import ASTNode
52
from mindsdb_sql_parser.utils import indent
63

mindsdb_sql_parser/ast/mindsdb/create_predictor.py

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from mindsdb_sql_parser.ast.base import ASTNode
2-
from mindsdb_sql_parser.utils import indent, dump_json
2+
from mindsdb_sql_parser.utils import indent, dump_using_dict
33
from mindsdb_sql_parser.ast.select import Identifier
4-
from mindsdb_sql_parser.ast.select.operation import Object
54

65

76
class CreatePredictorBase(ASTNode):
@@ -96,21 +95,7 @@ def get_string(self, *args, **kwargs):
9695
horizon_str = f'HORIZON {self.horizon} ' if self.horizon is not None else ''
9796
using_str = ''
9897
if self.using:
99-
using_ar = []
100-
for key, value in self.using.items():
101-
if isinstance(value, Object):
102-
args = [
103-
f'{k}={dump_json(v)}'
104-
for k, v in value.params.items()
105-
]
106-
args_str = ', '.join(args)
107-
value = f'{value.type}({args_str})'
108-
else:
109-
value = dump_json(value)
110-
111-
using_ar.append(f'{Identifier(key).to_string()}={value}')
112-
113-
using_str = f'USING ' + ', '.join(using_ar)
98+
using_str = f'USING {dump_using_dict(self.using)}'
11499

115100
query_str = ''
116101
if self.query_str is not None:
Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
from mindsdb_sql_parser.ast.base import ASTNode
2-
from mindsdb_sql_parser.utils import indent
3-
from mindsdb_sql_parser.ast.select.identifier import Identifier
2+
from mindsdb_sql_parser.utils import indent, dump_using_dict
3+
44

55
class CreateView(ASTNode):
66
def __init__(self,
77
name,
88
query_str,
99
from_table=None,
1010
if_not_exists=False,
11+
using=None,
1112
*args, **kwargs):
1213
super().__init__(*args, **kwargs)
1314
self.name = name
1415
self.query_str = query_str
1516
self.from_table = from_table
1617
self.if_not_exists = if_not_exists
18+
self.using = using
1719

1820
def to_tree(self, *args, level=0, **kwargs):
1921
ind = indent(level)
@@ -22,18 +24,24 @@ def to_tree(self, *args, level=0, **kwargs):
2224
from_table_str = f'\n{ind1}from_table=\n{self.from_table.to_tree(level=level+2)},' if self.from_table else ''
2325
query_str = f'\n{ind1}query="{self.query_str}"'
2426
if_not_exists_str = f'\n{ind1}if_not_exists=True,' if self.if_not_exists else ''
27+
using_str = f'\n{ind1}using={self.using},' if self.using else ''
2528

2629
out_str = f'{ind}CreateView(' \
2730
f'{if_not_exists_str}' \
2831
f'{name_str}' \
2932
f'{query_str}' \
3033
f'{from_table_str}' \
34+
f'{using_str}' \
3135
f'\n{ind})'
3236
return out_str
3337

3438
def get_string(self, *args, **kwargs):
3539
from_str = f'FROM {str(self.from_table)} ' if self.from_table else ''
36-
out_str = f'CREATE VIEW {"IF NOT EXISTS " if self.if_not_exists else ""}{self.name.to_string()} {from_str}AS ( {self.query_str} )'
40+
using_str = ''
41+
if self.using:
42+
using_str = f'USING {dump_using_dict(self.using)}'
43+
44+
out_str = f'CREATE VIEW {"IF NOT EXISTS " if self.if_not_exists else ""}{self.name.to_string()} {from_str}AS ( {self.query_str} ){using_str}'
3745

3846
return out_str
3947

mindsdb_sql_parser/parser.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -721,26 +721,34 @@ def use(self, p):
721721
return Use(value=p.identifier)
722722

723723
# CREATE VIEW
724-
@_('CREATE VIEW if_not_exists_or_empty identifier create_view_from_table_or_nothing AS LPAREN raw_query RPAREN',
724+
@_('CREATE VIEW if_not_exists_or_empty identifier create_view_from_table_or_nothing AS LPAREN raw_query RPAREN USING kw_parameter_list',
725+
'CREATE VIEW if_not_exists_or_empty identifier create_view_from_table_or_nothing LPAREN raw_query RPAREN USING kw_parameter_list',
726+
'CREATE VIEW if_not_exists_or_empty identifier create_view_from_table_or_nothing AS LPAREN raw_query RPAREN',
725727
'CREATE VIEW if_not_exists_or_empty identifier create_view_from_table_or_nothing LPAREN raw_query RPAREN')
726728
def create_view(self, p):
727729
query_str = tokens_to_string(p.raw_query)
730+
using = getattr(p, 'kw_parameter_list', None)
728731

729732
return CreateView(name=p.identifier,
730733
from_table=p.create_view_from_table_or_nothing,
731734
query_str=query_str,
732-
if_not_exists=p.if_not_exists_or_empty)
735+
if_not_exists=p.if_not_exists_or_empty,
736+
using=using)
733737

734738
# ALTER VIEW
735-
@_('ALTER VIEW identifier create_view_from_table_or_nothing AS LPAREN raw_query RPAREN',
739+
@_('ALTER VIEW identifier create_view_from_table_or_nothing AS LPAREN raw_query RPAREN USING kw_parameter_list',
740+
'ALTER VIEW identifier create_view_from_table_or_nothing LPAREN raw_query RPAREN USING kw_parameter_list',
741+
'ALTER VIEW identifier create_view_from_table_or_nothing AS LPAREN raw_query RPAREN',
736742
'ALTER VIEW identifier create_view_from_table_or_nothing LPAREN raw_query RPAREN')
737743
def alter_view(self, p):
738744
query_str = tokens_to_string(p.raw_query)
745+
using = getattr(p, 'kw_parameter_list', None)
739746

740747
return AlterView(
741748
name=p.identifier,
742749
from_table=p.create_view_from_table_or_nothing,
743-
query_str=query_str
750+
query_str=query_str,
751+
using=using
744752
)
745753

746754
@_('FROM identifier')

mindsdb_sql_parser/utils.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,18 @@ def tokens_to_string(tokens):
8181
# filling space between tokens
8282
line += ' '*(token.index - shift - len(line))
8383

84+
match token.type:
85+
case 'VARIABLE':
86+
token_value = '@' + token.value
87+
case 'SYSTEM_VARIABLE':
88+
token_value = '@@' + token.value
89+
case _:
90+
token_value = token.value
91+
8492
# add token
85-
line += token.value
93+
line += token_value
8694

87-
last_pos = token.index + len(token.value)
95+
last_pos = token.index + len(token_value)
8896

8997
# last line
9098
content += line
@@ -143,3 +151,25 @@ def dump_json(obj) -> str:
143151
return "true" if obj else "false"
144152

145153
return dump_json(str(obj))
154+
155+
156+
def dump_using_dict(using: dict | None) -> str | None:
157+
from mindsdb_sql_parser.ast.select import Identifier
158+
from mindsdb_sql_parser.ast.select.operation import Object
159+
160+
if using is None:
161+
return None
162+
using_ar = []
163+
for key, value in using.items():
164+
if isinstance(value, Object):
165+
args = [
166+
f'{k}={dump_json(v)}'
167+
for k, v in value.params.items()
168+
]
169+
args_str = ', '.join(args)
170+
value = f'{value.type}({args_str})'
171+
else:
172+
value = dump_json(value)
173+
174+
using_ar.append(f'{Identifier(key).to_string()}={value}')
175+
return ', '.join(using_ar)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def run(self):
3232
"Programming Language :: Python :: 3",
3333
"Operating System :: OS Independent",
3434
],
35-
python_requires=">=3.9",
35+
python_requires=">=3.10",
3636
cmdclass={
3737
'build': Build
3838
},

0 commit comments

Comments
 (0)