mrs_plugin/lib/MrsDdlListener.py (1,565 lines of code) (raw):

# Copyright (c) 2023, 2025, Oracle and/or its affiliates. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2.0, # as published by the Free Software Foundation. # # This program is designed to work with certain software (including # but not limited to OpenSSL) that is licensed under separate terms, as # designated in a particular file or component or in included license # documentation. The authors of MySQL hereby grant you an additional # permission to link the program and your derivative works with the # separately licensed software that they have either included with # the program or referenced in the documentation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See # the GNU General Public License, version 2.0, for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import mrs_plugin.lib as lib import antlr4 from mrs_plugin.lib.mrs_parser import MRSListener from mrs_plugin.lib.mrs_parser import MRSParser from mrs_plugin.lib.MrsDdlExecutorInterface import MrsDdlExecutorInterface import re import json def get_text_without_quotes(txt): if txt is None: return None if len(txt) < 2: return txt if txt[0] == "`" and txt[len(txt) - 1] == "`": return txt[1:-1].replace("\\`", "`").replace("\\\\", "\\") if txt[0] == "'" and txt[len(txt) - 1] == "'": return txt[1:-1].replace("\\'", "'").replace("\\\\", "\\") if txt[0] == '"' and txt[len(txt) - 1] == '"': return txt[1:-1].replace('\\"', '"').replace("\\\\", "\\") return txt def unquoted_node_text_or_none(node): if node: return get_text_without_quotes(node.getText()) return None def validate_rest_request_path(text, allow_wildcards=False): if not text or text[0] == "/" or (allow_wildcards and text[0] in "*?"): return raise Exception("Invalid REST request path or wildcard") class MrsDdlListener(MRSListener): def __init__(self, mrs_ddl_executor: MrsDdlExecutorInterface, session): self.mrs_ddl_executor = mrs_ddl_executor self.session = session self.mrs_object = {} def get_uuid(self): return lib.core.convert_id_to_string(lib.core.get_sequence_id(self.session)) # ------------------------------------------------------------------------------------------------------------------ # Common handlers def enterJsonOptions(self, ctx): try: if ctx.MERGE_SYMBOL() is not None: self.mrs_object["merge_options"] = True self.mrs_object["options"] = json.loads(ctx.jsonValue().getText()) except: pass def enterAppOptions(self, ctx): try: self.mrs_object["app_options"] = json.loads(ctx.jsonValue().getText()) except: pass def enterMetadata(self, ctx): try: self.mrs_object["metadata"] = json.loads(ctx.jsonValue().getText()) except: pass def enterComments(self, ctx): self.mrs_object["comments"] = get_text_without_quotes( ctx.textStringLiteral().getText() ) def enterEnabledDisabled(self, ctx): if ctx.ENABLED_SYMBOL() is not None: self.mrs_object["enabled"] = True if ctx.DISABLED_SYMBOL() is not None: self.mrs_object["enabled"] = False def enterPublishedUnpublished(self, ctx): if ctx.PUBLISHED_SYMBOL() is not None: self.mrs_object["published"] = True if ctx.UNPUBLISHED_SYMBOL() is not None: self.mrs_object["published"] = False def enterEnabledDisabledPrivate(self, ctx): if ctx.ENABLED_SYMBOL() is not None: self.mrs_object["enabled"] = True elif ctx.DISABLED_SYMBOL() is not None: self.mrs_object["enabled"] = False elif ctx.PRIVATE_SYMBOL() is not None: self.mrs_object["enabled"] = 2 def enterAuthenticationRequired(self, ctx): # If the NOT keyword is present in (AUTHENTICATION NOT? REQUIRED)? # authentication is not required if ctx.NOT_SYMBOL() is not None: self.mrs_object["requires_auth"] = False else: self.mrs_object["requires_auth"] = True def enterItemsPerPage(self, ctx): self.mrs_object["items_per_page"] = int(ctx.itemsPerPageNumber().getText()) def enterServiceDeveloperIdentifier(self, ctx): # If the new_developer_list list has been initialized, all following developers are part of the # new developer list to be set by the ALTER REST SERVICE statement if "new_developer_list" in self.mrs_object.keys(): self.mrs_object["new_developer_list"].append( get_text_without_quotes(ctx.getText()) ) else: if not "in_development" in self.mrs_object.keys(): self.mrs_object["in_development"] = {"developers": []} self.mrs_object["in_development"]["developers"].append( get_text_without_quotes(ctx.getText()) ) def exitRequestPathIdentifier(self, ctx): text = ctx.getText() if text and text[0] in '`"': text = get_text_without_quotes(text) validate_rest_request_path(text, False) return text def exitRequestPathIdentifierWithWildcard(self, ctx): text = ctx.getText() if text and text[0] in '`"': text = get_text_without_quotes(text) validate_rest_request_path(text, True) return text def enterServiceRequestPath(self, ctx): self.mrs_object["url_context_root"] = get_text_without_quotes( ctx.requestPathIdentifier().getText() ) def enterServiceRequestPathWildcard(self, ctx): self.mrs_object["url_context_root"] = get_text_without_quotes( ctx.requestPathIdentifierWithWildcard().getText() ) def enterServiceSchemaSelector(self, ctx): self.mrs_object["schema_request_path"] = get_text_without_quotes( ctx.schemaRequestPath().getText() ) def enterServiceSchemaSelectorWildcard(self, ctx): self.mrs_object["schema_request_path"] = get_text_without_quotes( ctx.schemaRequestPathWildcard().getText() ) def enterFileIgnoreList(self, ctx): self.mrs_object["file_ignore_list"] = get_text_without_quotes( ctx.textStringLiteral().getText() ) def enterAllowNewUsersToRegister(self, ctx): self.mrs_object["limit_to_registered_users"] = not ctx.NOT_SYMBOL() is None def enterDefaultRole(self, ctx): self.mrs_object["default_role"] = get_text_without_quotes( ctx.textOrIdentifier().getText() ) def enterAppId(self, ctx): self.mrs_object["app_id"] = get_text_without_quotes( ctx.textStringLiteral().getText() ) def enterAppSecret(self, ctx): self.mrs_object["app_secret"] = get_text_without_quotes( ctx.textStringLiteral().getText() ) def enterUrl(self, ctx): self.mrs_object["url"] = get_text_without_quotes( ctx.textStringLiteral().getText() ) def enterAddAuthApp(self, ctx): add_auth_apps = self.mrs_object.get("add_auth_apps", []) add_auth_apps.append(get_text_without_quotes(ctx.authAppName().getText())) self.mrs_object["add_auth_apps"] = add_auth_apps def enterRemoveAuthApp(self, ctx): add_auth_apps = self.mrs_object.get("remove_auth_apps", []) add_auth_apps.append(get_text_without_quotes(ctx.authAppName().getText())) self.mrs_object["remove_auth_apps"] = add_auth_apps # ================================================================================================================== # CREATE REST statements # ------------------------------------------------------------------------------------------------------------------ # CREATE REST METADATA def enterConfigureRestMetadataStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "CONFIGURE REST METADATA", "update_if_available": ( True if ( ctx.restMetadataOptions() is not None and ctx.restMetadataOptions().updateIfAvailable() is not None ) else False ), } def exitConfigureRestMetadataStatement(self, ctx): self.mrs_ddl_executor.createRestMetadata(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST SERVICE def enterCreateRestServiceStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " REST SERVICE", "do_replace": ctx.REPLACE_SYMBOL() is not None, } def enterRestProtocol(self, ctx): if ctx.HTTP_SYMBOL() is not None and ctx.HTTPS_SYMBOL() is not None: self.mrs_object["url_protocol"] = "HTTP,HTTPS" elif ctx.HTTP_SYMBOL() is not None: self.mrs_object["url_protocol"] = "HTTP" elif ctx.HTTPS_SYMBOL() is not None: self.mrs_object["url_protocol"] = "HTTPS" def enterAuthPath(self, ctx): val = ctx.quotedTextOrDefault().getText() if val != "DEFAULT": self.mrs_object["auth_path"] = get_text_without_quotes(val) def enterAuthRedirection(self, ctx): val = ctx.quotedTextOrDefault().getText() if val != "DEFAULT": self.mrs_object["auth_completed_url"] = get_text_without_quotes(val) def enterAuthValidation(self, ctx): val = ctx.quotedTextOrDefault().getText() if val != "DEFAULT": self.mrs_object["auth_completed_url_validation"] = get_text_without_quotes( val ) def enterAuthPageContent(self, ctx): val = ctx.quotedTextOrDefault().getText() if val != "DEFAULT": self.mrs_object["auth_completed_page_content"] = get_text_without_quotes( val ) def enterUserManagementSchema(self, ctx): val = ctx.schemaName() if val is not None: self.mrs_object["custom_metadata_schema"] = val.strip("`") def exitCreateRestServiceStatement(self, ctx): self.mrs_ddl_executor.createRestService(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST SCHEMA def enterCreateRestSchemaStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " REST SCHEMA", "do_replace": ctx.REPLACE_SYMBOL() is not None, "schema_name": get_text_without_quotes(ctx.schemaName().getText()), "schema_request_path": ( get_text_without_quotes(ctx.schemaRequestPath().getText()) if ctx.schemaRequestPath() is not None else f"/{lib.core.unquote(ctx.schemaName().getText())}" ), } def exitCreateRestSchemaStatement(self, ctx): self.mrs_ddl_executor.createRestSchema(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST VIEW def get_db_object_fields( self, object_id, db_schema_name, db_object_name, auto_enable_fields=False ): # Get the actual columns with references columns = lib.db_objects.get_table_columns_with_references( session=self.session, schema_name=db_schema_name, db_object_name=db_object_name, ) parent_reference_stack = self.mrs_object.get("parent_reference_stack") if len(parent_reference_stack) > 0: parent_reference_id = ( parent_reference_stack[-1].get("object_reference").get("id") ) else: parent_reference_id = None # Convert to object_fields and disable all to begin with fields = [] for column in columns: db_column = column.get("db_column") ref_mapping = column.get("reference_mapping") fields.append( { "id": self.get_uuid(), "object_id": object_id, "parent_reference_id": parent_reference_id, "name": lib.core.convert_snake_to_camel_case(column.get("name")), "position": column.get("position"), "db_column": column.get("db_column"), "enabled": (auto_enable_fields and ref_mapping is None), "allow_filtering": True, # Only allow sorting for top level fields "allow_sorting": ( len(parent_reference_stack) == 0 and db_column is not None and ( db_column.get("isPrimary") is not None or db_column.get("isUnique") is not None ) ), "no_check": False, "no_update": False, "sdk_options": None, "comments": None, "reference_mapping": ref_mapping, } ) return fields def get_service_sorted_developers(self, developer_list: list): sorted_developers = "" if developer_list is not None and len(developer_list) > 0: def quote(s): return f"'{s}'" developer_list.sort() sorted_developers = ( ",".join( ( quote(re.sub(r"(['\\])", "\\\\\\1", dev, 0, re.MULTILINE)) if not re.match(r"^\w+$", dev) else dev ) for dev in developer_list ) + "@" ) return sorted_developers def get_db_object(self, ctx): developer_list = None if ctx.serviceSchemaSelector() is not None: schema_request_path = get_text_without_quotes( ctx.serviceSchemaSelector().schemaRequestPath().getText() ) url_context_root = None url_host_name = "" serviceRequestPath = ctx.serviceSchemaSelector().serviceRequestPath() if serviceRequestPath is not None: url_context_root = get_text_without_quotes( ctx.serviceSchemaSelector() .serviceRequestPath() .requestPathIdentifier() .getText() ) if serviceRequestPath.serviceDevelopersIdentifier() is not None: developer_list = [] developersIdentifier = ( serviceRequestPath.serviceDevelopersIdentifier() ) for item in list(developersIdentifier.getChildren()): if isinstance( item, MRSParser.ServiceDeveloperIdentifierContext ): if item.textOrIdentifier() is not None: developer_list.append( get_text_without_quotes(item.getText()) ) else: developer_list.append(item.getText()) if len(developer_list) == 0: developer_list = None else: schema_request_path = self.mrs_object.get("schema_request_path") url_context_root = self.mrs_object.get("url_context_root") url_host_name = self.mrs_object.get("url_host_name") if ( "in_development" in self.mrs_object.keys() and "developers" in self.mrs_object.get("in_development") ): developer_list = self.mrs_object["in_development"]["developers"] if schema_request_path is None: schema_id = self.mrs_ddl_executor.current_schema_id if schema_id is None: raise Exception("No REST schema given.") else: if url_context_root is None: raise Exception("No REST service given.") service = lib.services.get_service( session=self.session, url_context_root=url_context_root, url_host_name=url_host_name, developer_list=developer_list, ) if service is None: raise Exception( f"The REST service `{ self.get_service_sorted_developers(developer_list)}" + f"{url_host_name}{url_context_root}` was not found." ) schema = lib.schemas.get_schema( session=self.session, service_id=service["id"], request_path=schema_request_path, ) if schema is None: raise Exception( f"""The REST schema `{ url_host_name if url_host_name is not None else ''}{ url_context_root if url_context_root is not None else ''}{ schema_request_path if schema_request_path is not None else '' }` was not found.""" ) schema_id = schema["id"] db_object = lib.db_objects.get_db_object( session=self.session, schema_id=schema_id, request_path=self.mrs_object.get("request_path"), ) if db_object is None: raise Exception( f"""REST object `{url_host_name}{url_context_root}{ schema_request_path}{ self.mrs_object.get("request_path")}` was not found.""" ) return db_object def set_schema_name_and_name(self, ctx): # If no schema name nor name was given, look it up from existing db_object if ctx.qualifiedIdentifier() is None: db_object = self.get_db_object(ctx=ctx) self.mrs_object["name"] = db_object.get("name") self.mrs_object["schema_name"] = db_object.get("schema_name") # If no db schema name was given, get the schema name from the current REST schema elif ctx.qualifiedIdentifier().dotIdentifier() is None: self.mrs_object["name"] = ctx.qualifiedIdentifier().getText() if self.mrs_ddl_executor.current_schema_id is None: raise Exception( f'The database schema for `{ self.mrs_object["name"]}` was not given.' ) schema = lib.schemas.get_schema( session=self.session, schema_id=self.mrs_ddl_executor.current_schema_id ) if schema is not None: self.mrs_object["schema_name"] = schema.get("name") else: raise Exception( f'The database schema was not found for `{ self.mrs_object["name"]}`' ) else: self.mrs_object["name"] = ( ctx.qualifiedIdentifier() .dotIdentifier() .identifier() .getText() .strip("`") ) self.mrs_object["schema_name"] = ( ctx.qualifiedIdentifier().identifier().getText().strip("`") ) def enterCreateRestViewStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": f"""CREATE{ '' if ctx.REPLACE_SYMBOL() is None else ' OR REPLACE' } REST VIEW""", "do_replace": ctx.REPLACE_SYMBOL() is not None, "id": self.get_uuid(), "request_path": get_text_without_quotes( ctx.viewRequestPath().requestPathIdentifier().getText() ), "parent_reference_stack": [], } self.set_schema_name_and_name(ctx=ctx) self.mrs_object["db_object_type"] = ( "TABLE" if ( lib.db_objects.db_schema_object_is_table( session=self.session, db_schema_name=self.mrs_object["schema_name"], db_object_name=self.mrs_object["name"], ) ) else "VIEW" ) object_id = self.get_uuid() options = self.build_options_list(ctx.graphQlCrudOptions()) self.mrs_object["objects"] = [ { "id": object_id, "db_object_id": self.mrs_object.get("id"), "name": get_text_without_quotes( ctx.restObjectName().getText() if ctx.restObjectName() is not None else None ), "kind": "RESULT", "position": 0, "options": { "dataMappingViewInsert": "INSERT" in options, "dataMappingViewUpdate": "UPDATE" in options, "dataMappingViewDelete": "DELETE" in options, "dataMappingViewNoCheck": "NOCHECK" in options, }, # Get top level fields "fields": self.get_db_object_fields( object_id=object_id, db_schema_name=self.mrs_object["schema_name"], db_object_name=self.mrs_object["name"], # If no graphQlObj is given, simply enabled all fields auto_enable_fields=(ctx.graphQlObj() is None), ), } ] def enterRestViewMediaType(self, ctx): if ctx.textStringLiteral() is not None: self.mrs_object["media_type"] = get_text_without_quotes( ctx.textStringLiteral().getText() ) elif ctx.AUTODETECT_SYMBOL() is not None: self.mrs_object["media_type_autodetect"] = True def enterRestViewFormat(self, ctx): if ctx.FEED_SYMBOL() is not None: self.mrs_object["format"] = "FEED" elif ctx.ITEM_SYMBOL() is not None: self.mrs_object["format"] = "ITEM" elif ctx.MEDIA_SYMBOL() is not None: self.mrs_object["format"] = "MEDIA" def enterRestViewAuthenticationProcedure(self, ctx): self.mrs_object["auth_stored_procedure"] = ctx.qualifiedIdentifier().getText() def build_options_list(self, ctx: MRSParser.GraphQlCrudOptionsContext): options_list = [] if ctx is None: return options_list # cSpell:ignore NOSELECT NOINSERT NOUPDATE NODELETE if (ctx.AT_INSERT_SYMBOL(0) is not None) and "INSERT" not in options_list: options_list.append("INSERT") if (ctx.AT_NOINSERT_SYMBOL(0) is not None) and "INSERT" in options_list: options_list.remove("INSERT") if (ctx.AT_UPDATE_SYMBOL(0) is not None) and "UPDATE" not in options_list: options_list.append("UPDATE") if (ctx.AT_NOUPDATE_SYMBOL(0) is not None) and "UPDATE" in options_list: options_list.remove("UPDATE") if (ctx.AT_DELETE_SYMBOL(0) is not None) and "DELETE" not in options_list: options_list.append("DELETE") if (ctx.AT_NODELETE_SYMBOL(0) is not None) and "DELETE" in options_list: options_list.remove("DELETE") if (ctx.AT_NOCHECK_SYMBOL(0) is not None) and "NOCHECK" not in options_list: options_list.append("NOCHECK") if (ctx.AT_CHECK_SYMBOL(0) is not None) and "NOCHECK" in options_list: options_list.remove("NOCHECK") return options_list def enterRestProcedureResult(self, ctx): # A REST PROCEDURE can have multiple results graph_ql_object_count = self.mrs_object.get("graph_ql_object_count", 0) + 1 self.mrs_object["graph_ql_object_count"] = graph_ql_object_count # Add a new mrs object for each RESULT self.mrs_object["objects"].append( { "id": self.get_uuid(), "db_object_id": self.mrs_object.get("id"), "name": ( ctx.restResultName().getText() if ctx.restResultName() is not None else None ), "kind": "RESULT", "position": graph_ql_object_count, "fields": [], } ) def enterRestFunctionResult(self, ctx): # A REST FUNCTION can have parameters and one result defined graph_ql_object_count = self.mrs_object.get("graph_ql_object_count", 0) + 1 self.mrs_object["graph_ql_object_count"] = graph_ql_object_count self.mrs_object["objects"][1]["name"] = ( ctx.restResultName().getText() if ctx.restResultName() is not None else None ) def enterGraphQlPair(self, ctx): objects = self.mrs_object["objects"] current_object = ( objects[-1] if (self.mrs_object.get("db_object_type", "") != "FUNCTION") else objects[self.mrs_object.get("graph_ql_object_count", 0)] ) fields = current_object["fields"] field_name = lib.core.unquote(ctx.graphQlPairKey().getText()) force_create = self.mrs_object.get("force_create", False) # Check if this GraphQlPair is inside a reference, and if so, adjust the ref_fields_offset so that # the handling only applies to the referenced fields ref_stack = self.mrs_object.get("parent_reference_stack") if ref_stack is not None and len(ref_stack) > 0: parent_ref = ref_stack[-1] ref_fields_offset = parent_ref.get("referenced_fields_offset") else: parent_ref = None ref_fields_offset = 0 # If there is no graphQlObj for this field, it's a column if ctx.graphQlObj() is None: db_column_name = ctx.graphQlPairValue().getText().strip("`") # Check if this is a REST PROCEDURE RESULT graph_ql_object_count = self.mrs_object.get("graph_ql_object_count", 0) if graph_ql_object_count == 0: # A REST VIEW RESULT or REST PROCEDURE/FUNCTION PARAMETERS for i, field in enumerate(fields): # Ignore all higher level fields and only consider referenced fields if i < ref_fields_offset: continue db_column = field.get("db_column") if ( db_column is not None and db_column.get("name") == db_column_name ): field["name"] = field_name field["enabled"] = True options_ctx: MRSParser.GraphQlValueOptionsContext = ( ctx.graphQlValueOptions() ) if options_ctx is not None: # cSpell:ignore NOCHECK NOFILTERING ROWOWNERSHIP if options_ctx.AT_NOCHECK_SYMBOL(0) is not None: field["no_check"] = True if options_ctx.AT_SORTABLE_SYMBOL(0) is not None: field["allow_sorting"] = True if options_ctx.AT_NOFILTERING_SYMBOL(0) is not None: field["allow_filtering"] = False if options_ctx.AT_ROWOWNERSHIP_SYMBOL(0) is not None: current_object["row_ownership_field_id"] = field.get( "id", None ) if options_ctx.AT_KEY_SYMBOL(0) is not None: db_column["is_primary"] = True if ( ctx.graphQlCrudOptions() is not None and ctx.graphQlCrudOptions().AT_NOUPDATE_SYMBOL() is not None ): field["no_update"] = True if ctx.AT_DATATYPE_SYMBOL() is not None: db_column["datatype"] = lib.core.unquote( ctx.graphQlDatatypeValue().getText().lower() ) if ctx.graphQlValueJsonSchema() is not None: try: field["json_schema"] = json.loads( ctx.graphQlValueJsonSchema().jsonValue().getText() ) except: pass break else: if not force_create: raise Exception( f"The column `{db_column_name}` does not exist on " f'`{self.mrs_object.get("schema_name")}`.`{ self.mrs_object.get("name")}`.' ) else: # Add parameters in case FORCE has been used fields.append( { "id": self.get_uuid(), "object_id": self.mrs_object.get("objects")[0].get( "id" ), "name": field_name, "position": len(fields), "db_column": { "name": db_column_name, "datatype": ( lib.core.unquote( ctx.graphQlDatatypeValue().getText().lower() ) if ctx.AT_DATATYPE_SYMBOL() else "varchar(255)" ), "in": ctx.AT_IN_SYMBOL() is not None or ctx.AT_INOUT_SYMBOL() is not None, "out": ctx.AT_OUT_SYMBOL() is not None or ctx.AT_INOUT_SYMBOL() is not None, }, "enabled": True, "allow_filtering": True, "allow_sorting": False, "no_check": False, "no_update": False, } ) else: # A REST PROCEDURE RESULT if self.mrs_object.get("db_object_type", "") != "FUNCTION": fields.append( { "id": self.get_uuid(), "object_id": self.mrs_object.get("objects")[ graph_ql_object_count ].get("id"), "name": field_name, "position": len(fields), "db_column": { "name": db_column_name, "datatype": ( lib.core.unquote( ctx.graphQlDatatypeValue().getText().lower() ) if ctx.AT_DATATYPE_SYMBOL() else "varchar(255)" ), }, "enabled": True, "allow_filtering": True, "allow_sorting": False, "no_check": False, "no_update": False, } ) current_object["fields"] = fields else: if ( ctx.graphQlPairValue().qualifiedIdentifier() is None or ctx.graphQlPairValue().qualifiedIdentifier().dotIdentifier() is None ): db_schema_name = self.mrs_object["schema_name"] db_object_name = ctx.graphQlPairValue().getText().strip("`") else: db_schema_name = ( ctx.graphQlPairValue() .qualifiedIdentifier() .identifier() .getText() .strip("`") ) db_object_name = ( ctx.graphQlPairValue() .qualifiedIdentifier() .dotIdentifier() .identifier() .getText() .strip("`") ) ref_mapping = None for field in fields: ref_mapping = field.get("reference_mapping") if ( ref_mapping is not None and ref_mapping.get("referenced_schema") == db_schema_name and ref_mapping.get("referenced_table") == db_object_name and field["enabled"] == False ): field["name"] = field_name field["enabled"] = True # Build object_reference obj_reference_id = self.get_uuid() options = self.build_options_list(ctx.graphQlCrudOptions()) obj_reference = { "id": obj_reference_id, "reference_mapping": ref_mapping, "options": { "dataMappingViewInsert": "INSERT" in options, "dataMappingViewUpdate": "UPDATE" in options, "dataMappingViewDelete": "DELETE" in options, "dataMappingViewNoCheck": "NOCHECK" in options, }, "unnest": self.isUnnestSet(ctx.graphQlValueOptions()), } field["object_reference"] = obj_reference field["represents_reference_id"] = obj_reference_id self.mrs_object.get("parent_reference_stack").append( { "object_reference": obj_reference, "referenced_fields_offset": len(fields), } ) # Get referenced fields as well ref_fields = self.get_db_object_fields( object_id=current_object.get("id"), db_schema_name=db_schema_name, db_object_name=db_object_name, ) # Append them to the fields list current_object["fields"] = fields + ref_fields break else: raise Exception( f"The table `{db_schema_name}`.`{db_object_name}` has no reference to " f'`{self.mrs_object.get("schema_name")}`.`{self.mrs_object.get("name")}`.' ) def isUnnestSet(self, options_ctx: MRSParser.GraphQlValueOptionsContext): unnest = False if options_ctx is not None: unnest = options_ctx.AT_UNNEST_SYMBOL(0) is not None return unnest def exitGraphQlPair(self, ctx): if ( ctx.graphQlPairValue().qualifiedIdentifier() is not None and ctx.graphQlPairValue().qualifiedIdentifier().dotIdentifier() is not None ): # Remove last reference_id ref_stack = self.mrs_object.get("parent_reference_stack") if len(ref_stack) > 0: parent_ref = ref_stack.pop() else: parent_ref = None if ( parent_ref is not None and self.isUnnestSet(ctx.graphQlValueOptions()) and parent_ref.get("object_reference") is not None and parent_ref["object_reference"].get("reference_mapping") is not None and parent_ref["object_reference"]["reference_mapping"].get("kind") == "1:n" ): # This is an unnest of a 1:n reference, so check if there is exactly one sub-field enabled # and set its id as the "reduce_to_value_of_field_id" of the reference objects = self.mrs_object["objects"] current_object = objects[-1] fields = current_object.get("fields") ref_fields_offset = parent_ref.get("referenced_fields_offset") obj_reference = parent_ref.get("object_reference") reduce_to_field_name = "" for i, reduce_to_field in enumerate(fields): # Ignore all higher level fields and only consider referenced fields if i < ref_fields_offset: continue if ( reduce_to_field.get("enabled") and reduce_to_field.get("represents_reference_id") is None ): if obj_reference.get("reduce_to_value_of_field_id") is None: obj_reference["reduce_to_value_of_field_id"] = ( reduce_to_field.get("id") ) reduce_to_field_name = reduce_to_field.get("name") else: raise Exception( f"Only one column `{reduce_to_field_name}` must be defined for a N:1 unnest operation. " f'The column `{reduce_to_field.get("name")}` needs to be removed.' ) if obj_reference.get("reduce_to_value_of_field_id") is None: raise Exception( f"At least one column must be defined for a N:1 unnest operation." ) def exitCreateRestViewStatement(self, ctx): self.mrs_ddl_executor.createRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST PROCEDURE def add_rest_procedure_params(self, ctx, db_schema_name, db_object_name): object_id = self.get_uuid() params = lib.db_objects.get_db_object_parameters( session=self.session, db_schema_name=db_schema_name, db_object_name=db_object_name, ) param_fields = [] for param in params: param_name = lib.core.convert_snake_to_camel_case(param.get("name")) field = { "id": self.get_uuid(), "object_id": object_id, "name": param_name, "position": param.get("position"), "db_column": { "name": param.get("name"), "in": "IN" in param.get("mode"), "out": "OUT" in param.get("mode"), "datatype": param.get("datatype"), # PROCEDURE IN/INOUT/OUT parameters are nullable by nature "not_null": False, "is_generated": False, "is_primary": False, "is_unique": False, "charset": param.get("charset"), "collation": param.get("collation"), }, # If explicit PARAMETERS are given, add the fields not enabled and enable only the given fields "enabled": ctx.PARAMETERS_SYMBOL() is None, "allow_filtering": True, "allow_sorting": False, "no_check": False, "no_update": False, } param_fields.append(field) self.mrs_object["objects"] = [ { "id": object_id, "db_object_id": self.mrs_object["id"], "name": get_text_without_quotes( ctx.restObjectName().getText() if ctx.restObjectName() is not None else None ), "kind": "PARAMETERS", "position": 0, "fields": param_fields, } ] def enterCreateRestProcedureStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " REST PROCEDURE", "do_replace": ctx.REPLACE_SYMBOL() is not None, "id": self.get_uuid(), "request_path": get_text_without_quotes( ctx.procedureRequestPath().getText() ), "db_object_type": "PROCEDURE", "crud_operations": ["UPDATE"], "force_create": ctx.FORCE_SYMBOL() is not None, } self.set_schema_name_and_name(ctx=ctx) self.add_rest_procedure_params( ctx=ctx, db_schema_name=self.mrs_object["schema_name"], db_object_name=self.mrs_object["name"], ) def exitCreateRestProcedureStatement(self, ctx): self.mrs_ddl_executor.createRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST FUNCTION def add_rest_functions_params_and_result(self, ctx, db_schema_name, db_object_name): object_id = self.get_uuid() params = lib.db_objects.get_db_object_parameters( session=self.session, db_schema_name=db_schema_name, db_object_name=db_object_name, db_type="FUNCTION", ) param_fields = [] for param in params: param_name = lib.core.convert_snake_to_camel_case(param.get("name")) field = { "id": self.get_uuid(), "object_id": object_id, "name": param_name, "position": param.get("position"), "db_column": { "name": param.get("name"), "in": "IN" in param.get("mode"), "out": "OUT" in param.get("mode"), "datatype": param.get("datatype"), # FUNCTION parameters are nullable by nature "not_null": False, "is_generated": False, "is_primary": False, "is_unique": False, "charset": param.get("charset"), "collation": param.get("collation"), }, # If explicit PARAMETERS are given, add the fields not enabled and enable only the given fields "enabled": ctx.PARAMETERS_SYMBOL() is None, "allow_filtering": True, "allow_sorting": False, "no_check": False, "no_update": False, } param_fields.append(field) self.mrs_object["objects"] = [ { "id": object_id, "db_object_id": self.mrs_object["id"], "name": get_text_without_quotes( ctx.restObjectName().getText() if ctx.restObjectName() is not None else None ), "kind": "PARAMETERS", "position": 0, "fields": param_fields, } ] # Get result datatype and add a result object for it returnDataType = lib.db_objects.get_db_function_return_type( session=self.session, db_schema_name=db_schema_name, db_object_name=db_object_name, ) object_id = self.get_uuid() result_fields = [ { "id": self.get_uuid(), "object_id": object_id, "name": "result", "position": 0, "db_column": { "name": "result", "datatype": returnDataType, "not_null": False, "is_generated": False, "is_primary": False, "is_unique": False, }, "enabled": True, "allow_filtering": True, "allow_sorting": False, "no_check": False, "no_update": False, } ] self.mrs_object["objects"].append( { "id": object_id, "db_object_id": self.mrs_object["id"], "name": ( ctx.restFunctionResult().restResultName().getText() if ( ctx.restFunctionResult() is not None and ctx.restObjectName() is not None ) else None ), "kind": "RESULT", "position": 1, "fields": result_fields, } ) def enterCreateRestFunctionStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " REST FUNCTION", "do_replace": ctx.REPLACE_SYMBOL() is not None, "id": self.get_uuid(), "request_path": get_text_without_quotes( ctx.functionRequestPath().getText() ), "db_object_type": "FUNCTION", "crud_operations": ["READ"], "force_create": ctx.FORCE_SYMBOL() is not None, } self.set_schema_name_and_name(ctx=ctx) self.add_rest_functions_params_and_result( ctx=ctx, db_schema_name=self.mrs_object["schema_name"], db_object_name=self.mrs_object["name"], ) def exitCreateRestFunctionStatement(self, ctx): self.mrs_ddl_executor.createRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST CONTENT SET def enterCreateRestContentSetStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " CONTENT SET", "do_replace": ctx.REPLACE_SYMBOL() is not None, "request_path": get_text_without_quotes( ctx.contentSetRequestPath().getText() ), "directory_file_path": ( get_text_without_quotes(ctx.directoryFilePath().getText()) if ctx.directoryFilePath() is not None else None ), "content_type": "STATIC", } def enterFileIgnoreList(self, ctx): self.mrs_object["ignore_file_list"] = ctx.textStringLiteral().getText() def enterLoadScripts(self, ctx): self.mrs_object["content_type"] = "SCRIPTS" if ctx.TYPESCRIPT_SYMBOL() is not None: self.mrs_object["language"] = "TypeScript" def exitCreateRestContentSetStatement(self, ctx): self.mrs_ddl_executor.createRestContentSet(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST CONTENT FILE def enterCreateRestContentFileStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " CONTENT FILE", "do_replace": ctx.REPLACE_SYMBOL() is not None, "request_path": get_text_without_quotes( ctx.contentFileRequestPath().getText() ), "content_set_path": get_text_without_quotes( ctx.contentSetRequestPath().getText() ), "directory_file_path": ( ctx.directoryFilePath().getText() if ctx.directoryFilePath() is not None else None ), "content": ( get_text_without_quotes(ctx.textStringLiteral().getText()) if ctx.textStringLiteral() is not None else None ), "is_binary": ctx.BINARY_SYMBOL() is not None, } def exitCreateRestContentFileStatement(self, ctx): self.mrs_ddl_executor.createRestContentFile(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST AUTH APP def enterCreateRestAuthAppStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " AUTH APP", "do_replace": ctx.REPLACE_SYMBOL() is not None, "name": get_text_without_quotes(ctx.authAppName().getText()), "vendor": ( get_text_without_quotes(ctx.vendorName().getText()) if ctx.vendorName() is not None else ("MySQL Internal" if ctx.MYSQL_SYMBOL() is not None else "MRS") ), "limit_to_registered_users": True, } def exitCreateRestAuthAppStatement(self, ctx): self.mrs_ddl_executor.createRestAuthApp(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST USER def enterCreateRestUserStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " USER", "do_replace": ctx.REPLACE_SYMBOL() is not None, "name": get_text_without_quotes(ctx.userName().getText()), "authAppName": get_text_without_quotes(ctx.authAppName().getText()), "password": unquoted_node_text_or_none(ctx.userPassword()), } def exitCreateRestUserStatement(self, ctx): self.mrs_ddl_executor.createRestUser(self.mrs_object) def enterAccountLock(self, ctx): self.mrs_object["login_permitted"] = ctx.LOCK_SYMBOL() is None # ------------------------------------------------------------------------------------------------------------------ # CREATE REST ROLE def enterCreateRestRoleStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "CREATE" if ctx.REPLACE_SYMBOL() is None else "CREATE OR REPLACE" ) + " ROLE", "do_replace": ctx.REPLACE_SYMBOL() is not None, "name": get_text_without_quotes(ctx.roleName().getText()), "extends": unquoted_node_text_or_none(ctx.parentRoleName()), "any_service": ctx.ANY_SYMBOL(), } def exitCreateRestRoleStatement(self, ctx): self.mrs_ddl_executor.createRestRole(self.mrs_object) # ================================================================================================================== # GRANT REST Statements # ------------------------------------------------------------------------------------------------------------------ # GRANT REST privileges def enterGrantRestPrivilegeStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "GRANT PRIVILEGE", "privileges": set(), "role": unquoted_node_text_or_none(ctx.roleName()), "object_request_path": unquoted_node_text_or_none( ctx.objectRequestPathWildcard() ), } def exitGrantRestPrivilegeStatement(self, ctx): self.mrs_ddl_executor.grantRestPrivileges(self.mrs_object) def enterPrivilegeList(self, ctx): self.mrs_object["privileges"].add(ctx.privilegeName().getText()) def exitPrivilegeList(self, ctx): pass # ------------------------------------------------------------------------------------------------------------------ # GRANT REST ROLE def enterGrantRestRoleStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "GRANT ROLE", "role": get_text_without_quotes(ctx.roleName().getText()), "user": unquoted_node_text_or_none(ctx.userName()), "auth_app_name": unquoted_node_text_or_none(ctx.authAppName()), "comments": unquoted_node_text_or_none(ctx.comments()), } def exitGrantRestRoleStatement(self, ctx): self.mrs_ddl_executor.grantRestRole(self.mrs_object) # ================================================================================================================== # REVOKE REST Statements # ------------------------------------------------------------------------------------------------------------------ # REVOKE REST ROLE def enterRevokeRestRoleStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "REVOKE ROLE", "role": get_text_without_quotes(ctx.roleName().getText()), "user": unquoted_node_text_or_none(ctx.userName()), "auth_app_name": unquoted_node_text_or_none(ctx.authAppName()), } def exitRevokeRestRoleStatement(self, ctx): self.mrs_ddl_executor.revokeRestRole(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # REVOKE REST privileges def enterRevokeRestPrivilegeStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "REVOKE PRIVILEGE", "privileges": set(), "role": unquoted_node_text_or_none(ctx.roleName()), "object_request_path": unquoted_node_text_or_none( ctx.objectRequestPathWildcard() ), } def exitRevokeRestPrivilegeStatement(self, ctx): self.mrs_ddl_executor.revokeRestPrivilege(self.mrs_object) # ================================================================================================================== # CLONE REST Statements # ------------------------------------------------------------------------------------------------------------------ # CLONE REST SERVICE def enterCloneRestServiceStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "CLONE REST SERVICE", } def exitCloneRestServiceStatement(self, ctx): self.mrs_ddl_executor.cloneRestService(self.mrs_object) # ================================================================================================================== # ALTER REST Statements # ------------------------------------------------------------------------------------------------------------------ # ALTER REST SERVICE def enterAlterRestServiceStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "ALTER REST SERVICE", } def enterNewServiceRequestPath(self, ctx): if self.mrs_object.get("new_developer_list") is None: self.mrs_object["new_developer_list"] = [] self.mrs_object["new_url_context_root"] = get_text_without_quotes( ctx.requestPathIdentifier().getText() ) def exitAlterRestServiceStatement(self, ctx): self.mrs_ddl_executor.alterRestService(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # ALTER REST SCHEMA def enterAlterRestSchemaStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "ALTER REST SCHEMA", "schema_request_path": ( get_text_without_quotes(ctx.schemaRequestPath().getText()) if ctx.schemaRequestPath() is not None else None ), } def enterNewSchemaRequestPath(self, ctx): self.mrs_object["new_request_path"] = get_text_without_quotes( ctx.requestPathIdentifier().getText() ) def exitAlterRestSchemaStatement(self, ctx): self.mrs_ddl_executor.alterRestSchema(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # ALTER REST VIEW def enterAlterRestViewStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "ALTER REST VIEW", "request_path": get_text_without_quotes( ctx.viewRequestPath().requestPathIdentifier().getText() ), "new_request_path": get_text_without_quotes( ( ctx.newViewRequestPath().requestPathIdentifier().getText() if ctx.newViewRequestPath() is not None else None ) ), "new_object_name": get_text_without_quotes( ctx.restObjectName().getText() if ctx.restObjectName() is not None else None ), "type": "VIEW", "parent_reference_stack": [], } # if ctx.graphQlCrudOptions() is not None: # self.mrs_object["crud_operations"] = self.build_options_list( # ctx=ctx.graphQlCrudOptions()) db_object = self.get_db_object(ctx=ctx) # Set mrs_object["id"] since the field listener need that self.mrs_object["id"] = db_object["id"] object_id = self.get_uuid() options = self.build_options_list(ctx.graphQlCrudOptions()) self.mrs_object["objects"] = [ { "id": object_id, "db_object_id": db_object["id"], "name": get_text_without_quotes( ctx.restObjectName().getText() if ctx.restObjectName() is not None else None ), "kind": "RESULT", "position": 0, "options": { "dataMappingViewInsert": "INSERT" in options, "dataMappingViewUpdate": "UPDATE" in options, "dataMappingViewDelete": "DELETE" in options, "dataMappingViewNoCheck": "NOCHECK" in options, }, # Get top level fields "fields": self.get_db_object_fields( object_id=object_id, db_schema_name=db_object["schema_name"], db_object_name=db_object["name"], ), } ] def exitAlterRestViewStatement(self, ctx): self.mrs_ddl_executor.alterRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # ALTER REST PROCEDURE def enterAlterRestProcedureStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "ALTER REST PROCEDURE", "request_path": get_text_without_quotes( ctx.procedureRequestPath().getText() ), "new_request_path": get_text_without_quotes( ( ctx.newProcedureRequestPath().requestPathIdentifier().getText() if ctx.newProcedureRequestPath() is not None else None ) ), "new_object_name": get_text_without_quotes( ctx.restObjectName().getText() if ctx.restObjectName() is not None else None ), "type": "PROCEDURE", } db_object = self.get_db_object(ctx=ctx) # Set mrs_object["id"] since the field listener need that self.mrs_object["id"] = db_object["id"] self.add_rest_procedure_params( ctx=ctx, db_schema_name=db_object["schema_name"], db_object_name=db_object["name"], ) def exitAlterRestProcedureStatement(self, ctx): self.mrs_ddl_executor.alterRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # ALTER REST FUNCTION def enterAlterRestFunctionStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "ALTER REST FUNCTION", "request_path": get_text_without_quotes( ctx.functionRequestPath().getText() ), "new_request_path": get_text_without_quotes( ( ctx.newFunctionRequestPath().requestPathIdentifier().getText() if ctx.newFunctionRequestPath() is not None else None ) ), "new_object_name": get_text_without_quotes( ctx.restObjectName().getText() if ctx.restObjectName() is not None else None ), "type": "FUNCTION", } db_object = self.get_db_object(ctx=ctx) # Set mrs_object["id"] since the field listener need that self.mrs_object["id"] = db_object["id"] self.add_rest_functions_params_and_result( ctx=ctx, db_schema_name=db_object["schema_name"], db_object_name=db_object["name"], ) def exitAlterRestFunctionStatement(self, ctx): self.mrs_ddl_executor.alterRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # CREATE REST CONTENT SET def enterAlterRestContentSetStatement(self, ctx): self.mrs_object = { "current_operation": "ALTER CONTENT SET", "request_path": get_text_without_quotes( ctx.contentSetRequestPath().getText() ), "new_request_path": get_text_without_quotes( ( ctx.newContentSetRequestPath().requestPathIdentifier().getText() if ctx.newContentSetRequestPath() is not None else None ) ), "content_type": "STATIC", } def exitAlterRestContentSetStatement(self, ctx): self.mrs_ddl_executor.alterRestContentSet(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # ALTER REST AUTH APP def enterAlterRestAuthAppStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "ALTER AUTH APP", "auth_app_name": get_text_without_quotes(ctx.authAppName().getText()), } if ctx.newAuthAppName() is not None: self.mrs_object["new_auth_app_name"] = get_text_without_quotes( ctx.newAuthAppName().getText() ) def exitAlterRestAuthAppStatement(self, ctx): self.mrs_ddl_executor.alterRestAuthApp(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # ALTER REST USER def enterAlterRestUserStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "ALTER USER", "name": unquoted_node_text_or_none(ctx.userName()), "authAppName": unquoted_node_text_or_none(ctx.authAppName()), "password": unquoted_node_text_or_none(ctx.userPassword()), } def exitAlterRestUserStatement(self, ctx): self.mrs_ddl_executor.alterRestUser(self.mrs_object) # ================================================================================================================== # DROP REST Statements # ------------------------------------------------------------------------------------------------------------------ # DROP REST SERVICE def enterDropRestServiceStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST SERVICE", } def exitDropRestServiceStatement(self, ctx): self.mrs_ddl_executor.dropRestService(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST SCHEMA def enterDropRestSchemaStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST SCHEMA", "request_path": get_text_without_quotes(ctx.schemaRequestPath().getText()), } def exitDropRestSchemaStatement(self, ctx): self.mrs_ddl_executor.dropRestSchema(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST VIEW def enterDropRestViewStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST VIEW", "request_path": get_text_without_quotes(ctx.viewRequestPath().getText()), "type": "VIEW", } def exitDropRestViewStatement(self, ctx): self.mrs_ddl_executor.dropRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST PROCEDURE def enterDropRestProcedureStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST PROCEDURE", "request_path": get_text_without_quotes( ctx.procedureRequestPath().getText() ), "type": "PROCEDURE", } def exitDropRestProcedureStatement(self, ctx): self.mrs_ddl_executor.dropRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST FUNCTION def enterDropRestFunctionStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST FUNCTION", "request_path": get_text_without_quotes( ctx.functionRequestPath().getText() ), "type": "FUNCTION", } def exitDropRestFunctionStatement(self, ctx): self.mrs_ddl_executor.dropRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST CONTENT SET def enterDropRestContentSetStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST CONTENT SET", "request_path": get_text_without_quotes( ctx.contentSetRequestPath().getText() ), } def exitDropRestContentSetStatement(self, ctx): self.mrs_ddl_executor.dropRestContentSet(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST CONTENT FILE def enterDropRestContentFileStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST CONTENT FILE", "request_path": get_text_without_quotes( ctx.contentFileRequestPath().getText() ), "content_set_path": get_text_without_quotes( ctx.contentSetRequestPath().getText() ), } def exitDropRestContentFileStatement(self, ctx): self.mrs_ddl_executor.dropRestContentFile(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST AUTH APP def enterDropRestAuthAppStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST AUTH APP", "name": get_text_without_quotes(ctx.authAppName().getText()), } def exitDropRestAuthAppStatement(self, ctx): self.mrs_ddl_executor.dropRestAuthApp(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST USER def enterDropRestUserStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST USER", "name": get_text_without_quotes(ctx.userName().getText()), "authAppName": get_text_without_quotes(ctx.authAppName().getText()), } def exitDropRestUserStatement(self, ctx): self.mrs_ddl_executor.dropRestUser(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DROP REST ROLE def enterDropRestRoleStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DROP REST ROLE", "name": get_text_without_quotes(ctx.roleName().getText()), } def exitDropRestRoleStatement(self, ctx): self.mrs_ddl_executor.dropRestRole(self.mrs_object) # ================================================================================================================== # USE REST Statement def enterUseStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": ( "USE REST " + "SERVICE" if (ctx.serviceAndSchemaRequestPaths().serviceSchemaSelector() is None) else "SCHEMA" ), "schema_request_path": get_text_without_quotes( ctx.serviceAndSchemaRequestPaths() .serviceSchemaSelector() .schemaRequestPath() .getText() if ( ctx.serviceAndSchemaRequestPaths().serviceSchemaSelector() is not None ) else None ), } def exitUseStatement(self, ctx): self.mrs_ddl_executor.use(self.mrs_object) # ================================================================================================================== # SHOW REST Statements # ------------------------------------------------------------------------------------------------------------------ # SHOW REST METADATA STATUS def enterShowRestMetadataStatusStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST METADATA STATUS", } def exitShowRestMetadataStatusStatement(self, ctx): self.mrs_ddl_executor.showRestMetadataStatus(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST SERVICES def enterShowRestServicesStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST SERVICES", } def exitShowRestServicesStatement(self, ctx): self.mrs_ddl_executor.showRestServices(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST SCHEMAS def enterShowRestSchemasStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST SCHEMAS", } def exitShowRestSchemasStatement(self, ctx): self.mrs_ddl_executor.showRestSchemas(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST VIEWS def enterShowRestViewsStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST VIEWS", "object_types": ["TABLE", "VIEW"], } def exitShowRestViewsStatement(self, ctx): self.mrs_ddl_executor.showRestDbObjects(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST PROCEDURES def enterShowRestProceduresStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST PROCEDURES", "object_types": ["PROCEDURE"], } def exitShowRestProceduresStatement(self, ctx): self.mrs_ddl_executor.showRestDbObjects(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST FUNCTIONS def enterShowRestFunctionsStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST FUNCTIONS", "object_types": ["FUNCTION"], } def exitShowRestFunctionsStatement(self, ctx): self.mrs_ddl_executor.showRestDbObjects(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST CONTENT SETS def enterShowRestContentSetsStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST CONTENT SETS", } def exitShowRestContentSetsStatement(self, ctx): self.mrs_ddl_executor.showRestContentSets(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST AUTH APPS def enterShowRestAuthAppsStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST AUTH APPS", } def exitShowRestAuthAppsStatement(self, ctx): self.mrs_ddl_executor.showRestAuthApps(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST ROLES def enterShowRestRolesStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST ROLES", "user_name": unquoted_node_text_or_none(ctx.userName()), "auth_app_name": unquoted_node_text_or_none(ctx.authAppName()), "any_service": ctx.ANY_SYMBOL(), } def exitShowRestRolesStatement(self, ctx): self.mrs_ddl_executor.showRestRoles(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW REST GRANTS def enterShowRestGrantsStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW REST GRANTS", "role": get_text_without_quotes(ctx.roleName().getText()), } def exitShowRestGrantsStatement(self, ctx): self.mrs_ddl_executor.showRestGrants(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW CREATE REST SERVICE def enterShowCreateRestServiceStatement(self, ctx): include_database_endpoints = False if ctx.DATABASE_SYMBOL() is not None: include_database_endpoints = True self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW CREATE REST SERVICE", "include_database_endpoints": include_database_endpoints, } def exitShowCreateRestServiceStatement(self, ctx): self.mrs_ddl_executor.showCreateRestService(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW CREATE REST SCHEMA def enterShowCreateRestSchemaStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW CREATE REST SCHEMA", "schema_request_path": ( get_text_without_quotes(ctx.schemaRequestPath().getText()) if ctx.schemaRequestPath() is not None else None ), } def exitShowCreateRestSchemaStatement(self, ctx): self.mrs_ddl_executor.showCreateRestSchema(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW CREATE REST VIEW def enterShowCreateRestViewStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW CREATE REST VIEW", "request_path": get_text_without_quotes(ctx.viewRequestPath().getText()), "type": "VIEW", } def exitShowCreateRestViewStatement(self, ctx): self.mrs_ddl_executor.showCreateRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW CREATE REST PROCEDURE def enterShowCreateRestProcedureStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW CREATE REST PROCEDURE", "request_path": get_text_without_quotes( ctx.procedureRequestPath().getText() ), "type": "PROCEDURE", } def exitShowCreateRestProcedureStatement(self, ctx): self.mrs_ddl_executor.showCreateRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW CREATE REST FUNCTION def enterShowCreateRestFunctionStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW CREATE REST FUNCTION", "request_path": get_text_without_quotes( ctx.functionRequestPath().getText() ), "type": "FUNCTION", } def exitShowCreateRestFunctionStatement(self, ctx): self.mrs_ddl_executor.showCreateRestDbObject(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # SHOW CREATE REST AUTH APP def enterShowCreateRestAuthAppStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "SHOW CREATE REST AUTH APP", "name": get_text_without_quotes(ctx.authAppName().getText()), } def exitShowCreateRestAuthAppStatement(self, ctx): self.mrs_ddl_executor.showCreateRestAuthApp(self.mrs_object) # ------------------------------------------------------------------------------------------------------------------ # DUMP REST SERVICE def enterDumpRestServiceStatement(self, ctx): self.mrs_object = { "line": ctx.start.line, "current_operation": "DUMP REST SERVICE", "destination_path": get_text_without_quotes(ctx.directoryFilePath().getText()), "include_database_endpoints": ctx.DATABASE_SYMBOL() is not None or ctx.ALL_SYMBOL() is not None, "include_static_endpoints": ctx.STATIC_SYMBOL() is not None or ctx.ALL_SYMBOL() is not None, "include_dynamic_endpoints": ctx.DYNAMIC_SYMBOL() is not None or ctx.ALL_SYMBOL() is not None, "zip": ctx.ZIP_SYMBOL() is not None, } def exitDumpRestServiceStatement(self, ctx): self.mrs_ddl_executor.dumpRestService(self.mrs_object) class MrsDdlErrorListener(antlr4.error.ErrorListener.ErrorListener): def __init__(self, errors): self.errors = errors def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): self.errors.append( { "line": line, "column": column, "message": msg.capitalize(), "fullMessage": f"Syntax Error: {msg.capitalize()} [Ln {line}: Col {column}]", } )