Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
SET PERSIST vsql_allow_preview_extensions = ON;
INSTALL EXTENSION vsql_slow_query_log;
SET GLOBAL vsql_slow_query_log.threshold_ms = 100;
SET GLOBAL vsql_slow_query_log.log_file = 'MYSQLTEST_VARDIR/tmp/vsql_slow.log';
SET GLOBAL vsql_slow_query_log.enabled = ON;
# Slow query (>= 100ms) should be logged
SELECT SLEEP(0.3) AS slow_marker;
slow_marker
0
# Fast query should NOT be logged
SELECT 1 AS fast_marker;
fast_marker
1
SET GLOBAL vsql_slow_query_log.enabled = OFF;
# While disabled, even slow queries should NOT be logged
SELECT SLEEP(0.3) AS disabled_marker;
disabled_marker
0
# Exactly one SLEEP entry (from the enabled period) is in the log
1
# The fast query did not get logged
0
# The disabled query did not get logged
0
# When log_file points at a non-existent directory, the hook's fopen
# fails and reports via result.error_msg(). The server logs that as a
# Warning in the error log.
call mtr.add_suppression("Query hook error in extension 'vsql_slow_query_log'");
SET GLOBAL vsql_slow_query_log.log_file = '/tmp/vsql_slow_query_log_uaf_test_dir_xyz/slow.log';
SET GLOBAL vsql_slow_query_log.enabled = ON;
SELECT SLEEP(0.3) AS unwritable_marker;
unwritable_marker
0
SET GLOBAL vsql_slow_query_log.enabled = OFF;
# Exactly one warning containing the unique path token
1
UNINSTALL EXTENSION vsql_slow_query_log;
SET PERSIST vsql_allow_preview_extensions = OFF;
RESET PERSIST vsql_allow_preview_extensions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
SET PERSIST vsql_allow_preview_extensions = ON;
INSTALL EXTENSION vsql_slow_query_log;
SET GLOBAL vsql_slow_query_log.threshold_ms = 0;
SET GLOBAL vsql_slow_query_log.log_file = 'MYSQLTEST_VARDIR/tmp/vsql_slow_ddl.log';
SET GLOBAL vsql_slow_query_log.enabled = ON;
# DDL: CREATE TABLE
CREATE TABLE vsql_hook_ddl_test (id INT PRIMARY KEY, val VARCHAR(32));
# DDL: ALTER TABLE
ALTER TABLE vsql_hook_ddl_test ADD COLUMN extra INT;
# DDL: DROP TABLE
DROP TABLE vsql_hook_ddl_test;
# GRANT
CREATE USER IF NOT EXISTS 'vsql_hook_test_user'@'localhost';
GRANT SELECT ON *.* TO 'vsql_hook_test_user'@'localhost';
# REVOKE
REVOKE SELECT ON *.* FROM 'vsql_hook_test_user'@'localhost';
DROP USER 'vsql_hook_test_user'@'localhost';
SET GLOBAL vsql_slow_query_log.enabled = OFF;
# CREATE TABLE was logged
1
# ALTER TABLE was logged
1
# DROP TABLE was logged
1
# GRANT was logged
1
# REVOKE was logged
1
UNINSTALL EXTENSION vsql_slow_query_log;
SET PERSIST vsql_allow_preview_extensions = OFF;
RESET PERSIST vsql_allow_preview_extensions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Test the vsql_slow_query_log extension.
#
# Verifies:
# - INSTALL EXTENSION registers the slow query log hook
# - Queries exceeding threshold_ms are written to log_file
# - Queries under threshold_ms are NOT written
# - Disabling 'enabled' silences the hook
# - When log_file cannot be opened, the hook reports an error via
# QueryHookResult::error_msg() and the server logs it
# - UNINSTALL EXTENSION removes the hook cleanly

# Unique token for the unwritable-path error message so the grep can't
# accidentally match an unrelated line in a long error log.
--let $error_log = $MYSQLTEST_VARDIR/log/mysqld.1.err
--let $bad_log_path = /tmp/vsql_slow_query_log_uaf_test_dir_xyz/slow.log
--let $slow_log = $MYSQLTEST_VARDIR/tmp/vsql_slow.log

SET PERSIST vsql_allow_preview_extensions = ON;

# Clean any stale log file from a previous run.
--error 0,1
--remove_file $slow_log

INSTALL EXTENSION vsql_slow_query_log;

SET GLOBAL vsql_slow_query_log.threshold_ms = 100;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
eval SET GLOBAL vsql_slow_query_log.log_file = '$slow_log';
SET GLOBAL vsql_slow_query_log.enabled = ON;

--echo # Slow query (>= 100ms) should be logged
SELECT SLEEP(0.3) AS slow_marker;

--echo # Fast query should NOT be logged
SELECT 1 AS fast_marker;

SET GLOBAL vsql_slow_query_log.enabled = OFF;

--echo # While disabled, even slow queries should NOT be logged
SELECT SLEEP(0.3) AS disabled_marker;

# grep -c exits 1 when it finds zero matches (still prints "0"). Wrap in
# sh -c "...; true" so mysqltest sees a success exit code regardless.
--echo # Exactly one SLEEP entry (from the enabled period) is in the log
--exec sh -c "grep -c 'slow_marker' $slow_log; true"

--echo # The fast query did not get logged
--exec sh -c "grep -c 'fast_marker' $slow_log; true"

--echo # The disabled query did not get logged
--exec sh -c "grep -c 'disabled_marker' $slow_log; true"

--echo # When log_file points at a non-existent directory, the hook's fopen
--echo # fails and reports via result.error_msg(). The server logs that as a
--echo # Warning in the error log.
call mtr.add_suppression("Query hook error in extension 'vsql_slow_query_log'");
eval SET GLOBAL vsql_slow_query_log.log_file = '$bad_log_path';
SET GLOBAL vsql_slow_query_log.enabled = ON;
SELECT SLEEP(0.3) AS unwritable_marker;
SET GLOBAL vsql_slow_query_log.enabled = OFF;

--echo # Exactly one warning containing the unique path token
--exec sh -c "grep -c 'vsql_slow_query_log_uaf_test_dir_xyz' $error_log; true"

UNINSTALL EXTENSION vsql_slow_query_log;

SET PERSIST vsql_allow_preview_extensions = OFF;
RESET PERSIST vsql_allow_preview_extensions;

--remove_file $slow_log
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Verify that the POSTEXECUTE query hook fires for DDL and GRANT/REVOKE
# statements, not only for DML. Uses threshold_ms=0 so every statement is
# logged regardless of duration.

--let $slow_log = $MYSQLTEST_VARDIR/tmp/vsql_slow_ddl.log

SET PERSIST vsql_allow_preview_extensions = ON;

--error 0,1
--remove_file $slow_log

INSTALL EXTENSION vsql_slow_query_log;

SET GLOBAL vsql_slow_query_log.threshold_ms = 0;
--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
eval SET GLOBAL vsql_slow_query_log.log_file = '$slow_log';
SET GLOBAL vsql_slow_query_log.enabled = ON;

--echo # DDL: CREATE TABLE
CREATE TABLE vsql_hook_ddl_test (id INT PRIMARY KEY, val VARCHAR(32));

--echo # DDL: ALTER TABLE
ALTER TABLE vsql_hook_ddl_test ADD COLUMN extra INT;

--echo # DDL: DROP TABLE
DROP TABLE vsql_hook_ddl_test;

--echo # GRANT
CREATE USER IF NOT EXISTS 'vsql_hook_test_user'@'localhost';
GRANT SELECT ON *.* TO 'vsql_hook_test_user'@'localhost';

--echo # REVOKE
REVOKE SELECT ON *.* FROM 'vsql_hook_test_user'@'localhost';
DROP USER 'vsql_hook_test_user'@'localhost';

SET GLOBAL vsql_slow_query_log.enabled = OFF;

--echo # CREATE TABLE was logged
--exec sh -c "grep -c 'CREATE TABLE vsql_hook_ddl_test' $slow_log; true"

--echo # ALTER TABLE was logged
--exec sh -c "grep -c 'ALTER TABLE vsql_hook_ddl_test' $slow_log; true"

--echo # DROP TABLE was logged
--exec sh -c "grep -c 'DROP TABLE vsql_hook_ddl_test' $slow_log; true"

--echo # GRANT was logged
--exec sh -c "grep -c 'GRANT SELECT' $slow_log; true"

--echo # REVOKE was logged
--exec sh -c "grep -c 'REVOKE SELECT' $slow_log; true"

UNINSTALL EXTENSION vsql_slow_query_log;

SET PERSIST vsql_allow_preview_extensions = OFF;
RESET PERSIST vsql_allow_preview_extensions;

--remove_file $slow_log
5 changes: 5 additions & 0 deletions sql/sql_audit.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright (c) 2007, 2026, Oracle and/or its affiliates.
Copyright (c) 2026 VillageSQL Contributors

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,
Expand Down Expand Up @@ -63,6 +64,7 @@
#include "sql_string.h"
#include "strxnmov.h"
#include "thr_mutex.h"
#include "villagesql/services/preview/statement_event.h"

namespace {
/**
Expand Down Expand Up @@ -1094,6 +1096,9 @@ int mysql_event_tracking_query_notify(
const char *subclass_name) {
mysql_event_tracking_query_data event;

if (subclass & EVENT_TRACKING_QUERY_STATUS_END)
villagesql::services::on_statement_postexecute(thd);

if (thd->check_event_subscribers(Event_tracking_class::QUERY, subclass, true))
return 0;

Expand Down
9 changes: 9 additions & 0 deletions villagesql/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ IF(NOT WITHOUT_SERVER)
BINARY_BASE ${CMAKE_CURRENT_BINARY_DIR}/examples
INSTALL_DEST ${INSTALL_VEBDIR}
)
vsql_add_extension(
DIR_NAME vsql-slow-query-log
EXT_TARGET vsql_slow_query_log_extension
VEB_NAME vsql_slow_query_log
SOURCE_BASE ${CMAKE_CURRENT_SOURCE_DIR}/examples
BINARY_BASE ${CMAKE_CURRENT_BINARY_DIR}/examples
INSTALL_DEST ${INSTALL_VEBDIR}
INSTALL_COMPONENT ExampleVebs
)

# Test extensions (share a single CMakeLists.txt template)
vsql_add_test_extension(vsql-config-vars-test vsql_config_vars_test)
Expand Down
54 changes: 54 additions & 0 deletions villagesql/examples/vsql-slow-query-log/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright (c) 2026 VillageSQL Contributors
#
# 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

# Quick start (standalone):
# mkdir build && cd build
# cmake .. -DCMAKE_PREFIX_PATH=/path/to/villagesql-extension-sdk
# make
# make install DESTDIR=/path/to/villagesql/lib/veb

cmake_minimum_required(VERSION 3.16)
project(vsql_slow_query_log_extension)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(EXTENSION_NAME "vsql_slow_query_log")

find_package(VillageSQLExtensionFramework REQUIRED)

include_directories(${VillageSQLExtensionFramework_INCLUDE_DIR})

add_library(extension SHARED
src/extension.cc
)

target_compile_options(extension PRIVATE -fPIC)

VEF_CREATE_VEB(
NAME ${EXTENSION_NAME}
LIBRARY_TARGET extension
MANIFEST ${CMAKE_CURRENT_SOURCE_DIR}/manifest.json
)

install(FILES ${VEB_OUTPUT} DESTINATION ".")
7 changes: 7 additions & 0 deletions villagesql/examples/vsql-slow-query-log/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "vsql_slow_query_log",
"version": "0.0.1",
"description": "Logs queries exceeding a configurable threshold to a log file",
"author": "VillageSQL Community",
"license": "GPL-2.0"
}
Loading