commit 950745e1eef3e9d9bb88c92622dd85bdf0a153f7
Author: Galina Shalygina <galina.sh@mariadb.com>
Date:   Mon Aug 26 17:31:20 2024 +0300

    MENT-2069: Pushdown of local SP variables into derived is not allowed
    
    Before the provided improvement pushdown into derived of local variables
    of stored procedures was not allowed due to the implementation limits.
    This limitation caused problems with perfomance for certain stored
    procedures calls.

diff --git a/mysql-test/main/sp.result b/mysql-test/main/sp.result
index 9efd098fd9c..67a6af09f20 100644
--- a/mysql-test/main/sp.result
+++ b/mysql-test/main/sp.result
@@ -9096,3 +9096,168 @@ database()
 test
 DROP FUNCTION database;
 DROP TABLE t1;
+#
+# MENT-2069: Pushdown of local SP variables into derived is not allowed
+#
+CREATE TABLE t1(
+a INT,
+b INT,
+PRIMARY KEY(a)
+);
+INSERT INTO t1 VALUES (1, 2), (3, 2), (4, 5);
+CREATE PROCEDURE pushdownDerivedSp()
+BEGIN
+DECLARE localA INT DEFAULT 1;
+DECLARE localB INT DEFAULT 2;
+SELECT dt.a
+FROM (
+SELECT t1.a, MIN(t1.b) as minB
+FROM t1
+GROUP BY t1.a
+) AS dt
+WHERE dt.minB = localB AND dt.a = localA;
+END$$
+CREATE PROCEDURE explainPushdownDerivedSp()
+BEGIN
+DECLARE localA INT DEFAULT 1;
+DECLARE localB INT DEFAULT 2;
+EXPLAIN format=json SELECT dt.a
+FROM (
+SELECT t1.a, MIN(t1.b) as minB
+FROM t1
+GROUP BY t1.a
+) AS dt
+WHERE dt.minB = localB AND dt.a = localA;
+END$$
+CREATE PROCEDURE pushdownFromHavingSp()
+BEGIN
+DECLARE localA INT DEFAULT 1;
+SELECT t1.a
+FROM t1
+GROUP BY t1.a
+HAVING t1.a = localA;
+END$$
+CREATE PROCEDURE explainPushdownFromHavingSp()
+BEGIN
+DECLARE localA INT DEFAULT 1;
+EXPLAIN format=json SELECT t1.a
+FROM t1
+GROUP BY t1.a
+HAVING t1.a = localA;
+END$$
+CALL pushdownDerivedSp();
+a
+1
+set statement optimizer_switch='condition_pushdown_for_derived=off' for CALL pushdownDerivedSp();
+a
+1
+CALL explainPushdownDerivedSp();
+EXPLAIN
+{
+  "query_block": {
+    "select_id": 1,
+    "table": {
+      "table_name": "<derived2>",
+      "access_type": "system",
+      "rows": 1,
+      "filtered": 100,
+      "materialized": {
+        "query_block": {
+          "select_id": 2,
+          "having_condition": "minB = localB@1",
+          "table": {
+            "table_name": "t1",
+            "access_type": "const",
+            "possible_keys": ["PRIMARY"],
+            "key": "PRIMARY",
+            "key_length": "4",
+            "used_key_parts": ["a"],
+            "ref": ["const"],
+            "rows": 1,
+            "filtered": 100
+          }
+        }
+      }
+    }
+  }
+}
+set statement optimizer_switch='condition_pushdown_for_derived=off' for CALL explainPushdownDerivedSp();
+EXPLAIN
+{
+  "query_block": {
+    "select_id": 1,
+    "table": {
+      "table_name": "<derived2>",
+      "access_type": "ALL",
+      "rows": 3,
+      "filtered": 100,
+      "attached_condition": "dt.minB = <cache>(localB@1) and dt.a = <cache>(localA@0)",
+      "materialized": {
+        "query_block": {
+          "select_id": 2,
+          "filesort": {
+            "sort_key": "t1.a",
+            "temporary_table": {
+              "table": {
+                "table_name": "t1",
+                "access_type": "ALL",
+                "rows": 3,
+                "filtered": 100
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+# before this fix pushdown from HAVING was already allowed for SP local variables
+CALL pushdownFromHavingSp();
+a
+1
+set statement optimizer_switch='condition_pushdown_from_having=off' for CALL pushdownFromHavingSp();
+a
+1
+CALL explainPushdownFromHavingSp();
+EXPLAIN
+{
+  "query_block": {
+    "select_id": 1,
+    "table": {
+      "table_name": "t1",
+      "access_type": "const",
+      "possible_keys": ["PRIMARY"],
+      "key": "PRIMARY",
+      "key_length": "4",
+      "used_key_parts": ["a"],
+      "ref": ["const"],
+      "rows": 1,
+      "filtered": 100,
+      "using_index": true
+    }
+  }
+}
+set statement optimizer_switch='condition_pushdown_from_having=off' for CALL explainPushdownFromHavingSp();
+EXPLAIN
+{
+  "query_block": {
+    "select_id": 1,
+    "having_condition": "t1.a = <cache>(localA@0)",
+    "table": {
+      "table_name": "t1",
+      "access_type": "index",
+      "key": "PRIMARY",
+      "key_length": "4",
+      "used_key_parts": ["a"],
+      "rows": 3,
+      "filtered": 100,
+      "attached_condition": "t1.a = <cache>(localA@0)",
+      "using_index": true
+    }
+  }
+}
+DROP PROCEDURE pushdownDerivedSp;
+DROP PROCEDURE explainPushdownDerivedSp;
+DROP PROCEDURE pushdownFromHavingSp;
+DROP PROCEDURE explainPushdownFromHavingSp;
+DROP TABLE t1;
diff --git a/mysql-test/main/sp.test b/mysql-test/main/sp.test
index 67bd22f6e7e..ba2b35e9f4d 100644
--- a/mysql-test/main/sp.test
+++ b/mysql-test/main/sp.test
@@ -10677,3 +10677,81 @@ DROP FUNCTION database;
 # Cleanup
 DROP TABLE t1;
 
+
+--echo #
+--echo # MENT-2069: Pushdown of local SP variables into derived is not allowed
+--echo #
+
+CREATE TABLE t1(
+  a INT,
+  b INT,
+  PRIMARY KEY(a)
+);
+INSERT INTO t1 VALUES (1, 2), (3, 2), (4, 5);
+
+DELIMITER $$;
+CREATE PROCEDURE pushdownDerivedSp()
+BEGIN
+    DECLARE localA INT DEFAULT 1;
+    DECLARE localB INT DEFAULT 2;
+    SELECT dt.a
+    FROM (
+      SELECT t1.a, MIN(t1.b) as minB
+      FROM t1
+      GROUP BY t1.a
+    ) AS dt
+    WHERE dt.minB = localB AND dt.a = localA;
+END$$
+
+CREATE PROCEDURE explainPushdownDerivedSp()
+BEGIN
+    DECLARE localA INT DEFAULT 1;
+    DECLARE localB INT DEFAULT 2;
+    EXPLAIN format=json SELECT dt.a
+    FROM (
+      SELECT t1.a, MIN(t1.b) as minB
+      FROM t1
+      GROUP BY t1.a
+    ) AS dt
+    WHERE dt.minB = localB AND dt.a = localA;
+END$$
+
+CREATE PROCEDURE pushdownFromHavingSp()
+BEGIN
+    DECLARE localA INT DEFAULT 1;
+    SELECT t1.a
+    FROM t1
+    GROUP BY t1.a
+    HAVING t1.a = localA;
+END$$
+
+CREATE PROCEDURE explainPushdownFromHavingSp()
+BEGIN
+    DECLARE localA INT DEFAULT 1;
+    EXPLAIN format=json SELECT t1.a
+    FROM t1
+    GROUP BY t1.a
+    HAVING t1.a = localA;
+END$$
+
+DELIMITER ;$$
+
+CALL pushdownDerivedSp();
+set statement optimizer_switch='condition_pushdown_for_derived=off' for CALL pushdownDerivedSp();
+
+CALL explainPushdownDerivedSp();
+set statement optimizer_switch='condition_pushdown_for_derived=off' for CALL explainPushdownDerivedSp();
+
+--echo # before this fix pushdown from HAVING was already allowed for SP local variables
+
+CALL pushdownFromHavingSp();
+set statement optimizer_switch='condition_pushdown_from_having=off' for CALL pushdownFromHavingSp();
+
+CALL explainPushdownFromHavingSp();
+set statement optimizer_switch='condition_pushdown_from_having=off' for CALL explainPushdownFromHavingSp();
+
+DROP PROCEDURE pushdownDerivedSp;
+DROP PROCEDURE explainPushdownDerivedSp;
+DROP PROCEDURE pushdownFromHavingSp;
+DROP PROCEDURE explainPushdownFromHavingSp;
+DROP TABLE t1;
diff --git a/sql/item.h b/sql/item.h
index fd9f8aec66a..5fd665a927c 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -3247,7 +3247,8 @@ class Item_splocal :public Item_sp_variable,
 
   bool append_for_log(THD *thd, String *str) override;
 
-  Item *get_copy(THD *) override { return nullptr; }
+  Item *get_copy(THD *thd) override
+  { return get_item_copy<Item_splocal>(thd, this); }
 
   /*
     Override the inherited create_field_for_create_select(),
