mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
mariadb-upgrade silently dropped EXECUTE grants on sys schema stored functions and procedures. The sys schema install scripts reinstalled every routine with DROP FUNCTION/PROCEDURE IF EXISTS followed by CREATE. DROP cascades to delete the routine's rows in mysql.procs_priv, so any EXECUTE grant a DBA had issued on e.g. sys.table_exists or sys.quote_identifier was lost every time mariadb-upgrade reinstalled the sys schema, even though the routine itself came back unchanged. Fix: replace DROP ... IF EXISTS + CREATE with CREATE OR REPLACE in all 53 sys_schema function/procedure files and in the two templates (templates/function.sql, templates/procedure.sql) so future routines follow the same pattern. CREATE OR REPLACE PROCEDURE/FUNCTION goes through sp_drop_routine_internal(), which only deletes the mysql.proc row and never reaches sp_revoke_privileges() (that is only called from the explicit DROP PROCEDURE/FUNCTION statement), so mysql.procs_priv is left untouched and existing grants survive. This mirrors the pattern already used by sys schema views (CREATE OR REPLACE ... VIEW, since MDEV-9077), which never had this problem. Four of the converted files (functions/format_path.sql, functions/ps_is_account_enabled_57.sql, procedures/ps_setup_reset_to_default.sql, procedures/ps_trace_thread_57.sql) are not referenced by scripts/sys_schema/CMakeLists.txt; they were converted anyway for consistency and have no behavioural effect. As a side effect, a pre-existing UDF whose name collides with a sys routine name is no longer destroyed before the reinstall fails: DROP FUNCTION IF EXISTS resolved the UDF namespace first, silently dropping the UDF and then failing on ER_SP_ALREADY_EXISTS anyway; CREATE OR REPLACE fails immediately on ER_UDF_EXISTS with the UDF intact. scripts/maria_add_gis_sp.sql.in and the sys_config triggers were deliberately left untouched: the GIS procedures are only re-installed at bootstrap time (mariadb-upgrade instead patches their definer in place via UPDATE), and triggers carry no procs_priv rows, so neither is on the code path this bug is about. Added mysql-test/main/mysql_upgrade_sys_routine_grants.test, which grants EXECUTE on sys.table_exists and sys.quote_identifier, overwrites both routine bodies with a marker to prove the upgrade actually reinstalls them (rather than the sys schema install being skipped), runs mariadb-upgrade, and checks both that the grants survived and that the real routine bodies came back. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
182 lines
6.8 KiB
SQL
182 lines
6.8 KiB
SQL
-- Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
|
--
|
|
-- This program is free software; you can redistribute it and/or modify
|
|
-- it under the terms of the GNU General Public License as published by
|
|
-- the Free Software Foundation; version 2 of the License.
|
|
--
|
|
-- 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 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
|
|
|
|
DELIMITER $$
|
|
|
|
CREATE OR REPLACE DEFINER='mariadb.sys'@'localhost' PROCEDURE table_exists (
|
|
IN in_db VARCHAR(64), IN in_table VARCHAR(64),
|
|
OUT out_exists ENUM('', 'BASE TABLE', 'VIEW', 'TEMPORARY', 'SEQUENCE', 'SYSTEM VIEW', 'TEMPORARY SEQUENCE')
|
|
)
|
|
COMMENT '
|
|
Description
|
|
-----------
|
|
|
|
Tests whether the table specified in in_db and in_table exists either as a regular
|
|
table, or as a temporary table. The returned value corresponds to the table that
|
|
will be used, so if there''s both a temporary and a permanent table with the given
|
|
name, then ''TEMPORARY'' will be returned.
|
|
|
|
Parameters
|
|
-----------
|
|
|
|
in_db (VARCHAR(64)):
|
|
The database name to check for the existence of the table in.
|
|
|
|
in_table (VARCHAR(64)):
|
|
The name of the table to check the existence of.
|
|
|
|
out_exists ENUM('''', ''BASE TABLE'', ''VIEW'', ''TEMPORARY'', ''SEQUENCE'', ''SYSTEM VIEW'', ''TEMPORARY SEQUENCE''):
|
|
The return value: whether the table exists. The value is one of:
|
|
* '''' - the table does not exist neither as a base table, view, sequence nor temporary table/sequence.
|
|
* ''BASE TABLE'' - the table name exists as a permanent base table table.
|
|
* ''VIEW'' - the table name exists as a view.
|
|
* ''TEMPORARY'' - the table name exists as a temporary table.
|
|
* ''SEQUENCE'' - the table name exists as a sequence.
|
|
* ''SYSTEM VIEW'' - the table name exists as a system view.
|
|
* ''TEMPORARY SEQUENCE'' - the table name exists as a temporary sequence.
|
|
|
|
Example
|
|
--------
|
|
|
|
MariaDB [sys]> CREATE DATABASE db1;
|
|
Query OK, 1 row affected (0.07 sec)
|
|
|
|
MariaDB [sys]> use db1;
|
|
Database changed
|
|
|
|
MariaDB [sys]> CREATE TABLE t1 (id INT PRIMARY KEY);
|
|
Query OK, 0 rows affected (0.08 sec)
|
|
|
|
MariaDB [sys]> CREATE TABLE t2 (id INT PRIMARY KEY);
|
|
Query OK, 0 rows affected (0.08 sec)
|
|
|
|
MariaDB [sys]> CREATE view v_t1 AS SELECT * FROM t1;
|
|
Query OK, 0 rows affected (0.00 sec)
|
|
|
|
MariaDB [sys]> CREATE TEMPORARY TABLE t1 (id INT PRIMARY KEY);
|
|
Query OK, 0 rows affected (0.00 sec)
|
|
|
|
MariaDB [sys]> CREATE SEQUENCE s;
|
|
Query OK, 0 rows affected (0.00 sec)
|
|
|
|
MariaDB [sys]> CREATE TEMPORARY SEQUENCE s_temp;
|
|
Query OK, 0 rows affected (0.00 sec)
|
|
|
|
MariaDB [sys]> CALL sys.table_exists(''db1'', ''t1'', @exists); SELECT @exists;
|
|
Query OK, 0 rows affected (0.00 sec)
|
|
|
|
+------------+
|
|
| @exists |
|
|
+------------+
|
|
| TEMPORARY |
|
|
+------------+
|
|
1 row in set (0.00 sec)
|
|
|
|
MariaDB [sys]> CALL sys.table_exists(''db1'', ''t2'', @exists); SELECT @exists;
|
|
Query OK, 0 rows affected (0.00 sec)
|
|
|
|
+------------+
|
|
| @exists |
|
|
+------------+
|
|
| BASE TABLE |
|
|
+------------+
|
|
1 row in set (0.01 sec)
|
|
|
|
MariaDB [sys]> CALL sys.table_exists(''db1'', ''v_t1'', @exists); SELECT @exists;
|
|
Query OK, 0 rows affected (0.00 sec)
|
|
|
|
+---------+
|
|
| @exists |
|
|
+---------+
|
|
| VIEW |
|
|
+---------+
|
|
1 row in set (0.00 sec)
|
|
|
|
MariaDB [sys]> CALL sys.table_exists(''db1'', ''s'', @exists); SELECT @exists;
|
|
Query OK, 0 rows affected (0.006 sec)
|
|
|
|
+----------+
|
|
| @exists |
|
|
+----------+
|
|
| SEQUENCE |
|
|
+----------+
|
|
1 row in set (0.000 sec)
|
|
|
|
MariaDB [sys]> CALL table_exists(''information_schema'', ''user_variables'', @exists); SELECT @exists;
|
|
Query OK, 0 rows affected (0.003 sec)
|
|
|
|
+-------------+
|
|
| @exists |
|
|
+-------------+
|
|
| SYSTEM VIEW |
|
|
+-------------+
|
|
1 row in set (0.001 sec)
|
|
|
|
MariaDB [sys]> CALL sys.table_exists(''db1'', ''t3'', @exists); SELECT @exists;
|
|
Query OK, 0 rows affected (0.01 sec)
|
|
|
|
+---------+
|
|
| @exists |
|
|
+---------+
|
|
| |
|
|
+---------+
|
|
1 row in set (0.00 sec)
|
|
|
|
MariaDB [sys]> CALL table_exists(''db1'', ''s_temp'', @exists); SELECT @exists;
|
|
Query OK, 0 rows affected (0.003 sec)
|
|
|
|
+--------------------+
|
|
| @exists |
|
|
+--------------------+
|
|
| TEMPORARY SEQUENCE |
|
|
+--------------------+
|
|
1 row in set (0.001 sec)
|
|
'
|
|
SQL SECURITY INVOKER
|
|
NOT DETERMINISTIC
|
|
CONTAINS SQL
|
|
BEGIN
|
|
DECLARE v_error BOOLEAN DEFAULT FALSE;
|
|
DECLARE db_quoted VARCHAR(64);
|
|
DECLARE table_quoted VARCHAR(64);
|
|
DECLARE v_table_type VARCHAR(30) DEFAULT '';
|
|
DECLARE CONTINUE HANDLER FOR 1050 SET v_error = TRUE;
|
|
DECLARE CONTINUE HANDLER FOR 1146 SET v_error = TRUE;
|
|
|
|
-- First check do we have multiple rows, what can happen if temporary table
|
|
-- and/or sequence is shadowing base table for example.
|
|
-- In such scenario return temporary.
|
|
SET v_table_type = (SELECT GROUP_CONCAT(TABLE_TYPE) FROM information_schema.TABLES WHERE
|
|
TABLE_SCHEMA = in_db AND TABLE_NAME = in_table);
|
|
|
|
IF v_table_type LIKE '%,%' THEN
|
|
SET out_exists = 'TEMPORARY';
|
|
ELSE
|
|
IF v_table_type is NULL
|
|
THEN
|
|
SET v_table_type='';
|
|
END IF;
|
|
-- Don't fail on table_type='SYSTEM VERSIONED'
|
|
-- but return 'BASE TABLE' for compatibility with existing tooling
|
|
IF v_table_type = 'SYSTEM VERSIONED' THEN
|
|
SET out_exists = 'BASE TABLE';
|
|
ELSE
|
|
SET out_exists = v_table_type;
|
|
END IF;
|
|
END IF;
|
|
END$$
|
|
|
|
DELIMITER ;
|