mrs_plugin/lib/general.py (167 lines of code) (raw):

# Copyright (c) 2022, 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 from mrs_plugin import lib import msm_plugin.lib.management as schema_management # Define plugin version VERSION = "1.19.10" DB_VERSION = [4, 1, 2] REQUIRED_ROUTER_VERSION = [8, 1, 0] SUPPORTED_MAJOR_VERSION = 3 DB_VERSION_STR = '%d.%d.%d' % tuple(DB_VERSION) DB_VERSION_NUM = DB_VERSION[0] * 100000 + DB_VERSION[1] * 1000 + DB_VERSION[2] REQUIRED_ROUTER_VERSION_STR = '%d.%d.%d' % tuple(REQUIRED_ROUTER_VERSION) def get_status(session): # Check if the MRS metadata schema already exists row = lib.database.get_schema(session, "mysql_rest_service_metadata") if row is None: return { 'service_configured': False, 'service_enabled': False, 'service_count': 0, 'service_upgradeable': False, 'service_upgrade_ignored': False, 'major_upgrade_required': False, 'service_being_upgraded': False, } # Check if the msm_schema_version VIEW already exists row = lib.database.get_db_object( session, "mysql_rest_service_metadata", "msm_schema_version", "VIEW") if row is None: # Also check the schema_version VIEW that was used for versions <= 3.1.0 row = lib.database.get_db_object( session, "mysql_rest_service_metadata", "schema_version", "VIEW") if row is None: return { 'service_configured': False, 'service_enabled': False, 'service_count': 0, 'service_upgradeable': False, 'service_upgrade_ignored': False, 'major_upgrade_required': False, 'service_being_upgraded': True, } # Get current version of metadata schema current_version = lib.core.get_mrs_schema_version(session) result = { 'service_configured': True, 'service_enabled': False, 'service_upgradeable': DB_VERSION > current_version, 'service_upgrade_ignored': False, 'service_count': 0, 'service_being_upgraded': current_version[0] == 0 and current_version[1] == 0 and current_version[2] == 0, 'major_upgrade_required': current_version[0] == 1, 'current_metadata_version': '%d.%d.%d' % tuple(current_version), 'available_metadata_version': DB_VERSION_STR, 'required_router_version': REQUIRED_ROUTER_VERSION_STR, } if result["service_being_upgraded"] == True: return result # Check if MRS is enabled row = lib.core.select(table="config", cols=[ "service_enabled", "JSON_VALUE(data, '$.ignore_service_upgrades_till') as ignore_till"], where="id=1" ).exec(session).first result['service_enabled'] = row["service_enabled"] == 1 ignore_till = row["ignore_till"] if ignore_till != None: result['service_upgrade_ignored'] = [ int(i) for i in ignore_till.split(".")] >= current_version # Get the number of enabled services row = lib.core.select(table="service", cols="SUM(enabled) as service_count" ).exec(session).first result['service_count'] = 0 if row["service_count"] is None else int( row["service_count"]) return result def configure(session=None, enable_mrs: bool = None, options: str = None, update_if_available=False, edition: str = None, version: str = None, merge_options=False): """Initializes and configures the MySQL REST Data Service Args: session (object): The database session to use enable_mrs (bool): Whether MRS should be enabled or disabled options (str): a JSON string containing the MRS options update_if_available (bool): Whether the MRS metadata schema should be updated edition (str): If set to HeatWave or MySQLAi, special handling for those edition is triggered. version (str): The exact version to upgrade the metadata schema to. merge_options (bool): If set to True, specified options will be merged rather than overwritten Returns: A dict with status information """ skip_update = False with lib.core.MrsDbSession(session=session, check_version=False) as session: if lib.core.mrs_metadata_schema_exists(session): current_db_version = lib.core.get_mrs_schema_version(session) current_version_str = '%d.%d.%d' % tuple(current_db_version) last_deployment_version = schema_management.get_last_deployment_script_version( schema_project_path=lib.core.script_path( "db_schema", "mysql_rest_service_metadata.msm.project")) if current_db_version[0] > lib.general.DB_VERSION[0]: raise Exception( "This version of MySQL Shell does not support the MRS " "metadata database schema version " f"{current_version_str}. Please update " "MySQL Shell to work with this MRS version.") if current_db_version[0] < lib.general.SUPPORTED_MAJOR_VERSION and not update_if_available: raise Exception( f"The MRS metadata version {current_version_str} is " "too old to be managed by this version of MySQL Shell. " "Please update the MRS metadata version, e.g. run " "`mrs.configure(update_if_available=True)` to update.") if current_db_version < last_deployment_version and not update_if_available: skip_update = True # When upgrading from major version 3, make sure to create the # msm_schema_version view in order to allow the msm_plugin to update it if current_db_version[0] == 3: session.run_sql(""" CREATE OR REPLACE VIEW mysql_rest_service_metadata.msm_schema_version AS SELECT * FROM mysql_rest_service_metadata.schema_version""") if edition is None or edition.lower() != "heatwave": # For now, let's remove any previous version of the mysql_tasks schema session.run_sql("DROP SCHEMA IF EXISTS mysql_tasks") # For all editions except heatwave, also deploy the mysql_tasks # database schema if not skip_update: schema_management.deploy_schema( session=session, schema_project_path=lib.core.script_path( "db_schema", "mysql_tasks.msm.project")) # Start the MRS metadata schema deployment which will either create # or update an existing schema to the given version if not skip_update: info_msg = schema_management.deploy_schema( session=session, schema_project_path=lib.core.script_path( "db_schema", "mysql_rest_service_metadata.msm.project"), version=version) schema_changed = not ("No changes" in info_msg) # Check if the HeatWave default endpoints have already been deployed if edition is not None and ( edition.lower() == "heatwave" or edition.lower() == "mysqlai"): res = session.run_sql(""" SELECT COUNT(*) > 0 AS has_default_endpoints FROM mysql_rest_service_metadata.service WHERE url_context_root = '/HeatWave/v1';""").fetch_one() if res and int(res[0]) == 0: # HeatWave default endpoints have not been deployed yet, # so deploy them now schema_management.execute_msm_sql_script( session=session, script_name="HeatWave Default Endpoints", sql_file_path=lib.core.script_path( "scripts", "default_heatwave_endpoints", "heatwave_rest_service_1.0.0.sql")) else: schema_changed = False info_msg = ( "MRS metadata version update available, but update skipped.") if enable_mrs is not None: lib.core.update( table="config", sets="service_enabled=?", where="id=1" ).exec(session, [1 if enable_mrs else 0]) else: row = lib.core.select( table="config", cols="service_enabled", where="id=1" ).exec(session).first enable_mrs = row["service_enabled"] if row else 0 if options is not None: if merge_options: session.run_sql(""" UPDATE `mysql_rest_service_metadata`.`config` SET data = JSON_MERGE_PATCH(data, ?) WHERE id = 1""", [options]) else: session.run_sql(""" UPDATE `mysql_rest_service_metadata`.`config` SET data = ? WHERE id = 1""", [options]) return { "schema_changed": schema_changed, "info_msg": info_msg, "mrs_enabled": True if enable_mrs else False } def ignore_version_upgrade(session): lib.core.update( table="`mysql_rest_service_metadata`.`config`", sets=f"data = JSON_SET(data, '$.ignore_service_upgrades_till', '{DB_VERSION_STR}')", where="id = 1" ).exec(session) def get_available_metadata_versions(session): return schema_management.get_released_versions( schema_project_path=lib.core.script_path( "db_schema", "mysql_rest_service_metadata.msm.project")) def get_config_options(session): # Check if the msm_schema_version VIEW already exists row = lib.database.get_db_object( session, "mysql_rest_service_metadata", "config", "TABLE") if row: row = lib.core.select( table="config", cols=["data"], where="id=1" ).exec(session).first return row["data"] else: return {}