diff --git a/mindsdb_sql/parser/ast/create.py b/mindsdb_sql/parser/ast/create.py index ee256673..8813958d 100644 --- a/mindsdb_sql/parser/ast/create.py +++ b/mindsdb_sql/parser/ast/create.py @@ -15,12 +15,14 @@ def __init__(self, from_select=None, columns: List[TableColumn] = None, is_replace=False, + if_not_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name self.is_replace = is_replace self.from_select = from_select self.columns = columns + self.if_not_exists = if_not_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -45,6 +47,7 @@ def to_tree(self, *args, level=0, **kwargs): columns_str = f'{ind1}columns=\n' + '\n'.join(columns) out_str = f'{ind}CreateTable(\n' \ + f'{ind1}if_not_exists={self.if_not_exists},\n' \ f'{ind1}name={self.name}\n' \ f'{replace_str}' \ f'{from_select_str}' \ @@ -72,4 +75,4 @@ def get_string(self, *args, **kwargs): from_select_str = self.from_select.to_string() name_str = str(self.name) - return f'CREATE{replace_str} TABLE {name_str} {columns_str} {from_select_str}' + return f'CREATE{replace_str} TABLE {"IF NOT EXISTS " if self.if_not_exists else ""}{name_str} {columns_str} {from_select_str}' diff --git a/mindsdb_sql/parser/dialects/mindsdb/create_database.py b/mindsdb_sql/parser/dialects/mindsdb/create_database.py index 057d8686..759dbd57 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/create_database.py +++ b/mindsdb_sql/parser/dialects/mindsdb/create_database.py @@ -9,12 +9,14 @@ def __init__(self, engine, parameters, is_replace=False, + if_not_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name self.engine = engine self.parameters = parameters self.is_replace = is_replace + self.if_not_exists = if_not_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -28,6 +30,7 @@ def to_tree(self, *args, level=0, **kwargs): replace_str = f'\n{ind1}is_replace=True' out_str = f'{ind}CreateDatabase(' \ + f'\n{ind1}if_not_exists={self.if_not_exists},' \ f'{name_str}' \ f'{engine_str}' \ f'{parameters_str}' \ @@ -43,5 +46,5 @@ def get_string(self, *args, **kwargs): parameters_str = '' if self.parameters: parameters_str = f', PARAMETERS = {json.dumps(self.parameters)}' - out_str = f'CREATE{replace_str} DATABASE {self.name.to_string()} WITH ENGINE = {repr(self.engine)}{parameters_str}' + out_str = f'CREATE{replace_str} DATABASE {"IF NOT EXISTS " if self.if_not_exists else ""}{self.name.to_string()} WITH ENGINE = {repr(self.engine)}{parameters_str}' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/create_job.py b/mindsdb_sql/parser/dialects/mindsdb/create_job.py index 69f32196..b7cfd5e2 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/create_job.py +++ b/mindsdb_sql/parser/dialects/mindsdb/create_job.py @@ -12,6 +12,7 @@ def __init__(self, start_str=None, end_str=None, repeat_str=None, + if_not_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name @@ -20,6 +21,7 @@ def __init__(self, self.end_str = end_str self.repeat_str = repeat_str self.date_format = '%Y-%m-%d %H:%M:%S' + self.if_not_exists = if_not_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -40,7 +42,12 @@ def to_tree(self, *args, level=0, **kwargs): if self.repeat_str is not None: repeat_str = f'\n{ind1}repeat_str={self.repeat_str},' + if_not_exists_str = '' + if self.if_not_exists: + if_not_exists_str = f'\n{ind1}if_not_exists=True,' + out_str = f'{ind}CreateJob(' \ + f'{if_not_exists_str}' \ f'{name_str}' \ f'{query_str}' \ f'{start_str}' \ @@ -63,5 +70,5 @@ def get_string(self, *args, **kwargs): if self.repeat_str is not None: repeat_str = f" EVERY '{self.repeat_str}'" - out_str = f'CREATE JOB {self.name.to_string()} ({self.query_str}){start_str}{end_str}{repeat_str}' + out_str = f'CREATE JOB {"IF NOT EXISTS" if self.if_not_exists else ""} {self.name.to_string()} ({self.query_str}){start_str}{end_str}{repeat_str}' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/create_ml_engine.py b/mindsdb_sql/parser/dialects/mindsdb/create_ml_engine.py index ca9ddce0..9e70f9a1 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/create_ml_engine.py +++ b/mindsdb_sql/parser/dialects/mindsdb/create_ml_engine.py @@ -7,11 +7,13 @@ def __init__(self, name, handler, params=None, + if_not_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name self.handler = handler self.params = params + self.if_not_exists = if_not_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -20,6 +22,7 @@ def to_tree(self, *args, level=0, **kwargs): param_str = f'{repr(self.params)},' out_str = f'{ind}CreateMLEngine(' \ + f'\n{ind1}if_not_exists={self.if_not_exists}' \ f'\n{ind1}name={self.name.to_tree()}' \ f'\n{ind1}handler={self.handler}' \ f'\n{ind1}using={param_str}' \ @@ -33,7 +36,7 @@ def get_string(self, *args, **kwargs): using_str = 'USING ' + ', '.join(using_ar) - out_str = f'CREATE ML_ENGINE {self.name.to_string()} FROM {self.handler} {using_str}' + out_str = f'CREATE ML_ENGINE {"IF NOT EXISTS" if self.if_not_exists else ""} {self.name.to_string()} FROM {self.handler} {using_str}' return out_str.strip() diff --git a/mindsdb_sql/parser/dialects/mindsdb/create_predictor.py b/mindsdb_sql/parser/dialects/mindsdb/create_predictor.py index ae7f7bbc..09401de6 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/create_predictor.py +++ b/mindsdb_sql/parser/dialects/mindsdb/create_predictor.py @@ -18,6 +18,7 @@ def __init__(self, horizon=None, using=None, is_replace=False, + if_not_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name @@ -31,6 +32,7 @@ def __init__(self, self.horizon = horizon self.using = using self.is_replace = is_replace + self.if_not_exists = if_not_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -69,7 +71,10 @@ def to_tree(self, *args, level=0, **kwargs): horizon_str = f'\n{ind1}horizon={repr(self.horizon)},' using_str = f'\n{ind1}using={repr(self.using)},' + if_not_exists_str = f'\n{ind1}if_not_exists={self.if_not_exists},' if self.if_not_exists else '' + out_str = f'{ind}{self.__class__.__name__}(' \ + f'{if_not_exists_str}' \ f'{name_str}' \ f'{integration_name_str}' \ f'{query_str}' \ @@ -119,7 +124,9 @@ def get_string(self, *args, **kwargs): if self.integration_name is not None: integration_name_str = f'FROM {self.integration_name.to_string()} ' - out_str = f'{self._command} {self.name.to_string()} {integration_name_str}{query_str}' \ + if_not_exists_str = 'IF NOT EXISTS ' if self.if_not_exists else '' + + out_str = f'{self._command} {if_not_exists_str}{self.name.to_string()} {integration_name_str}{query_str}' \ f'{datasource_name_str}' \ f'{targets_str} ' \ f'{order_by_str}' \ diff --git a/mindsdb_sql/parser/dialects/mindsdb/create_view.py b/mindsdb_sql/parser/dialects/mindsdb/create_view.py index d56ac7d1..5abc6d50 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/create_view.py +++ b/mindsdb_sql/parser/dialects/mindsdb/create_view.py @@ -7,6 +7,7 @@ def __init__(self, name, query_str, from_table=None, + if_not_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) # todo remove it @@ -15,6 +16,7 @@ def __init__(self, self.name = name self.query_str = query_str self.from_table = from_table + self.if_not_exists = if_not_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -23,8 +25,10 @@ def to_tree(self, *args, level=0, **kwargs): # name_str = f'\n{ind1}name={self.name.to_string()},' from_table_str = f'\n{ind1}from_table=\n{self.from_table.to_tree(level=level+2)},' if self.from_table else '' query_str = f'\n{ind1}query="{self.query_str}"' + if_not_exists_str = f'\n{ind1}if_not_exists=True,' if self.if_not_exists else '' out_str = f'{ind}CreateView(' \ + f'{if_not_exists_str}' \ f'{name_str}' \ f'{query_str}' \ f'{from_table_str}' \ @@ -34,7 +38,7 @@ def to_tree(self, *args, level=0, **kwargs): def get_string(self, *args, **kwargs): from_str = f'FROM {str(self.from_table)} ' if self.from_table else '' # out_str = f'CREATE VIEW {self.name.to_string()} {from_str}AS ( {self.query_str} )' - out_str = f'CREATE VIEW {str(self.name)} {from_str}AS ( {self.query_str} )' + out_str = f'CREATE VIEW {"IF NOT EXISTS " if self.if_not_exists else ""}{str(self.name)} {from_str}AS ( {self.query_str} )' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/drop_dataset.py b/mindsdb_sql/parser/dialects/mindsdb/drop_dataset.py index 31141d8c..abdbb288 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/drop_dataset.py +++ b/mindsdb_sql/parser/dialects/mindsdb/drop_dataset.py @@ -5,9 +5,11 @@ class DropDataset(Drop): def __init__(self, name, + if_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name + self.if_exists = if_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -15,11 +17,12 @@ def to_tree(self, *args, level=0, **kwargs): name_str = f'\n{ind1}name={self.name.to_tree()},' out_str = f'{ind}DropDataset(' \ + f'{ind1}if_exists={self.if_exists},' \ f'{name_str}' \ f'\n{ind})' return out_str def get_string(self, *args, **kwargs): - out_str = f'DROP DATASET {str(self.name)}' + out_str = f'DROP DATASET {"IF EXISTS " if self.if_exists else ""}{str(self.name)}' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/drop_datasource.py b/mindsdb_sql/parser/dialects/mindsdb/drop_datasource.py index ffb55f91..59830a08 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/drop_datasource.py +++ b/mindsdb_sql/parser/dialects/mindsdb/drop_datasource.py @@ -5,9 +5,11 @@ class DropDatasource(Drop): def __init__(self, name, + if_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name + self.if_exists = if_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -15,11 +17,12 @@ def to_tree(self, *args, level=0, **kwargs): name_str = f'\n{ind1}name={self.name.to_tree()},' out_str = f'{ind}DropDatasource(' \ + f'{ind1}if_exists={self.if_exists},' \ f'{name_str}' \ f'\n{ind})' return out_str def get_string(self, *args, **kwargs): - out_str = f'DROP DATASOURCE {str(self.name)}' + out_str = f'DROP DATASOURCE {"IF EXISTS " if self.if_exists else ""}{str(self.name)}' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/drop_integration.py b/mindsdb_sql/parser/dialects/mindsdb/drop_integration.py index 95ac6470..227dcc08 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/drop_integration.py +++ b/mindsdb_sql/parser/dialects/mindsdb/drop_integration.py @@ -6,9 +6,11 @@ class DropIntegration(Drop): def __init__(self, name, + if_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name + self.if_exists = if_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -16,10 +18,11 @@ def to_tree(self, *args, level=0, **kwargs): name_str = f'\n{ind1}name={self.name.to_tree()},' out_str = f'{ind}DropIntegration(' \ + f'{ind1}if_exists={self.if_exists},' \ f'{name_str}' \ f'\n{ind})' return out_str def get_string(self, *args, **kwargs): - out_str = f'DROP INTEGRATION {str(self.name)}' + out_str = f'DROP INTEGRATION {"IF EXISTS " if self.if_exists else ""}{str(self.name)}' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/drop_job.py b/mindsdb_sql/parser/dialects/mindsdb/drop_job.py index 10bbd6cb..3589665e 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/drop_job.py +++ b/mindsdb_sql/parser/dialects/mindsdb/drop_job.py @@ -5,9 +5,11 @@ class DropJob(Drop): def __init__(self, name, + if_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name + self.if_exists = if_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -15,11 +17,12 @@ def to_tree(self, *args, level=0, **kwargs): name_str = f'\n{ind1}name={self.name.to_tree()},' out_str = f'{ind}DropJob(' \ + f'{ind1}if_exists={self.if_exists},' \ f'{name_str}' \ f'\n{ind})' return out_str def get_string(self, *args, **kwargs): - out_str = f'DROP JOB {str(self.name)}' + out_str = f'DROP JOB {"IF EXISTS " if self.if_exists else ""}{str(self.name)}' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/drop_ml_engine.py b/mindsdb_sql/parser/dialects/mindsdb/drop_ml_engine.py index 7075100a..4a640f64 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/drop_ml_engine.py +++ b/mindsdb_sql/parser/dialects/mindsdb/drop_ml_engine.py @@ -5,9 +5,11 @@ class DropMLEngine(Drop): def __init__(self, name, + if_exists=False, *args, **kwargs): super().__init__(*args, **kwargs) self.name = name + self.if_exists = if_exists def to_tree(self, *args, level=0, **kwargs): ind = indent(level) @@ -15,11 +17,12 @@ def to_tree(self, *args, level=0, **kwargs): name_str = f'\n{ind1}name={self.name.to_tree()},' out_str = f'{ind}DropMLEngine(' \ + f'{ind1}if_exists={self.if_exists},' \ f'{name_str}' \ f'\n{ind})' return out_str def get_string(self, *args, **kwargs): - out_str = f'DROP ML_ENGINE {str(self.name)}' + out_str = f'DROP ML_ENGINE {"IF EXISTS " if self.if_exists else ""}{str(self.name)}' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/drop_predictor.py b/mindsdb_sql/parser/dialects/mindsdb/drop_predictor.py index abd30271..3c17549b 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/drop_predictor.py +++ b/mindsdb_sql/parser/dialects/mindsdb/drop_predictor.py @@ -17,8 +17,8 @@ def to_tree(self, *args, level=0, **kwargs): name_str = f'\n{ind1}name={self.name.to_tree()},' out_str = f'{ind}DropPredictor(' \ - f'{name_str}' \ f'if_exists={self.if_exists}' \ + f'{name_str}' \ f'\n{ind})' return out_str diff --git a/mindsdb_sql/parser/dialects/mindsdb/lexer.py b/mindsdb_sql/parser/dialects/mindsdb/lexer.py index 5106b866..2136507e 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/lexer.py +++ b/mindsdb_sql/parser/dialects/mindsdb/lexer.py @@ -39,7 +39,7 @@ class MindsDBLexer(Lexer): GLOBAL, PROCEDURE, FUNCTION, INDEX, WARNINGS, ENGINES, CHARSET, COLLATION, PLUGINS, CHARACTER, PERSIST, PERSIST_ONLY, DEFAULT, - IF_EXISTS, COLUMNS, FIELDS, COLLATE, SEARCH_PATH, + IF_EXISTS, IF_NOT_EXISTS, COLUMNS, FIELDS, COLLATE, SEARCH_PATH, # SELECT Keywords WITH, SELECT, DISTINCT, FROM, WHERE, AS, LIMIT, OFFSET, ASC, DESC, NULLS_FIRST, NULLS_LAST, @@ -158,6 +158,7 @@ class MindsDBLexer(Lexer): PERSIST_ONLY = r'\bPERSIST_ONLY\b' DEFAULT = r'\bDEFAULT\b' IF_EXISTS = r'\bIF[\s]+EXISTS\b' + IF_NOT_EXISTS = r'\bIF[\s]+NOT[\s]+EXISTS\b' COLUMNS = r'\bCOLUMNS\b' FIELDS = r'\bFIELDS\b' EXTENDED = r'\bEXTENDED\b' diff --git a/mindsdb_sql/parser/dialects/mindsdb/parser.py b/mindsdb_sql/parser/dialects/mindsdb/parser.py index 998b3033..70e764af 100644 --- a/mindsdb_sql/parser/dialects/mindsdb/parser.py +++ b/mindsdb_sql/parser/dialects/mindsdb/parser.py @@ -132,7 +132,11 @@ def drop_trigger(self, p): @_('CREATE JOB identifier LPAREN raw_query RPAREN job_schedule', 'CREATE JOB identifier AS LPAREN raw_query RPAREN job_schedule', 'CREATE JOB identifier LPAREN raw_query RPAREN', - 'CREATE JOB identifier AS LPAREN raw_query RPAREN') + 'CREATE JOB identifier AS LPAREN raw_query RPAREN', + 'CREATE JOB IF_NOT_EXISTS identifier LPAREN raw_query RPAREN job_schedule', + 'CREATE JOB IF_NOT_EXISTS identifier AS LPAREN raw_query RPAREN job_schedule', + 'CREATE JOB IF_NOT_EXISTS identifier LPAREN raw_query RPAREN', + 'CREATE JOB IF_NOT_EXISTS identifier AS LPAREN raw_query RPAREN') def create_job(self, p): query_str = tokens_to_string(p.raw_query) @@ -158,7 +162,8 @@ def create_job(self, p): query_str=query_str, start_str=start_str, end_str=end_str, - repeat_str=repeat_str + repeat_str=repeat_str, + if_not_exists=hasattr(p, 'IF_NOT_EXISTS') ) @_('START string', @@ -190,9 +195,10 @@ def job_schedule(self, p): schedule = {param: value} return schedule - @_('DROP JOB identifier') + @_('DROP JOB identifier', + 'DROP JOB IF_EXISTS identifier') def drop_job(self, p): - return DropJob(name=p.identifier) + return DropJob(name=p.identifier, if_exists=hasattr(p, 'IF_EXISTS')) # Explain @@ -578,13 +584,16 @@ def use(self, p): # CREATE VIEW @_('CREATE VIEW identifier create_view_from_table_or_nothing AS LPAREN raw_query RPAREN', - 'CREATE VIEW identifier create_view_from_table_or_nothing LPAREN raw_query RPAREN') + 'CREATE VIEW identifier create_view_from_table_or_nothing LPAREN raw_query RPAREN', + 'CREATE VIEW IF_NOT_EXISTS identifier create_view_from_table_or_nothing AS LPAREN raw_query RPAREN', + 'CREATE VIEW IF_NOT_EXISTS identifier create_view_from_table_or_nothing LPAREN raw_query RPAREN') def create_view(self, p): query_str = tokens_to_string(p.raw_query) return CreateView(name=p.identifier, from_table=p.create_view_from_table_or_nothing, - query_str=query_str) + query_str=query_str, + if_not_exists=hasattr(p, 'IF_NOT_EXISTS')) @_('FROM identifier') def create_view_from_table_or_nothing(self, p): @@ -597,20 +606,23 @@ def create_view_from_table_or_nothing(self, p): # DROP PREDICTOR @_('DROP PREDICTOR identifier', 'DROP MODEL identifier', - 'DROP PREDICTOR IF_EXISTS identifier') + 'DROP PREDICTOR IF_EXISTS identifier', + 'DROP MODEL IF_EXISTS identifier') def drop_predictor(self, p): if_exists = hasattr(p, 'IF_EXISTS') return DropPredictor(p.identifier, if_exists=if_exists) # DROP DATASOURCE - @_('DROP DATASOURCE identifier') + @_('DROP DATASOURCE identifier', + 'DROP DATASOURCE IF_EXISTS identifier') def drop_datasource(self, p): - return DropDatasource(p.identifier) + return DropDatasource(p.identifier, if_exists=hasattr(p, 'IF_EXISTS')) # DROP DATASET - @_('DROP DATASET identifier') + @_('DROP DATASET identifier', + 'DROP DATASET IF_EXISTS identifier') def drop_dataset(self, p): - return DropDataset(p.identifier) + return DropDataset(p.identifier, if_exists=hasattr(p, 'IF_EXISTS')) # DROP TABLE @_('DROP TABLE IF_EXISTS identifier', @@ -622,6 +634,8 @@ def drop_table(self, p): # create table @_('CREATE TABLE identifier select', 'CREATE TABLE identifier LPAREN select RPAREN', + 'CREATE TABLE IF_NOT_EXISTS identifier select', + 'CREATE TABLE IF_NOT_EXISTS identifier LPAREN select RPAREN', 'CREATE OR REPLACE TABLE identifier select', 'CREATE OR REPLACE TABLE identifier LPAREN select RPAREN') def create_table(self, p): @@ -632,7 +646,8 @@ def create_table(self, p): return CreateTable( name=p.identifier, is_replace=is_replace, - from_select=p.select + from_select=p.select, + if_not_exists=hasattr(p, 'IF_NOT_EXISTS') ) @_('CREATE TABLE identifier USING kw_parameter_list') @@ -673,8 +688,12 @@ def create_predictor(self, p): @_('CREATE PREDICTOR identifier FROM identifier LPAREN raw_query RPAREN PREDICT result_columns', 'CREATE PREDICTOR identifier PREDICT result_columns', + 'CREATE PREDICTOR IF_NOT_EXISTS identifier FROM identifier LPAREN raw_query RPAREN PREDICT result_columns', + 'CREATE PREDICTOR IF_NOT_EXISTS identifier PREDICT result_columns', 'CREATE MODEL identifier FROM identifier LPAREN raw_query RPAREN PREDICT result_columns', - 'CREATE MODEL identifier PREDICT result_columns') + 'CREATE MODEL identifier PREDICT result_columns', + 'CREATE MODEL IF_NOT_EXISTS identifier FROM identifier LPAREN raw_query RPAREN PREDICT result_columns', + 'CREATE MODEL IF_NOT_EXISTS identifier PREDICT result_columns') def create_predictor(self, p): query_str = None if hasattr(p, 'raw_query'): @@ -690,7 +709,8 @@ def create_predictor(self, p): name=name, integration_name=getattr(p, 'identifier1', None), query_str=query_str, - targets=p.result_columns + targets=p.result_columns, + if_not_exists=hasattr(p, 'IF_NOT_EXISTS') ) # RETRAIN PREDICTOR @@ -760,16 +780,20 @@ def evaluate(self, p): # ML ENGINE # CREATE @_('CREATE ML_ENGINE identifier FROM id USING kw_parameter_list', - 'CREATE ML_ENGINE identifier FROM id') + 'CREATE ML_ENGINE identifier FROM id', + 'CREATE ML_ENGINE IF_NOT_EXISTS identifier FROM id USING kw_parameter_list', + 'CREATE ML_ENGINE IF_NOT_EXISTS identifier FROM id') def create_integration(self, p): return CreateMLEngine(name=p.identifier, handler=p.id, - params=getattr(p, 'kw_parameter_list', None)) + params=getattr(p, 'kw_parameter_list', None), + if_not_exists=hasattr(p, 'IF_NOT_EXISTS')) # DROP - @_('DROP ML_ENGINE identifier') + @_('DROP ML_ENGINE identifier', + 'DROP ML_ENGINE IF_EXISTS identifier') def create_integration(self, p): - return DropMLEngine(name=p.identifier) + return DropMLEngine(name=p.identifier, if_exists=hasattr(p, 'IF_EXISTS')) # CREATE INTEGRATION @_('CREATE database_engine', @@ -793,19 +817,26 @@ def create_integration(self, p): return CreateDatabase(name=p.database_engine['identifier'], engine=p.database_engine['engine'], is_replace=is_replace, - parameters=parameters) + parameters=parameters, + if_not_exists=p.database_engine['if_not_exists']) @_('DATABASE identifier', 'PROJECT identifier', 'DATABASE identifier ENGINE string', 'DATABASE identifier ENGINE EQUALS string', 'DATABASE identifier WITH ENGINE string', - 'DATABASE identifier WITH ENGINE EQUALS string') + 'DATABASE identifier WITH ENGINE EQUALS string', + 'DATABASE IF_NOT_EXISTS identifier', + 'DATABASE IF_NOT_EXISTS identifier ENGINE string', + 'DATABASE IF_NOT_EXISTS identifier ENGINE EQUALS string', + 'DATABASE IF_NOT_EXISTS identifier WITH ENGINE string', + 'DATABASE IF_NOT_EXISTS identifier WITH ENGINE EQUALS string', + 'PROJECT IF_NOT_EXISTS identifier') def database_engine(self, p): engine = None if hasattr(p, 'string'): engine = p.string - return {'identifier': p.identifier, 'engine': engine} + return {'identifier': p.identifier, 'engine': engine, 'if_not_exists': hasattr(p, 'IF_NOT_EXISTS')} # UNION / UNION ALL @_('select UNION select') diff --git a/tests/test_parser/test_mindsdb/test_create_integration.py b/tests/test_parser/test_mindsdb/test_create_integration.py index 9595c19d..d5c7d0ff 100644 --- a/tests/test_parser/test_mindsdb/test_create_integration.py +++ b/tests/test_parser/test_mindsdb/test_create_integration.py @@ -7,18 +7,19 @@ class TestCreateDatabase: def test_create_database_lexer(self): - sql = "CREATE DATABASE db WITH ENGINE = 'mysql', PARAMETERS = {\"user\": \"admin\", \"password\": \"admin\"}" + sql = "CREATE DATABASE IF NOT EXISTS db WITH ENGINE = 'mysql', PARAMETERS = {\"user\": \"admin\", \"password\": \"admin\"}" tokens = list(MindsDBLexer().tokenize(sql)) assert tokens[0].type == 'CREATE' assert tokens[1].type == 'DATABASE' - assert tokens[2].type == 'ID' - assert tokens[3].type == 'WITH' - assert tokens[4].type == 'ENGINE' - assert tokens[5].type == 'EQUALS' - assert tokens[6].type == 'QUOTE_STRING' - assert tokens[7].type == 'COMMA' - assert tokens[8].type == 'PARAMETERS' - assert tokens[9].type == 'EQUALS' + assert tokens[2].type == 'IF_NOT_EXISTS' + assert tokens[3].type == 'ID' + assert tokens[4].type == 'WITH' + assert tokens[5].type == 'ENGINE' + assert tokens[6].type == 'EQUALS' + assert tokens[7].type == 'QUOTE_STRING' + assert tokens[8].type == 'COMMA' + assert tokens[9].type == 'PARAMETERS' + assert tokens[10].type == 'EQUALS' # next tokens come separately, not just single JSON # assert tokens[10].type == 'JSON' @@ -76,6 +77,18 @@ def test_create_database_ok(self, ): assert str(ast) == str(expected_ast) assert ast.to_tree() == expected_ast.to_tree() + # test with if not exists + sql = """ + CREATE DATABASE IF NOT EXISTS db + WITH ENGINE='mysql' + """ + ast = parse_sql(sql, dialect='mindsdb') + expected_ast = CreateDatabase(name=Identifier('db'), + engine='mysql', + if_not_exists=True, + parameters=None) + assert str(ast) == str(expected_ast) + def test_create_database_invalid_json(self): sql = "CREATE DATABASE db WITH ENGINE = 'mysql', PARAMETERS = 'wow'" with pytest.raises(ParsingException): @@ -91,3 +104,14 @@ def test_create_project(self): assert str(ast).lower() == str(expected_ast).lower() assert ast.to_tree() == expected_ast.to_tree() + # test with if not exists + sql = """ + CREATE PROJECT IF NOT EXISTS db + """ + ast = parse_sql(sql, dialect='mindsdb') + expected_ast = CreateDatabase(name=Identifier('db'), + engine=None, + if_not_exists=True, + parameters=None) + + diff --git a/tests/test_parser/test_mindsdb/test_create_predictor.py b/tests/test_parser/test_mindsdb/test_create_predictor.py index fe83d335..8737e03d 100644 --- a/tests/test_parser/test_mindsdb/test_create_predictor.py +++ b/tests/test_parser/test_mindsdb/test_create_predictor.py @@ -60,7 +60,7 @@ def test_create_predictor_full(self): assert ast.to_tree() == ast2.to_tree() def test_create_predictor_minimal(self): - sql = """CREATE PREDICTOR pred + sql = """CREATE PREDICTOR IF NOT EXISTS pred FROM integration_name (select * FROM table_name) PREDICT f1 as f1_alias, f2 @@ -68,6 +68,7 @@ def test_create_predictor_minimal(self): ast = parse_sql(sql, dialect='mindsdb') expected_ast = CreatePredictor( name=Identifier('pred'), + if_not_exists=True, integration_name=Identifier('integration_name'), query_str="select * FROM table_name", targets=[Identifier('f1', alias=Identifier('f1_alias')), diff --git a/tests/test_parser/test_mindsdb/test_create_view.py b/tests/test_parser/test_mindsdb/test_create_view.py index e9031256..f73e4b99 100644 --- a/tests/test_parser/test_mindsdb/test_create_view.py +++ b/tests/test_parser/test_mindsdb/test_create_view.py @@ -22,9 +22,10 @@ def test_create_view_raises_wrong_dialect(self): ast = parse_sql(sql, dialect=dialect) def test_create_view_full(self): - sql = "CREATE VIEW my_view FROM integr AS ( SELECT * FROM pred )" + sql = "CREATE VIEW IF NOT EXISTS my_view FROM integr AS ( SELECT * FROM pred )" ast = parse_sql(sql, dialect='mindsdb') expected_ast = CreateView(name=Identifier('my_view'), + if_not_exists=True, from_table=Identifier('integr'), query_str="SELECT * FROM pred") diff --git a/tests/test_parser/test_mindsdb/test_drop_dataset.py b/tests/test_parser/test_mindsdb/test_drop_dataset.py index d4409671..f55f1e15 100644 --- a/tests/test_parser/test_mindsdb/test_drop_dataset.py +++ b/tests/test_parser/test_mindsdb/test_drop_dataset.py @@ -7,9 +7,9 @@ class TestDropDataset: def test_drop_dataset(self): - sql = "DROP DATASET dsname" + sql = "DROP DATASET IF EXISTS dsname" ast = parse_sql(sql, dialect='mindsdb') - expected_ast = DropDataset(name=Identifier('dsname')) + expected_ast = DropDataset(name=Identifier('dsname'), if_exists=True) assert str(ast).lower() == sql.lower() assert str(ast) == str(expected_ast) assert ast.to_tree() == expected_ast.to_tree() diff --git a/tests/test_parser/test_mindsdb/test_drop_datasource.py b/tests/test_parser/test_mindsdb/test_drop_datasource.py index 408d1fa2..33ff5e17 100644 --- a/tests/test_parser/test_mindsdb/test_drop_datasource.py +++ b/tests/test_parser/test_mindsdb/test_drop_datasource.py @@ -7,9 +7,9 @@ class TestDropDatasource: def test_drop_datasource(self): - sql = "DROP DATASOURCE dsname" + sql = "DROP DATASOURCE IF EXISTS dsname" ast = parse_sql(sql, dialect='mindsdb') - expected_ast = DropDatasource(name=Identifier('dsname')) + expected_ast = DropDatasource(name=Identifier('dsname'), if_exists=True) assert str(ast).lower() == sql.lower() assert str(ast) == str(expected_ast) assert ast.to_tree() == expected_ast.to_tree() diff --git a/tests/test_parser/test_mindsdb/test_jobs.py b/tests/test_parser/test_mindsdb/test_jobs.py index ad2a5cec..47f8ddc7 100644 --- a/tests/test_parser/test_mindsdb/test_jobs.py +++ b/tests/test_parser/test_mindsdb/test_jobs.py @@ -56,6 +56,21 @@ def test_create_job(self): assert str(ast) == str(expected_ast) assert ast.to_tree() == expected_ast.to_tree() + def test_create_job_minimal_with_if_not_exists(self): + sql = ''' + create job if not exists proj2.j1 ( + select * from pg.tbl1 where b>{{PREVIOUS_START_DATE}} + ) + ''' + ast = parse_sql(sql, dialect='mindsdb') + expected_ast = CreateJob( + name=Identifier('proj2.j1'), + query_str="select * from pg.tbl1 where b>{{PREVIOUS_START_DATE}}", + if_not_exists=True + ) + assert str(ast) == str(expected_ast) + assert ast.to_tree() == expected_ast.to_tree() + def test_drop_job(self): sql = ''' drop job proj1.j1 @@ -65,4 +80,16 @@ def test_drop_job(self): name=Identifier('proj1.j1'), ) assert str(ast) == str(expected_ast) + assert ast.to_tree() == expected_ast.to_tree() + + # test with if exists + sql = ''' + drop job if exists proj1.j1 + ''' + ast = parse_sql(sql, dialect='mindsdb') + expected_ast = DropJob( + name=Identifier('proj1.j1'), + if_exists=True + ) + assert str(ast) == str(expected_ast) assert ast.to_tree() == expected_ast.to_tree() \ No newline at end of file diff --git a/tests/test_parser/test_mindsdb/test_ml_engine.py b/tests/test_parser/test_mindsdb/test_ml_engine.py index abb9e263..0d12dc5d 100644 --- a/tests/test_parser/test_mindsdb/test_ml_engine.py +++ b/tests/test_parser/test_mindsdb/test_ml_engine.py @@ -33,6 +33,20 @@ def test_create_predictor_full(self): assert to_single_line(str(ast)) == to_single_line(str(expected_ast)) assert ast.to_tree() == expected_ast.to_tree() + # test if not exists + sql = """ + CREATE ML_ENGINE IF NOT EXISTS name FROM ml_handler_name + """ + ast = parse_sql(sql, dialect='mindsdb') + expected_ast = CreateMLEngine( + name=Identifier('name'), + handler='ml_handler_name', + params=None, + if_not_exists=True + ) + assert to_single_line(str(ast)) == to_single_line(str(expected_ast)) + assert ast.to_tree() == expected_ast.to_tree() + class TestDropMLEngine: def test_create_predictor_full(self): @@ -45,3 +59,15 @@ def test_create_predictor_full(self): ) assert to_single_line(str(ast)) == to_single_line(str(expected_ast)) assert ast.to_tree() == expected_ast.to_tree() + + # test if exists + sql = """ + DROP ML_ENGINE IF EXISTS name + """ + ast = parse_sql(sql, dialect='mindsdb') + expected_ast = DropMLEngine( + name=Identifier('name'), + if_exists=True + ) + assert to_single_line(str(ast)) == to_single_line(str(expected_ast)) + assert ast.to_tree() == expected_ast.to_tree()