diff --git a/mysql-test/main/mysqld--help.test b/mysql-test/main/mysqld--help.test index 2fd06dbef1b5f..d43fd8b9dd57a 100644 --- a/mysql-test/main/mysqld--help.test +++ b/mysql-test/main/mysqld--help.test @@ -41,7 +41,8 @@ perl; wsrep file-key-management cracklib-password-check user-variables provider-bzip2 provider-lzma provider-lzo caching-sha2-password thread-pool-groups thread-pool-queues thread-pool-stats - thread-pool-waits hashicorp provider gssapi parsec videx/; + thread-pool-waits hashicorp provider gssapi parsec videx + ps-thread-id ps-current-thread-id/; # And substitute the content some environment variables with their # names: diff --git a/mysql-test/mariadb-test-run.pl b/mysql-test/mariadb-test-run.pl index cb0a7a702a68f..6c11a5198cf2a 100755 --- a/mysql-test/mariadb-test-run.pl +++ b/mysql-test/mariadb-test-run.pl @@ -1846,8 +1846,9 @@ sub collect_mysqld_features { # Enable or disable ARCHIVE plugin. Possible values are ON, OFF, # FORCE (don't start if the plugin fails to load), # FORCE_PLUS_PERMANENT (like FORCE, but the plugin can not be uninstalled). - # For Innodb I_S plugins that are referenced in sys schema - # do not make them optional, to prevent diffs in tests. + # For Innodb I_S plugins and the performance schema functions + # that are referenced in sys schema do not make them optional, + # to prevent diffs in tests. push @optional_plugins, $1 if /^ --([-a-z0-9]+)\[=name\] +Enable or disable \w+ plugin. One of: ON, OFF, FORCE/ and $1 ne "innodb-metrics" @@ -1855,6 +1856,8 @@ sub collect_mysqld_features { and $1 ne "innodb-lock-waits" and $1 ne "innodb-locks" and $1 ne "innodb-trx" + and $1 ne "ps-thread-id" + and $1 ne "ps-current-thread-id" and $1 ne "gssapi"; next; } diff --git a/mysql-test/suite/perfschema/r/native_func_thread_id.result b/mysql-test/suite/perfschema/r/native_func_thread_id.result new file mode 100644 index 0000000000000..30c760daf3b00 --- /dev/null +++ b/mysql-test/suite/perfschema/r/native_func_thread_id.result @@ -0,0 +1,95 @@ +# +# MDEV-19629 Implement MySQL 8.0 native functions: +# PS_THREAD_ID() and PS_CURRENT_THREAD_ID() +# +# Function plugins of the performance schema library +SELECT PLUGIN_NAME, PLUGIN_STATUS, PLUGIN_TYPE, PLUGIN_LIBRARY +FROM information_schema.PLUGINS +WHERE PLUGIN_NAME IN ('ps_thread_id', 'ps_current_thread_id') +ORDER BY PLUGIN_NAME; +PLUGIN_NAME PLUGIN_STATUS PLUGIN_TYPE PLUGIN_LIBRARY +ps_current_thread_id ACTIVE FUNCTION NULL +ps_thread_id ACTIVE FUNCTION NULL +SELECT PS_CURRENT_THREAD_ID() = @tid; +PS_CURRENT_THREAD_ID() = @tid +1 +SELECT PS_THREAD_ID(CONNECTION_ID()) = @tid; +PS_THREAD_ID(CONNECTION_ID()) = @tid +1 +SELECT PS_THREAD_ID(CONNECTION_ID()) = PS_CURRENT_THREAD_ID(); +PS_THREAD_ID(CONNECTION_ID()) = PS_CURRENT_THREAD_ID() +1 +# Another connection +connect con1,localhost,root,,; +connection default; +con1_matches +1 +# Same answer as performance_schema.threads for every connection +SELECT COUNT(*) >= 2 AS have_two, +COUNT(*) = SUM(PS_THREAD_ID(PROCESSLIST_ID) <=> THREAD_ID) AS all_match +FROM performance_schema.threads +WHERE PROCESSLIST_ID IS NOT NULL; +have_two all_match +1 1 +disconnect con1; +gone_after_disconnect +1 +# NULL for unknown, NULL, zero, negative and non-integer arguments +SELECT PS_THREAD_ID(234623462376), PS_THREAD_ID(NULL), PS_THREAD_ID(0), +PS_THREAD_ID(-1), PS_THREAD_ID(1.9), PS_THREAD_ID(18446744073709551615); +PS_THREAD_ID(234623462376) PS_THREAD_ID(NULL) PS_THREAD_ID(0) PS_THREAD_ID(-1) PS_THREAD_ID(1.9) PS_THREAD_ID(18446744073709551615) +NULL NULL NULL NULL NULL NULL +SELECT PS_THREAD_ID('abc'); +PS_THREAD_ID('abc') +NULL +Warnings: +Warning 1292 Truncated incorrect INTEGER value: 'abc' +# Prepared statements +PREPARE s FROM 'SELECT PS_THREAD_ID(?) = PS_CURRENT_THREAD_ID()'; +SET @cid= CONNECTION_ID(); +EXECUTE s USING @cid; +PS_THREAD_ID(?) = PS_CURRENT_THREAD_ID() +1 +EXECUTE s USING NULL; +PS_THREAD_ID(?) = PS_CURRENT_THREAD_ID() +NULL +DEALLOCATE PREPARE s; +# Result type +CREATE TABLE t1 AS SELECT PS_THREAD_ID(1) AS a, PS_CURRENT_THREAD_ID() AS b; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(21) unsigned DEFAULT NULL, + `b` bigint(21) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci +DROP TABLE t1; +# Wrong argument count +SELECT PS_THREAD_ID(); +ERROR 42000: Incorrect parameter count in the call to native function 'PS_THREAD_ID' +SELECT PS_THREAD_ID(1, 2); +ERROR 42000: Incorrect parameter count in the call to native function 'PS_THREAD_ID' +SELECT PS_CURRENT_THREAD_ID(1); +ERROR 42000: Incorrect parameter count in the call to native function 'PS_CURRENT_THREAD_ID' +# Allowed in VIRTUAL columns, not in PERSISTENT ones (like CONNECTION_ID()) +CREATE TABLE t1 (a INT, b BIGINT UNSIGNED AS (PS_THREAD_ID(a)) VIRTUAL, +c BIGINT UNSIGNED AS (PS_CURRENT_THREAD_ID()) VIRTUAL); +DROP TABLE t1; +CREATE TABLE t1 (a INT, b BIGINT UNSIGNED AS (PS_THREAD_ID(a)) PERSISTENT); +ERROR HY000: Function or expression 'ps_thread_id()' cannot be used in the GENERATED ALWAYS AS clause of `b` +CREATE TABLE t1 (a INT, b BIGINT UNSIGNED AS (PS_CURRENT_THREAD_ID()) PERSISTENT); +ERROR HY000: Function or expression 'ps_current_thread_id()' cannot be used in the GENERATED ALWAYS AS clause of `b` +# sys.ps_thread_id() now uses the native functions +SELECT sys.ps_thread_id(NULL) = PS_CURRENT_THREAD_ID(); +sys.ps_thread_id(NULL) = PS_CURRENT_THREAD_ID() +1 +Warnings: +Note 1585 This function 'ps_thread_id' has the same name as a native function +SELECT sys.ps_thread_id(CONNECTION_ID()) = PS_CURRENT_THREAD_ID(); +sys.ps_thread_id(CONNECTION_ID()) = PS_CURRENT_THREAD_ID() +1 +SELECT sys.ps_thread_id(234623462376); +sys.ps_thread_id(234623462376) +NULL +# +# End of 13.2 tests +# diff --git a/mysql-test/suite/perfschema/r/native_func_thread_id_off.result b/mysql-test/suite/perfschema/r/native_func_thread_id_off.result new file mode 100644 index 0000000000000..e93186232a708 --- /dev/null +++ b/mysql-test/suite/perfschema/r/native_func_thread_id_off.result @@ -0,0 +1,22 @@ +# +# MDEV-19629 PS_THREAD_ID() and PS_CURRENT_THREAD_ID() with the +# Performance Schema disabled +# +SELECT @@performance_schema; +@@performance_schema +0 +SELECT PS_CURRENT_THREAD_ID(); +ERROR HY000: 'ps_current_thread_id': The Performance Schema is not enabled. +SELECT PS_THREAD_ID(CONNECTION_ID()); +ERROR HY000: 'ps_thread_id': The Performance Schema is not enabled. +SELECT PS_THREAD_ID(NULL); +ERROR HY000: 'ps_thread_id': The Performance Schema is not enabled. +# sys.ps_thread_id() keeps returning NULL +SELECT sys.ps_thread_id(NULL), sys.ps_thread_id(CONNECTION_ID()); +sys.ps_thread_id(NULL) sys.ps_thread_id(CONNECTION_ID()) +NULL NULL +Warnings: +Note 1585 This function 'ps_thread_id' has the same name as a native function +# +# End of 13.2 tests +# diff --git a/mysql-test/suite/perfschema/t/native_func_thread_id.test b/mysql-test/suite/perfschema/t/native_func_thread_id.test new file mode 100644 index 0000000000000..d9f885bff10f0 --- /dev/null +++ b/mysql-test/suite/perfschema/t/native_func_thread_id.test @@ -0,0 +1,97 @@ +--source include/not_embedded.inc +--source include/have_perfschema.inc + +--echo # +--echo # MDEV-19629 Implement MySQL 8.0 native functions: +--echo # PS_THREAD_ID() and PS_CURRENT_THREAD_ID() +--echo # + +--echo # Function plugins of the performance schema library +SELECT PLUGIN_NAME, PLUGIN_STATUS, PLUGIN_TYPE, PLUGIN_LIBRARY +FROM information_schema.PLUGINS +WHERE PLUGIN_NAME IN ('ps_thread_id', 'ps_current_thread_id') +ORDER BY PLUGIN_NAME; + +let $tid= `SELECT THREAD_ID FROM performance_schema.threads + WHERE PROCESSLIST_ID = CONNECTION_ID()`; +--disable_query_log +eval SET @tid= $tid; +--enable_query_log + +SELECT PS_CURRENT_THREAD_ID() = @tid; +SELECT PS_THREAD_ID(CONNECTION_ID()) = @tid; +SELECT PS_THREAD_ID(CONNECTION_ID()) = PS_CURRENT_THREAD_ID(); + +--echo # Another connection +connect (con1,localhost,root,,); +let $con1_id= `SELECT CONNECTION_ID()`; +let $con1_tid= `SELECT PS_CURRENT_THREAD_ID()`; +connection default; +--disable_query_log +eval SELECT PS_THREAD_ID($con1_id) = $con1_tid AS con1_matches; +--enable_query_log + +--echo # Same answer as performance_schema.threads for every connection +SELECT COUNT(*) >= 2 AS have_two, + COUNT(*) = SUM(PS_THREAD_ID(PROCESSLIST_ID) <=> THREAD_ID) AS all_match +FROM performance_schema.threads +WHERE PROCESSLIST_ID IS NOT NULL; + +disconnect con1; +--disable_query_log +let $wait_condition= SELECT COUNT(*) = 0 FROM information_schema.processlist + WHERE id = $con1_id; +--source include/wait_condition.inc +--disable_query_log +eval SELECT PS_THREAD_ID($con1_id) IS NULL AS gone_after_disconnect; +--enable_query_log + +--echo # NULL for unknown, NULL, zero, negative and non-integer arguments +SELECT PS_THREAD_ID(234623462376), PS_THREAD_ID(NULL), PS_THREAD_ID(0), + PS_THREAD_ID(-1), PS_THREAD_ID(1.9), PS_THREAD_ID(18446744073709551615); +SELECT PS_THREAD_ID('abc'); + +--echo # Prepared statements +PREPARE s FROM 'SELECT PS_THREAD_ID(?) = PS_CURRENT_THREAD_ID()'; +SET @cid= CONNECTION_ID(); +EXECUTE s USING @cid; +EXECUTE s USING NULL; +DEALLOCATE PREPARE s; + +--echo # Result type +CREATE TABLE t1 AS SELECT PS_THREAD_ID(1) AS a, PS_CURRENT_THREAD_ID() AS b; +SHOW CREATE TABLE t1; +DROP TABLE t1; + +--echo # Wrong argument count +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +SELECT PS_THREAD_ID(); +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +SELECT PS_THREAD_ID(1, 2); +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +SELECT PS_CURRENT_THREAD_ID(1); + +--echo # Allowed in VIRTUAL columns, not in PERSISTENT ones (like CONNECTION_ID()) +CREATE TABLE t1 (a INT, b BIGINT UNSIGNED AS (PS_THREAD_ID(a)) VIRTUAL, + c BIGINT UNSIGNED AS (PS_CURRENT_THREAD_ID()) VIRTUAL); +DROP TABLE t1; +--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED +CREATE TABLE t1 (a INT, b BIGINT UNSIGNED AS (PS_THREAD_ID(a)) PERSISTENT); +--error ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED +CREATE TABLE t1 (a INT, b BIGINT UNSIGNED AS (PS_CURRENT_THREAD_ID()) PERSISTENT); + +--echo # sys.ps_thread_id() now uses the native functions +# Various protocols display ER_NATIVE_FCT_NAME_COLLISION very differently. +--disable_ps_protocol +--disable_cursor_protocol +--disable_view_protocol +SELECT sys.ps_thread_id(NULL) = PS_CURRENT_THREAD_ID(); +SELECT sys.ps_thread_id(CONNECTION_ID()) = PS_CURRENT_THREAD_ID(); +SELECT sys.ps_thread_id(234623462376); +--enable_view_protocol +--enable_cursor_protocol +--enable_ps_protocol + +--echo # +--echo # End of 13.2 tests +--echo # diff --git a/mysql-test/suite/perfschema/t/native_func_thread_id_off-master.opt b/mysql-test/suite/perfschema/t/native_func_thread_id_off-master.opt new file mode 100644 index 0000000000000..f051d8cb685ff --- /dev/null +++ b/mysql-test/suite/perfschema/t/native_func_thread_id_off-master.opt @@ -0,0 +1 @@ +--loose-performance-schema=OFF diff --git a/mysql-test/suite/perfschema/t/native_func_thread_id_off.test b/mysql-test/suite/perfschema/t/native_func_thread_id_off.test new file mode 100644 index 0000000000000..96d0f5941dd4f --- /dev/null +++ b/mysql-test/suite/perfschema/t/native_func_thread_id_off.test @@ -0,0 +1,30 @@ +--source include/not_embedded.inc +--source include/have_perfschema.inc + +--echo # +--echo # MDEV-19629 PS_THREAD_ID() and PS_CURRENT_THREAD_ID() with the +--echo # Performance Schema disabled +--echo # + +SELECT @@performance_schema; + +--error ER_WRONG_PERFSCHEMA_USAGE +SELECT PS_CURRENT_THREAD_ID(); +--error ER_WRONG_PERFSCHEMA_USAGE +SELECT PS_THREAD_ID(CONNECTION_ID()); +--error ER_WRONG_PERFSCHEMA_USAGE +SELECT PS_THREAD_ID(NULL); + +--echo # sys.ps_thread_id() keeps returning NULL +# Various protocols display ER_NATIVE_FCT_NAME_COLLISION very differently. +--disable_ps_protocol +--disable_cursor_protocol +--disable_view_protocol +SELECT sys.ps_thread_id(NULL), sys.ps_thread_id(CONNECTION_ID()); +--enable_view_protocol +--enable_cursor_protocol +--enable_ps_protocol + +--echo # +--echo # End of 13.2 tests +--echo # diff --git a/mysql-test/suite/sysschema/r/fn_ps_thread_account.result b/mysql-test/suite/sysschema/r/fn_ps_thread_account.result index 67f7e8fac86b8..351c640450c31 100644 --- a/mysql-test/suite/sysschema/r/fn_ps_thread_account.result +++ b/mysql-test/suite/sysschema/r/fn_ps_thread_account.result @@ -7,3 +7,5 @@ NULL SELECT sys.ps_thread_account(sys.ps_thread_id(NULL)); sys.ps_thread_account(sys.ps_thread_id(NULL)) root@localhost +Warnings: +Note 1585 This function 'ps_thread_id' has the same name as a native function diff --git a/mysql-test/suite/sysschema/r/fn_ps_thread_id.result b/mysql-test/suite/sysschema/r/fn_ps_thread_id.result index 8d1677af6b13e..dd86a8e28d02a 100644 --- a/mysql-test/suite/sysschema/r/fn_ps_thread_id.result +++ b/mysql-test/suite/sysschema/r/fn_ps_thread_id.result @@ -1,6 +1,8 @@ SELECT sys.ps_thread_id(234623462376); sys.ps_thread_id(234623462376) NULL +Warnings: +Note 1585 This function 'ps_thread_id' has the same name as a native function SELECT sys.ps_thread_id(CONNECTION_ID()) = @ps_thread_id; sys.ps_thread_id(CONNECTION_ID()) = @ps_thread_id 1 diff --git a/mysql-test/suite/sysschema/r/fn_ps_thread_trx_info.result b/mysql-test/suite/sysschema/r/fn_ps_thread_trx_info.result index 7c46c9ba9806b..8b3f1f9f82b92 100644 --- a/mysql-test/suite/sysschema/r/fn_ps_thread_trx_info.result +++ b/mysql-test/suite/sysschema/r/fn_ps_thread_trx_info.result @@ -4,6 +4,8 @@ NULL SELECT JSON_VALID(sys.ps_thread_trx_info(sys.ps_thread_id(NULL))); JSON_VALID(sys.ps_thread_trx_info(sys.ps_thread_id(NULL))) 1 +Warnings: +Note 1585 This function 'ps_thread_id' has the same name as a native function CREATE DATABASE trx; CREATE TABLE trx.info (id INT PRIMARY KEY, info VARCHAR(20)); connect con1,localhost,root,,; diff --git a/mysql-test/suite/sysschema/r/pr_ps_trace_statement_digest.result b/mysql-test/suite/sysschema/r/pr_ps_trace_statement_digest.result index c0b9372dc4449..59a5cd828e5f4 100644 --- a/mysql-test/suite/sysschema/r/pr_ps_trace_statement_digest.result +++ b/mysql-test/suite/sysschema/r/pr_ps_trace_statement_digest.result @@ -1,5 +1,7 @@ use test; SET @threadid = sys.ps_thread_id(NULL); +Warnings: +Note 1585 This function 'ps_thread_id' has the same name as a native function CREATE TABLE t1 (id INT PRIMARY KEY, val int) ENGINE=MEMORY; INSERT INTO test.t1 VALUES (1, 9); SET @digest.insert = (SELECT DIGEST FROM performance_schema.events_statements_history WHERE THREAD_ID = @threadid AND SQL_TEXT LIKE 'INSERT INTO test.t1 VALUES (1, 9)'); diff --git a/mysql-test/suite/sysschema/t/fn_ps_thread_account.test b/mysql-test/suite/sysschema/t/fn_ps_thread_account.test index 77b6241add0b5..3f64e6a049cd3 100644 --- a/mysql-test/suite/sysschema/t/fn_ps_thread_account.test +++ b/mysql-test/suite/sysschema/t/fn_ps_thread_account.test @@ -8,4 +8,11 @@ SELECT sys.ps_thread_account(234623462376); # Check result +# Various protocols display ER_NATIVE_FCT_NAME_COLLISION very differently. +--disable_ps_protocol +--disable_cursor_protocol +--disable_view_protocol SELECT sys.ps_thread_account(sys.ps_thread_id(NULL)); +--enable_view_protocol +--enable_cursor_protocol +--enable_ps_protocol diff --git a/mysql-test/suite/sysschema/t/fn_ps_thread_id.test b/mysql-test/suite/sysschema/t/fn_ps_thread_id.test index ca8b4fb437c64..950724b162683 100644 --- a/mysql-test/suite/sysschema/t/fn_ps_thread_id.test +++ b/mysql-test/suite/sysschema/t/fn_ps_thread_id.test @@ -1,4 +1,11 @@ -- source include/not_embedded.inc + +# Various protocols display ER_NATIVE_FCT_NAME_COLLISION very differently. +# Disable all of them. + +--disable_ps_protocol +--disable_cursor_protocol +--disable_view_protocol # Tests for sys schema # Verify the sys.ps_thread_id() function perfoms as expected @@ -16,3 +23,7 @@ SELECT sys.ps_thread_id(CONNECTION_ID()) = @ps_thread_id; # Passing NULL should also return the current connection thread id SELECT sys.ps_thread_id(NULL) = @ps_thread_id; + +--enable_view_protocol +--enable_cursor_protocol +--enable_ps_protocol diff --git a/scripts/sys_schema/functions/ps_thread_id.sql b/scripts/sys_schema/functions/ps_thread_id.sql index 31517759b2bfc..1e28afbdb6f1c 100644 --- a/scripts/sys_schema/functions/ps_thread_id.sql +++ b/scripts/sys_schema/functions/ps_thread_id.sql @@ -55,10 +55,14 @@ CREATE OR REPLACE DEFINER='mariadb.sys'@'localhost' FUNCTION ps_thread_id ( NOT DETERMINISTIC READS SQL DATA BEGIN - RETURN (SELECT THREAD_ID - FROM `performance_schema`.`threads` - WHERE PROCESSLIST_ID = IFNULL(in_connection_id, CONNECTION_ID()) - ); + -- The native functions raise an error when the Performance Schema + -- is disabled; keep returning NULL, as this function always did. + IF NOT @@performance_schema THEN + RETURN NULL; + END IF; + RETURN IF(in_connection_id IS NULL, + PS_CURRENT_THREAD_ID(), + PS_THREAD_ID(in_connection_id)); END$$ DELIMITER ; diff --git a/storage/perfschema/CMakeLists.txt b/storage/perfschema/CMakeLists.txt index 541ae0462db06..70309e0e0600b 100644 --- a/storage/perfschema/CMakeLists.txt +++ b/storage/perfschema/CMakeLists.txt @@ -70,6 +70,7 @@ pfs_instr.h pfs_instr_class.h pfs_lock.h pfs_memory.h +pfs_native_func.h pfs_server.h pfs_setup_actor.h pfs_setup_object.h @@ -187,6 +188,7 @@ pfs_host.cc pfs_instr.cc pfs_instr_class.cc pfs_memory.cc +pfs_native_func.cc pfs_server.cc pfs_setup_actor.cc pfs_setup_object.cc diff --git a/storage/perfschema/ha_perfschema.cc b/storage/perfschema/ha_perfschema.cc index ce94e268c0236..3cceb4938f84d 100644 --- a/storage/perfschema/ha_perfschema.cc +++ b/storage/perfschema/ha_perfschema.cc @@ -39,6 +39,8 @@ #include "pfs_program.h" #include "pfs_prepared_stmt.h" #include "pfs_buffer_container.h" +#include "pfs_native_func.h" +#include handlerton *pfs_hton= NULL; @@ -230,6 +232,36 @@ maria_declare_plugin(perfschema) NULL, "5.7.31", MariaDB_PLUGIN_MATURITY_STABLE +}, +{ + MariaDB_FUNCTION_PLUGIN, + &pfs_function_ps_thread_id, + "ps_thread_id", + "Shubham Chandrawanshi", + "Function PS_THREAD_ID()", + PLUGIN_LICENSE_GPL, + NULL, + NULL, + 0x0100, + NULL, + NULL, + "1.0", + MariaDB_PLUGIN_MATURITY_STABLE +}, +{ + MariaDB_FUNCTION_PLUGIN, + &pfs_function_ps_current_thread_id, + "ps_current_thread_id", + "Shubham Chandrawanshi", + "Function PS_CURRENT_THREAD_ID()", + PLUGIN_LICENSE_GPL, + NULL, + NULL, + 0x0100, + NULL, + NULL, + "1.0", + MariaDB_PLUGIN_MATURITY_STABLE } maria_declare_plugin_end; diff --git a/storage/perfschema/pfs_native_func.cc b/storage/perfschema/pfs_native_func.cc new file mode 100644 index 0000000000000..f87e695083e84 --- /dev/null +++ b/storage/perfschema/pfs_native_func.cc @@ -0,0 +1,212 @@ +/* Copyright (c) 2026, Shubham Chandrawanshi + + 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 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, + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +/** + @file storage/perfschema/pfs_native_func.cc + Function plugins PS_THREAD_ID() and PS_CURRENT_THREAD_ID() + (MySQL 8.0.16 compatible). + + The functions are declared by the performance schema plugin library, + so they read PFS_thread directly instead of going through the PSI + interface, and they do not exist in a server built without the + performance schema. +*/ + +#include "mariadb.h" +#include "sql_class.h" +#include +#include "pfs_native_func.h" +#include "pfs_global.h" +#include "pfs_server.h" +#include "pfs_instr.h" +#include "pfs_buffer_container.h" + + +/** + Raise an error unless the performance schema is running, + like MySQL does for PS_THREAD_ID() and PS_CURRENT_THREAD_ID(). +*/ +static bool pfs_func_check_enabled(const char *func_name) +{ + if (pfs_param.m_enabled && pfs_initialized) + return false; + my_printf_error(ER_WRONG_PERFSCHEMA_USAGE, + "'%s': The Performance Schema is not enabled.", MYF(0), + func_name); + return true; +} + + +/** + performance_schema.threads.THREAD_ID of the thread with the given + processlist id, or 0 if there is no such instrumented thread. +*/ +static ulonglong pfs_thread_id_by_processlist_id(ulonglong processlist_id) +{ + PFS_thread_iterator it= global_thread_container.iterate(); + for (PFS_thread *pfs= it.scan_next(); pfs != NULL; pfs= it.scan_next()) + { + pfs_optimistic_state lock; + /* Protect this reader against thread termination */ + pfs->m_lock.begin_optimistic_lock(&lock); + ulonglong pfs_processlist_id= pfs->m_processlist_id; + ulonglong thread_id= pfs->m_thread_internal_id; + if (pfs_processlist_id == processlist_id && + pfs->m_lock.end_optimistic_lock(&lock)) + return thread_id; + } + return 0; +} + + +/** + PS_CURRENT_THREAD_ID(): THREAD_ID of the current connection. +*/ +class Item_func_ps_current_thread_id :public Item_longlong_func +{ +public: + Item_func_ps_current_thread_id(THD *thd): Item_longlong_func(thd) {} + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("ps_current_thread_id") }; + return name; + } + bool fix_length_and_dec(THD *thd) override + { + max_length= 21; + unsigned_flag= 1; + set_maybe_null(); + return false; + } + bool fix_fields(THD *thd, Item **ref) override + { + if (Item_longlong_func::fix_fields(thd, ref)) + return true; + thd->used|= THD::THREAD_SPECIFIC_USED; + return false; + } + longlong val_int() override + { + DBUG_ASSERT(fixed()); + null_value= true; + if (pfs_func_check_enabled(func_name())) + return 0; + PFS_thread *pfs= PFS_thread::get_current_thread(); + if (pfs == NULL || pfs->m_thread_internal_id == 0) + return 0; + null_value= false; + return (longlong) pfs->m_thread_internal_id; + } + bool check_vcol_func_processor(void *arg) override + { + return mark_unsupported_function(func_name(), "()", arg, + VCOL_SESSION_FUNC); + } +protected: + Item *shallow_copy(THD *thd) const override + { return get_item_copy(thd, this); } +}; + + +/** + PS_THREAD_ID(connection_id): THREAD_ID of the connection, or NULL. +*/ +class Item_func_ps_thread_id :public Item_longlong_func +{ +public: + Item_func_ps_thread_id(THD *thd, Item *a): Item_longlong_func(thd, a) {} + LEX_CSTRING func_name_cstring() const override + { + static LEX_CSTRING name= {STRING_WITH_LEN("ps_thread_id") }; + return name; + } + bool fix_length_and_dec(THD *thd) override + { + max_length= 21; + unsigned_flag= 1; + set_maybe_null(); + return false; + } + longlong val_int() override + { + DBUG_ASSERT(fixed()); + null_value= true; + if (pfs_func_check_enabled(func_name())) + return 0; + longlong processlist_id= args[0]->val_int(); + /* Like MySQL: NULL, non-integer, zero and negative arguments give NULL */ + if (args[0]->null_value || args[0]->cmp_type() != INT_RESULT || + processlist_id == 0 || + (!args[0]->unsigned_flag && processlist_id < 0)) + return 0; + ulonglong thread_id= + pfs_thread_id_by_processlist_id((ulonglong) processlist_id); + if (thread_id == 0) + return 0; + null_value= false; + return (longlong) thread_id; + } + /* The thread behind a connection id can end at any time */ + bool const_item() const override { return false; } + table_map used_tables() const override + { return Item_longlong_func::used_tables() | RAND_TABLE_BIT; } + bool check_vcol_func_processor(void *arg) override + { + return mark_unsupported_function(func_name(), "()", arg, + VCOL_NON_DETERMINISTIC); + } +protected: + Item *shallow_copy(THD *thd) const override + { return get_item_copy(thd, this); } +}; + + +class Create_func_ps_current_thread_id : public Create_func_arg0 +{ +public: + Item *create_builder(THD *thd) override + { + thd->lex->safe_to_cache_query= 0; + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + return new (thd->mem_root) Item_func_ps_current_thread_id(thd); + } + static Create_func_ps_current_thread_id s_singleton; +protected: + Create_func_ps_current_thread_id() = default; +}; + + +class Create_func_ps_thread_id : public Create_func_arg1 +{ +public: + Item *create_1_arg(THD *thd, Item *arg1) override + { + thd->lex->safe_to_cache_query= 0; + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + return new (thd->mem_root) Item_func_ps_thread_id(thd, arg1); + } + static Create_func_ps_thread_id s_singleton; +protected: + Create_func_ps_thread_id() = default; +}; + + +Create_func_ps_current_thread_id Create_func_ps_current_thread_id::s_singleton; +Create_func_ps_thread_id Create_func_ps_thread_id::s_singleton; + +Plugin_function pfs_function_ps_current_thread_id( + &Create_func_ps_current_thread_id::s_singleton); +Plugin_function + pfs_function_ps_thread_id(&Create_func_ps_thread_id::s_singleton); diff --git a/storage/perfschema/pfs_native_func.h b/storage/perfschema/pfs_native_func.h new file mode 100644 index 0000000000000..cc630303fede1 --- /dev/null +++ b/storage/perfschema/pfs_native_func.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2026, Shubham Chandrawanshi + + 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 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, + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + +#ifndef PFS_NATIVE_FUNC_H +#define PFS_NATIVE_FUNC_H + +/** + @file storage/perfschema/pfs_native_func.h + Function plugins PS_THREAD_ID() and PS_CURRENT_THREAD_ID(). +*/ + +class Plugin_function; + +extern Plugin_function pfs_function_ps_thread_id; +extern Plugin_function pfs_function_ps_current_thread_id; + +#endif