From d82128dab3c3bebc55608c49d0715b5a47bb9910 Mon Sep 17 00:00:00 2001 From: james Date: Sat, 3 Aug 2024 15:46:55 +0800 Subject: [PATCH] add gms_debug --- GNUmakefile.in | 1 + build/script/aarch64_lite_list | 3 + build/script/aarch64_opengauss_list | 3 + build/script/loongarch64_lite_list | 3 + build/script/opengauss_release_list_mini | 3 + .../opengauss_release_list_ubuntu_single | 3 + build/script/x86_64_lite_list | 3 + build/script/x86_64_opengauss_list | 3 + contrib/CMakeLists.txt | 3 + contrib/Makefile | 3 +- contrib/gms_debug/CMakeLists.txt | 21 + contrib/gms_debug/Makefile | 29 + contrib/gms_debug/data/dummy.txt | 1 + .../expected/gms_debugger_client1.out | 269 +++++++++ .../expected/gms_debugger_client2.out | 144 +++++ .../expected/gms_debugger_client3.out | 144 +++++ .../expected/gms_debugger_client4.out | 192 +++++++ .../expected/gms_debugger_client5.out | 200 +++++++ .../expected/gms_debugger_client6.out | 177 ++++++ .../expected/gms_debugger_server1.out | 181 ++++++ .../expected/gms_debugger_server2.out | 46 ++ .../expected/gms_debugger_server3.out | 53 ++ .../expected/gms_debugger_server4.out | 114 ++++ .../expected/gms_debugger_server5.out | 122 ++++ .../expected/gms_debugger_server6.out | 114 ++++ contrib/gms_debug/gms_debug--1.0.sql | 387 +++++++++++++ contrib/gms_debug/gms_debug.control | 5 + contrib/gms_debug/gms_debug.cpp | 519 ++++++++++++++++++ contrib/gms_debug/gms_debug.h | 46 ++ .../gms_debug/sql/gms_debugger_client1.sql | 138 +++++ .../gms_debug/sql/gms_debugger_client2.sql | 82 +++ .../gms_debug/sql/gms_debugger_client3.sql | 82 +++ .../gms_debug/sql/gms_debugger_client4.sql | 88 +++ .../gms_debug/sql/gms_debugger_client5.sql | 116 ++++ .../gms_debug/sql/gms_debugger_client6.sql | 99 ++++ .../gms_debug/sql/gms_debugger_client7.sql | 103 ++++ .../gms_debug/sql/gms_debugger_server1.sql | 95 ++++ .../gms_debug/sql/gms_debugger_server2.sql | 32 ++ .../gms_debug/sql/gms_debugger_server3.sql | 39 ++ .../gms_debug/sql/gms_debugger_server4.sql | 97 ++++ .../gms_debug/sql/gms_debugger_server5.sql | 97 ++++ .../gms_debug/sql/gms_debugger_server6.sql | 97 ++++ .../gms_debug/sql/gms_debugger_server7.sql | 29 + src/common/backend/utils/cache/lsyscache.cpp | 20 +- src/common/pl/plpgsql/src/pl_debugger.cpp | 269 ++++++++- src/common/pl/plpgsql/src/pl_debugger_api.cpp | 64 ++- src/common/pl/plpgsql/src/pl_exec.cpp | 4 + src/common/pl/plpgsql/src/pl_handler.cpp | 31 +- .../process/threadpool/knl_session.cpp | 1 + src/include/knl/knl_instance.h | 7 + src/include/knl/knl_session.h | 5 + src/include/utils/lsyscache.h | 1 + src/include/utils/pl_debug.h | 53 ++ src/include/utils/plpgsql.h | 15 + .../regress/expected/gms_debugger_client1.out | 269 +++++++++ .../regress/expected/gms_debugger_client2.out | 144 +++++ .../regress/expected/gms_debugger_client3.out | 144 +++++ .../regress/expected/gms_debugger_client4.out | 192 +++++++ .../regress/expected/gms_debugger_client5.out | 200 +++++++ .../regress/expected/gms_debugger_client6.out | 177 ++++++ .../regress/expected/gms_debugger_server1.out | 181 ++++++ .../regress/expected/gms_debugger_server2.out | 46 ++ .../regress/expected/gms_debugger_server3.out | 53 ++ .../regress/expected/gms_debugger_server4.out | 114 ++++ .../regress/expected/gms_debugger_server5.out | 122 ++++ .../regress/expected/gms_debugger_server6.out | 114 ++++ src/test/regress/parallel_schedule0 | 2 +- src/test/regress/pg_regress.cpp | 2 +- src/test/regress/sql/gms_debugger_client1.sql | 138 +++++ src/test/regress/sql/gms_debugger_client2.sql | 82 +++ src/test/regress/sql/gms_debugger_client3.sql | 82 +++ src/test/regress/sql/gms_debugger_client4.sql | 88 +++ src/test/regress/sql/gms_debugger_client5.sql | 116 ++++ src/test/regress/sql/gms_debugger_client6.sql | 99 ++++ src/test/regress/sql/gms_debugger_client7.sql | 103 ++++ src/test/regress/sql/gms_debugger_server1.sql | 95 ++++ src/test/regress/sql/gms_debugger_server2.sql | 32 ++ src/test/regress/sql/gms_debugger_server3.sql | 39 ++ src/test/regress/sql/gms_debugger_server4.sql | 97 ++++ src/test/regress/sql/gms_debugger_server5.sql | 97 ++++ src/test/regress/sql/gms_debugger_server6.sql | 97 ++++ src/test/regress/sql/gms_debugger_server7.sql | 29 + 82 files changed, 7378 insertions(+), 32 deletions(-) create mode 100644 contrib/gms_debug/CMakeLists.txt create mode 100644 contrib/gms_debug/Makefile create mode 100644 contrib/gms_debug/data/dummy.txt create mode 100644 contrib/gms_debug/expected/gms_debugger_client1.out create mode 100644 contrib/gms_debug/expected/gms_debugger_client2.out create mode 100644 contrib/gms_debug/expected/gms_debugger_client3.out create mode 100644 contrib/gms_debug/expected/gms_debugger_client4.out create mode 100644 contrib/gms_debug/expected/gms_debugger_client5.out create mode 100644 contrib/gms_debug/expected/gms_debugger_client6.out create mode 100644 contrib/gms_debug/expected/gms_debugger_server1.out create mode 100644 contrib/gms_debug/expected/gms_debugger_server2.out create mode 100644 contrib/gms_debug/expected/gms_debugger_server3.out create mode 100644 contrib/gms_debug/expected/gms_debugger_server4.out create mode 100644 contrib/gms_debug/expected/gms_debugger_server5.out create mode 100644 contrib/gms_debug/expected/gms_debugger_server6.out create mode 100644 contrib/gms_debug/gms_debug--1.0.sql create mode 100644 contrib/gms_debug/gms_debug.control create mode 100644 contrib/gms_debug/gms_debug.cpp create mode 100644 contrib/gms_debug/gms_debug.h create mode 100644 contrib/gms_debug/sql/gms_debugger_client1.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_client2.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_client3.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_client4.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_client5.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_client6.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_client7.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_server1.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_server2.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_server3.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_server4.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_server5.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_server6.sql create mode 100644 contrib/gms_debug/sql/gms_debugger_server7.sql create mode 100644 src/include/utils/pl_debug.h create mode 100644 src/test/regress/expected/gms_debugger_client1.out create mode 100644 src/test/regress/expected/gms_debugger_client2.out create mode 100644 src/test/regress/expected/gms_debugger_client3.out create mode 100644 src/test/regress/expected/gms_debugger_client4.out create mode 100644 src/test/regress/expected/gms_debugger_client5.out create mode 100644 src/test/regress/expected/gms_debugger_client6.out create mode 100644 src/test/regress/expected/gms_debugger_server1.out create mode 100644 src/test/regress/expected/gms_debugger_server2.out create mode 100644 src/test/regress/expected/gms_debugger_server3.out create mode 100644 src/test/regress/expected/gms_debugger_server4.out create mode 100644 src/test/regress/expected/gms_debugger_server5.out create mode 100644 src/test/regress/expected/gms_debugger_server6.out create mode 100644 src/test/regress/sql/gms_debugger_client1.sql create mode 100644 src/test/regress/sql/gms_debugger_client2.sql create mode 100644 src/test/regress/sql/gms_debugger_client3.sql create mode 100644 src/test/regress/sql/gms_debugger_client4.sql create mode 100644 src/test/regress/sql/gms_debugger_client5.sql create mode 100644 src/test/regress/sql/gms_debugger_client6.sql create mode 100644 src/test/regress/sql/gms_debugger_client7.sql create mode 100644 src/test/regress/sql/gms_debugger_server1.sql create mode 100644 src/test/regress/sql/gms_debugger_server2.sql create mode 100644 src/test/regress/sql/gms_debugger_server3.sql create mode 100644 src/test/regress/sql/gms_debugger_server4.sql create mode 100644 src/test/regress/sql/gms_debugger_server5.sql create mode 100644 src/test/regress/sql/gms_debugger_server6.sql create mode 100644 src/test/regress/sql/gms_debugger_server7.sql diff --git a/GNUmakefile.in b/GNUmakefile.in index ae841234a..d011c67b5 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -123,6 +123,7 @@ install: fi @if test -d contrib/gms_lob; then $(MAKE) -C contrib/gms_lob $@; fi @if test -d contrib/gms_sql; then $(MAKE) -C contrib/gms_sql $@; fi + @if test -d contrib/gms_debug; then $(MAKE) -C contrib/gms_debug $@; fi +@echo "openGauss installation complete." endif endif diff --git a/build/script/aarch64_lite_list b/build/script/aarch64_lite_list index 1d1730bf8..d2ef26e24 100644 --- a/build/script/aarch64_lite_list +++ b/build/script/aarch64_lite_list @@ -71,6 +71,8 @@ ./share/postgresql/extension/gms_stats.control ./share/postgresql/extension/gms_profiler--1.0.sql ./share/postgresql/extension/gms_profiler.control +./share/postgresql/extension/gms_debug--1.0.sql +./share/postgresql/extension/gms_debug.control ./share/postgresql/extension/gms_lob--1.0.sql ./share/postgresql/extension/gms_lob.control ./share/postgresql/extension/gms_sql--1.0.sql @@ -775,6 +777,7 @@ ./lib/postgresql/gms_lob.so ./lib/postgresql/gms_sql.so ./lib/postgresql/gms_i18n.so +./lib/postgresql/gms_debug.so ./lib/postgresql/gms_inaddr.so ./include/postgresql/server/postgres_ext.h ./include/postgresql/server/pg_config_os.h diff --git a/build/script/aarch64_opengauss_list b/build/script/aarch64_opengauss_list index 1c7d0bcf9..b2f6b0d48 100644 --- a/build/script/aarch64_opengauss_list +++ b/build/script/aarch64_opengauss_list @@ -138,6 +138,8 @@ ./share/postgresql/extension/gms_tcp.control ./share/postgresql/extension/gms_profiler--1.0.sql ./share/postgresql/extension/gms_profiler.control +./share/postgresql/extension/gms_debug--1.0.sql +./share/postgresql/extension/gms_debug.control ./share/postgresql/extension/gms_sql--1.0.sql ./share/postgresql/extension/gms_sql.control ./share/postgresql/extension/gms_xmlgen--1.0.sql @@ -851,6 +853,7 @@ ./lib/postgresql/gms_stats.so ./lib/postgresql/gms_tcp.so ./lib/postgresql/gms_profiler.so +./lib/postgresql/gms_debug.so ./lib/postgresql/gms_sql.so ./lib/postgresql/gms_xmlgen.so ./lib/postgresql/gms_i18n.so diff --git a/build/script/loongarch64_lite_list b/build/script/loongarch64_lite_list index 3fe5776e9..1bbea984c 100644 --- a/build/script/loongarch64_lite_list +++ b/build/script/loongarch64_lite_list @@ -70,6 +70,8 @@ ./share/postgresql/extension/gms_i18n.control ./share/postgresql/extension/gms_utility--1.0.sql ./share/postgresql/extension/gms_utility.control +./share/postgresql/extension/gms_debug--1.0.sql +./share/postgresql/extension/gms_debug.control ./share/postgresql/timezone/GB-Eire ./share/postgresql/timezone/Turkey ./share/postgresql/timezone/Kwajalein @@ -765,6 +767,7 @@ ./lib/postgresql/gms_sql.so ./lib/postgresql/gms_i18n.so ./lib/postgresql/gms_utility.so +./lib/postgresql/gms_debug.so ./include/postgresql/server/postgres_ext.h ./include/postgresql/server/pg_config_os.h ./include/postgresql/server/pgtime.h diff --git a/build/script/opengauss_release_list_mini b/build/script/opengauss_release_list_mini index c8271cb89..cca830ab0 100644 --- a/build/script/opengauss_release_list_mini +++ b/build/script/opengauss_release_list_mini @@ -100,6 +100,8 @@ ./share/postgresql/extension/gms_i18n.control ./share/postgresql/extension/gms_utility--1.0.sql ./share/postgresql/extension/gms_utility.control +./share/postgresql/extension/gms_debug--1.0.sql +./share/postgresql/extension/gms_debug.control ./share/postgresql/timezone/GB-Eire ./share/postgresql/timezone/Turkey ./share/postgresql/timezone/Kwajalein @@ -783,6 +785,7 @@ ./lib/postgresql/gms_sql.so ./lib/postgresql/gms_i18n.so ./lib/postgresql/gms_utility.so +./lib/postgresql/gms_debug.so ./lib/libpljava.so ./lib/libpq.a ./lib/libpq.so diff --git a/build/script/opengauss_release_list_ubuntu_single b/build/script/opengauss_release_list_ubuntu_single index 306981363..6ebe00447 100644 --- a/build/script/opengauss_release_list_ubuntu_single +++ b/build/script/opengauss_release_list_ubuntu_single @@ -123,6 +123,8 @@ ./share/postgresql/extension/gms_utility.control ./share/postgresql/extension/gms_profiler--1.0.sql ./share/postgresql/extension/gms_profiler.control +./share/postgresql/extension/gms_debug--1.0.sql +./share/postgresql/extension/gms_debug.control ./share/postgresql/extension/gms_sql--1.0.sql ./share/postgresql/extension/gms_sql.control ./share/postgresql/extension/gms_tcp--1.0.sql @@ -819,6 +821,7 @@ ./lib/postgresql/gms_stats.so ./lib/postgresql/gms_utility.so ./lib/postgresql/gms_profiler.so +./lib/postgresql/gms_debug.so ./lib/postgresql/gms_sql.so ./lib/postgresql/gms_tcp.so ./lib/postgresql/gms_xmlgen.so diff --git a/build/script/x86_64_lite_list b/build/script/x86_64_lite_list index 037b477e5..f3331a677 100644 --- a/build/script/x86_64_lite_list +++ b/build/script/x86_64_lite_list @@ -79,6 +79,8 @@ ./share/postgresql/extension/gms_i18n.control ./share/postgresql/extension/gms_inaddr--1.0.sql ./share/postgresql/extension/gms_inaddr.control +./share/postgresql/extension/gms_debug--1.0.sql +./share/postgresql/extension/gms_debug.control ./share/postgresql/timezone/GB-Eire ./share/postgresql/timezone/Turkey ./share/postgresql/timezone/Kwajalein @@ -775,6 +777,7 @@ ./lib/postgresql/gms_sql.so ./lib/postgresql/gms_i18n.so ./lib/postgresql/gms_inaddr.so +./lib/postgresql/gms_debug.so ./lib/libxgboost.so ./include/postgresql/server/postgres_ext.h ./include/postgresql/server/pg_config_os.h diff --git a/build/script/x86_64_opengauss_list b/build/script/x86_64_opengauss_list index 4e7775a4e..475203cd1 100644 --- a/build/script/x86_64_opengauss_list +++ b/build/script/x86_64_opengauss_list @@ -138,6 +138,8 @@ ./share/postgresql/extension/gms_tcp.control ./share/postgresql/extension/gms_profiler--1.0.sql ./share/postgresql/extension/gms_profiler.control +./share/postgresql/extension/gms_debug--1.0.sql +./share/postgresql/extension/gms_debug.control ./share/postgresql/extension/gms_sql--1.0.sql ./share/postgresql/extension/gms_sql.control ./share/postgresql/extension/gms_xmlgen--1.0.sql @@ -854,6 +856,7 @@ ./lib/postgresql/gms_sql.so ./lib/postgresql/gms_xmlgen.so ./lib/postgresql/gms_i18n.so +./lib/postgresql/gms_debug.so ./lib/libpljava.so ./lib/libpq.a ./lib/libpq.so diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 3802be761..9817c519c 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -35,6 +35,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/gms_sql ${CMAKE_CURRENT_SOURCE_DIR}/gms_tcp ${CMAKE_CURRENT_SOURCE_DIR}/gms_i18n + ${CMAKE_CURRENT_SOURCE_DIR}/gms_debug + ) if("${USE_LIBXML}" STREQUAL "ON") @@ -65,6 +67,7 @@ add_subdirectory(gms_utility) add_subdirectory(gms_stats) add_subdirectory(gms_sql) add_subdirectory(gms_tcp) +add_subdirectory(gms_debug) if("${ENABLE_MULTIPLE_NODES}" STREQUAL "OFF") add_subdirectory(gc_fdw) endif() diff --git a/contrib/Makefile b/contrib/Makefile index d2693e3e9..942c8609d 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -64,7 +64,8 @@ SUBDIRS = \ gms_stats \ gms_lob \ gms_sql \ - gms_i18n + gms_i18n \ + gms_debug ifeq ($(with_openssl),yes) SUBDIRS += sslinfo diff --git a/contrib/gms_debug/CMakeLists.txt b/contrib/gms_debug/CMakeLists.txt new file mode 100644 index 000000000..6bdaf5f89 --- /dev/null +++ b/contrib/gms_debug/CMakeLists.txt @@ -0,0 +1,21 @@ +#This is the main CMAKE for build all gms_debug. +# gms_debug +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} TGT_gms_debug_SRC) +set(TGT_gms_debug_INC + ${PROJECT_OPENGS_DIR}/contrib/gms_debug + ${PROJECT_OPENGS_DIR}/contrib +) + +set(gms_debug_DEF_OPTIONS ${MACRO_OPTIONS}) +set(gms_debug_COMPILE_OPTIONS ${OPTIMIZE_OPTIONS} ${OS_OPTIONS} ${PROTECT_OPTIONS} ${WARNING_OPTIONS} ${LIB_SECURE_OPTIONS} ${CHECK_OPTIONS}) +set(gms_debug_LINK_OPTIONS ${LIB_LINK_OPTIONS}) +add_shared_libtarget(gms_debug TGT_gms_debug_SRC TGT_gms_debug_INC "${gms_debug_DEF_OPTIONS}" "${gms_debug_COMPILE_OPTIONS}" "${gms_debug_LINK_OPTIONS}") +set_target_properties(gms_debug PROPERTIES PREFIX "") + +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gms_debug.control + DESTINATION share/postgresql/extension/ +) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gms_debug--1.0.sql + DESTINATION share/postgresql/extension/ +) +install(TARGETS gms_debug DESTINATION lib/postgresql) diff --git a/contrib/gms_debug/Makefile b/contrib/gms_debug/Makefile new file mode 100644 index 000000000..758f932f2 --- /dev/null +++ b/contrib/gms_debug/Makefile @@ -0,0 +1,29 @@ +# contrib/gms_debug/Makefile +MODULE_big = gms_debug +OBJS = gms_debug.o + +EXTENSION = gms_debug +DATA = gms_debug--1.0.sql + +exclude_option = -fPIE +override CPPFLAGS := -fstack-protector-strong $(filter-out $(exclude_option),$(CPPFLAGS)) + +REGRESS = gms_debug + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/gms_debug +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +regress_home = $(top_builddir)/src/test/regress +REGRESS_OPTS = -c 0 -d 1 -r 1 -p 25632 --single_node -w --keep_last_data=false \ + --regconf=$(regress_home)/regress.conf \ + --temp-config=$(regress_home)/make_fastcheck_postgresql.conf +include $(top_srcdir)/contrib/contrib-global.mk +endif + +gms_debug.o: gms_debug.cpp + diff --git a/contrib/gms_debug/data/dummy.txt b/contrib/gms_debug/data/dummy.txt new file mode 100644 index 000000000..8e09a4f6c --- /dev/null +++ b/contrib/gms_debug/data/dummy.txt @@ -0,0 +1 @@ +The openGauss regression needs this file to run. diff --git a/contrib/gms_debug/expected/gms_debugger_client1.out b/contrib/gms_debug/expected/gms_debugger_client1.out new file mode 100644 index 000000000..b02d884ff --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_client1.out @@ -0,0 +1,269 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test1; +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_info() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.get_runtime_info(1, run_info); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_breakpoint('test_debug', 0); -- negative +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 15); -- ok +NOTICE: ret= 1 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 17); -- invalid +NOTICE: ret= 2 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 22); -- ok +NOTICE: ret= 3 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 15); -- ok +WARNING: the given line number already contains a valid breakpoint. +CONTEXT: SQL statement "CALL gms_debug.add_breakpoint(proid,line#,breakpoint#,fuzzy,iterations,sts)" +PL/pgSQL function gms_debug.set_breakpoint(gms_debug.program_info,integer,integer,integer) line 8 at SQL statement +SQL statement "CALL gms_debug.set_breakpoint(pro_info,lineno,bkline,1,1)" +PL/pgSQL function gms_breakpoint(text,integer) line 8 at assignment +referenced column: gms_breakpoint +NOTICE: ret= -1 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= -3 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 16 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 19 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 19 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= 3 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 22 +CONTEXT: referenced column: gms_next +NOTICE: reason= 2 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 46 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 46 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_debug.detach_session(); +connection to server was lost diff --git a/contrib/gms_debug/expected/gms_debugger_client2.out b/contrib/gms_debug/expected/gms_debugger_client2.out new file mode 100644 index 000000000..845517858 --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_client2.out @@ -0,0 +1,144 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test2; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 3 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 4 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 5 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 0 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_debug.detach_session(); + detach_session +---------------- + +(1 row) + diff --git a/contrib/gms_debug/expected/gms_debugger_client3.out b/contrib/gms_debug/expected/gms_debugger_client3.out new file mode 100644 index 000000000..58f388db4 --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_client3.out @@ -0,0 +1,144 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test3; +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 6 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 10 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 14 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 6 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_debug.detach_session(); + detach_session +---------------- + +(1 row) + diff --git a/contrib/gms_debug/expected/gms_debugger_client4.out b/contrib/gms_debug/expected/gms_debugger_client4.out new file mode 100644 index 000000000..0024bb853 --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_client4.out @@ -0,0 +1,192 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test4; +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_step +NOTICE: line= 6 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_step +NOTICE: line= 7 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_step +NOTICE: line= 15 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_step +NOTICE: line= 16 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_step +NOTICE: line= 19 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 14 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 3 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 15 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_debug.detach_session(); + detach_session +---------------- + +(1 row) + diff --git a/contrib/gms_debug/expected/gms_debugger_client5.out b/contrib/gms_debug/expected/gms_debugger_client5.out new file mode 100644 index 000000000..6a030edd3 --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_client5.out @@ -0,0 +1,200 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test5; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_finish() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 8, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 6 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 7 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 14 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_step +NOTICE: line= 15 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_finish +NOTICE: line= 15 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 0 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_finish +NOTICE: line= 15 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 0 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_debug.detach_session(); +connection to server was lost diff --git a/contrib/gms_debug/expected/gms_debugger_client6.out b/contrib/gms_debug/expected/gms_debugger_client6.out new file mode 100644 index 000000000..f58a3ac9c --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_client6.out @@ -0,0 +1,177 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test6; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_finish() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 8, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_breakpoint('test_debug', 31); +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 44); +NOTICE: ret= 1 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= 0 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_finish +NOTICE: line= 31 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 2 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= 1 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_finish +NOTICE: line= 44 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 2 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_finish +NOTICE: line= 14 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 3 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 15 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_debug.detach_session(); + detach_session +---------------- + +(1 row) + diff --git a/contrib/gms_debug/expected/gms_debugger_server1.out b/contrib/gms_debug/expected/gms_debugger_server1.out new file mode 100644 index 000000000..5e9758952 --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_server1.out @@ -0,0 +1,181 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test1 cascade; +NOTICE: schema "gms_debugger_test1" does not exist, skipping +create schema gms_debugger_test1; +set search_path = gms_debugger_test1; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; +create table show_code_table(lineno int, code text, canBreak bool); +do $$ +declare + funcoid oid; +begin + select oid from pg_proc into funcoid where proname = 'test_debug'; + INSERT INTO show_code_table SELECT * FROM dbe_pldebugger.info_code(funcoid); +end; +$$; +select * from show_code_table; + lineno | code | canbreak +--------+----------------------------------------------------------------------------------+---------- + | CREATE OR REPLACE FUNCTION gms_debugger_test1.test_debug(x integer) | f + | RETURNS SETOF test | f + | LANGUAGE plpgsql | f + | NOT FENCED NOT SHIPPABLE | f + 1 | AS $function$ | f + 2 | DECLARE | f + 3 | sql_stmt VARCHAR2(500); | f + 4 | r test%rowtype; | f + 5 | rec record; | f + 6 | b_tmp text; | f + 7 | cnt int; | f + 8 | a_tmp int; | f + 9 | cur refcursor; | f + 10 | n_tmp NUMERIC(24,6); | f + 11 | t_tmp tsquery; | f + 12 | CURSOR cur_arg(criterion INTEGER) IS | f + 13 | SELECT * FROM test WHERE a < criterion; | f + 14 | BEGIN | f + 15 | cnt := 0; | t + 16 | FOR r IN SELECT * FROM test | t + 17 | WHERE a > x | f + 18 | LOOP | f + 19 | RETURN NEXT r; | t + 20 | END LOOP; | f + 21 | | f + 22 | FOR rec in SELECT * FROM test | t + 23 | WHERE a < x | f + 24 | LOOP | f + 25 | RETURN NEXT rec; | t + 26 | END LOOP; | f + 27 | | f + 28 | FORALL index_1 IN 0..1 | t + 29 | INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); | t + 30 | | f + 31 | SELECT b FROM test where a = 7 INTO b_tmp; | t + 32 | sql_stmt := 'select a from test where b = :1;'; | t + 33 | OPEN cur FOR sql_stmt USING b_tmp; | t + 34 | IF cur%isopen then LOOP | t + 35 | FETCH cur INTO a_tmp; | t + 36 | EXIT WHEN cur%notfound; | t + 37 | END LOOP; | f + 38 | END IF; | f + 39 | CLOSE cur; | t + 40 | WHILE cnt < 3 LOOP | t + 41 | cnt := cnt + 1; | t + 42 | END LOOP; | f + 43 | | f + 44 | RAISE INFO 'cnt is %', cnt; | t + 45 | | f + 46 | RETURN; | t + 47 | | f + 48 | END | f + 49 | $function$; | f +(53 rows) + +-- attach fail (target not turned on) +select * from gms_debug.attach_session('datanode1-0'); +ERROR: target session should be init first. +-- turn off without turn on +select * from gms_debug.debug_off(); +ERROR: target session should be init first +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +-- start debug - 1st run +select * from test_debug(4); +INFO: cnt is 3 + a | b | c +---+---------------------+-------------------------- + 5 | Fasting | Sat Mar 02 00:00:00 2030 + 7 | Treasures in Heaven | Mon Apr 02 00:00:00 2040 + 2 | Giving to the Needy | Sun Feb 02 00:00:00 2020 + 3 | Prayer | Thu Dec 02 00:00:00 2021 +(4 rows) + +-- start debug - 2nd run - to be aborted +select * from test_debug(4); +INFO: cnt is 3 + a | b | c +---+-----------------------+-------------------------- + 5 | Fasting | Sat Mar 02 00:00:00 2030 + 7 | Treasures in Heaven | Mon Apr 02 00:00:00 2040 + 2 | Giving to the Needy | Sun Feb 02 00:00:00 2020 + 3 | Prayer | Thu Dec 02 00:00:00 2021 + 0 | Happy Children's Day! | Tue Jun 01 00:00:00 2021 + 1 | Happy Children's Day! | Tue Jun 01 00:00:00 2021 +(6 rows) + +drop schema gms_debugger_test1 cascade; +NOTICE: drop cascades to 8 other objects +DETAIL: drop cascades to table test +drop cascades to function test_debug(integer) +drop cascades to table show_code_table +drop cascades to function gms_breakpoint(text,integer) +drop cascades to function gms_continue() +drop cascades to function gms_next() +drop cascades to function gms_step() +drop cascades to function gms_info() diff --git a/contrib/gms_debug/expected/gms_debugger_server2.out b/contrib/gms_debug/expected/gms_debugger_server2.out new file mode 100644 index 000000000..e3106e5bb --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_server2.out @@ -0,0 +1,46 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test2 cascade; +NOTICE: schema "gms_debugger_test2" does not exist, skipping +create schema gms_debugger_test2; +set search_path = gms_debugger_test2; +-- commit/rollback in procedure +create table tb1(a int); +create or replace procedure test_debug2 as +begin + insert into tb1 values (1000); + commit; + insert into tb1 values (2000); + rollback; +end; +/ +-- start debug +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +-- start debug - 1st run +select * from test_debug2(); + test_debug2 +------------- + +(1 row) + +-- start debug - 2nd run - to be aborted +select * from test_debug2(); +ERROR: receive abort message +CONTEXT: PL/pgSQL function test_debug2() line 2 at SQL statement +drop schema gms_debugger_test2 cascade; +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to table tb1 +drop cascades to function test_debug2() +drop cascades to function gms_continue() +drop cascades to function gms_next() diff --git a/contrib/gms_debug/expected/gms_debugger_server3.out b/contrib/gms_debug/expected/gms_debugger_server3.out new file mode 100644 index 000000000..245965bf2 --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_server3.out @@ -0,0 +1,53 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test3 cascade; +NOTICE: schema "gms_debugger_test3" does not exist, skipping +create schema gms_debugger_test3; +set search_path = gms_debugger_test3; +-- test for implicit variables +CREATE OR REPLACE function test_debug3(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + ELSE + b := 999; + END CASE; + raise info 'pi_return : %',pi_return ; + return b; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +-- start debug - 1st run +select * from test_debug3(1); + test_debug3 +------------- + 101 +(1 row) + +-- start debug - 2nd run - to be aborted +select * from test_debug3(1); +ERROR: receive abort message +CONTEXT: PL/pgSQL function test_debug3(integer) line 13 at assignment +drop schema gms_debugger_test3 cascade; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to function test_debug3(integer) +drop cascades to function gms_next() +drop cascades to function gms_continue() diff --git a/contrib/gms_debug/expected/gms_debugger_server4.out b/contrib/gms_debug/expected/gms_debugger_server4.out new file mode 100644 index 000000000..16213e6d2 --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_server4.out @@ -0,0 +1,114 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test4 cascade; +NOTICE: schema "gms_debugger_test4" does not exist, skipping +create schema gms_debugger_test4; +set search_path = gms_debugger_test4; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +select * from test_debug4(1); +INFO: cnt is 3 +CONTEXT: SQL statement "call test_debug(a)" +PL/pgSQL function test_debug4(integer) line 7 at SQL statement + test_debug4 +------------- + 101 +(1 row) + +-- test with client error in exception +select * from test_debug4(1); +ERROR: receive abort message +CONTEXT: PL/pgSQL function test_debug4(integer) line 14 at assignment +drop schema gms_debugger_test4 cascade; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to table test +drop cascades to function test_debug(integer) +drop cascades to function test_debug4(integer) +drop cascades to function gms_step() +drop cascades to function gms_continue() diff --git a/contrib/gms_debug/expected/gms_debugger_server5.out b/contrib/gms_debug/expected/gms_debugger_server5.out new file mode 100644 index 000000000..c3518210d --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_server5.out @@ -0,0 +1,122 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test5 cascade; +NOTICE: schema "gms_debugger_test5" does not exist, skipping +create schema gms_debugger_test5; +set search_path = gms_debugger_test5; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +select * from test_debug4(1); +INFO: cnt is 3 +CONTEXT: SQL statement "call test_debug(a)" +PL/pgSQL function test_debug4(integer) line 7 at SQL statement + test_debug4 +------------- + 101 +(1 row) + +-- test with client error in exception +select * from test_debug4(1); +INFO: cnt is 3 +CONTEXT: SQL statement "call test_debug(a)" +PL/pgSQL function test_debug4(integer) line 7 at SQL statement + test_debug4 +------------- + 101 +(1 row) + +drop schema gms_debugger_test5 cascade; +NOTICE: drop cascades to 7 other objects +DETAIL: drop cascades to table test +drop cascades to function test_debug(integer) +drop cascades to function test_debug4(integer) +drop cascades to function gms_continue() +drop cascades to function gms_step() +drop cascades to function gms_next() +drop cascades to function gms_finish() diff --git a/contrib/gms_debug/expected/gms_debugger_server6.out b/contrib/gms_debug/expected/gms_debugger_server6.out new file mode 100644 index 000000000..1a78fa213 --- /dev/null +++ b/contrib/gms_debug/expected/gms_debugger_server6.out @@ -0,0 +1,114 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test6 cascade; +NOTICE: schema "gms_debugger_test6" does not exist, skipping +create schema gms_debugger_test6; +set search_path = gms_debugger_test6; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +select * from test_debug4(1); +INFO: cnt is 3 +CONTEXT: SQL statement "call test_debug(a)" +PL/pgSQL function test_debug4(integer) line 7 at SQL statement + test_debug4 +------------- + 101 +(1 row) + +-- test with client error in exception +select * from test_debug4(1); +ERROR: Debug Comm 0 has been released or not turned on yet. +drop schema gms_debugger_test6 cascade; +NOTICE: drop cascades to 6 other objects +DETAIL: drop cascades to table test +drop cascades to function test_debug(integer) +drop cascades to function test_debug4(integer) +drop cascades to function gms_continue() +drop cascades to function gms_breakpoint(text,integer) +drop cascades to function gms_finish() diff --git a/contrib/gms_debug/gms_debug--1.0.sql b/contrib/gms_debug/gms_debug--1.0.sql new file mode 100644 index 000000000..b7a50dff8 --- /dev/null +++ b/contrib/gms_debug/gms_debug--1.0.sql @@ -0,0 +1,387 @@ +/* contrib/gms_debug/gms_debug--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION gms_debug" to load this file. \quit + +-- gms_debug package begin +set behavior_compat_options='proc_outparam_override'; +CREATE SCHEMA gms_debug; +GRANT USAGE ON SCHEMA gms_debug TO PUBLIC; + +CREATE TYPE gms_debug.program_info AS ( + namespace oid, + name varchar2(30), + owner varchar2(30), + dblink varchar2(30), + line# binary_integer, + libunittype binary_integer, + entrypointname varchar2(30)); + +CREATE TYPE gms_debug.runtime_info AS ( + line# binary_integer, + terminated binary_integer, + breakpoint binary_integer, + stackdepth binary_integer, + interpreterdepth binary_integer, + reason binary_integer, + program gms_debug.program_info); + +CREATE or REPLACE FUNCTION gms_debug.initialize( +IN debug_session_id varchar2(30) DEFAULT '' , +IN diagnostics binary_integer DEFAULT 0) +returns varchar2 +AS 'MODULE_PATHNAME', 'gms_debug_initialize' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE or REPLACE FUNCTION gms_debug.debug_on( +IN no_client_side_plsql_engine BOOLEAN DEFAULT TRUE , +IN immediate BOOLEAN DEFAULT FALSE) +returns void AS $$ +BEGIN + return; +END; +$$ language plpgsql IMMUTABLE; + +CREATE or REPLACE FUNCTION gms_debug.attach_session( +IN debug_session_id varchar2(30) , +IN diagnostics binary_integer DEFAULT 0) +returns void +AS 'MODULE_PATHNAME', 'gms_debug_attach_session' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE or REPLACE FUNCTION gms_debug.add_breakpoint( +IN proid oid, +IN line# binary_integer , +OUT breakpoint# binary_integer, +IN fuzzy binary_integer, +IN iterations binary_integer, +OUT sts binary_integer) +AS 'MODULE_PATHNAME', 'gms_debug_set_breakpoint' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE or REPLACE FUNCTION gms_debug.set_breakpoint( +IN program gms_debug.program_info, +IN line# binary_integer, +OUT breakpoint# binary_integer, +IN fuzzy binary_integer, +IN iterations binary_integer) +returns binary_integer AS $$ +DECLARE +proid oid; +sts binary_integer; +BEGIN + select oid from pg_proc where proname = program.name limit 1 into proid; + set behavior_compat_options=''; + gms_debug.add_breakpoint(proid, line#, breakpoint#, fuzzy, iterations, sts); + set behavior_compat_options='proc_outparam_override'; + return sts; +END; +$$ language plpgsql; + +CREATE or REPLACE FUNCTION gms_debug.call_continue( +IN breakflags binary_integer , +IN info_requested binary_integer DEFAULT NULL, +OUT err_code binary_integer, +OUT run_line binary_integer, +OUT run_breakpoint binary_integer, +OUT run_stackdepth binary_integer, +OUT run_reason binary_integer, +OUT pro_namespace oid, +OUT pro_name varchar2(30), +OUT pro_owner oid) +AS 'MODULE_PATHNAME', 'gms_debug_continue' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE or REPLACE FUNCTION gms_debug.continue( +OUT run_info gms_debug.runtime_info , +IN breakflags binary_integer , +IN info_requested binary_integer DEFAULT NULL) +returns binary_integer AS $$ +DECLARE + err_code binary_integer; + run_line binary_integer; + run_terminated binary_integer; + run_breakpoint binary_integer; + run_stackdepth binary_integer; + run_interpreterdepth binary_integer; + run_reason binary_integer; + pro_namespace oid; + pro_name varchar2(30); + pro_owner oid; + pro_ownername varchar2(30); + pro gms_debug.program_info; +BEGIN + set behavior_compat_options=''; + gms_debug.call_continue(breakflags, info_requested, err_code, run_line, + run_breakpoint, run_stackdepth, run_reason, pro_namespace, + pro_name, pro_owner); + set behavior_compat_options='proc_outparam_override'; + select usename from pg_user where usesysid = pro_owner into pro_ownername; + run_info.line# = run_line; + run_info.terminated = 0; + run_info.breakpoint = run_breakpoint; + run_info.stackdepth = run_stackdepth; + run_info.interpreterdepth = -1; + run_info.reason = run_reason; + pro.namespace = pro_namespace; + pro.name = pro_name; + pro.owner = pro_ownername; + pro.dblink = ''; + pro.line# = run_line; + pro.libunittype = 0; + pro.entrypointname = ''; + run_info.program = pro; + return err_code; +END; +$$ language plpgsql; + +CREATE or REPLACE FUNCTION gms_debug.get_debug_runtime_info( +IN info_requested binary_integer, +OUT err_code binary_integer, +OUT run_line binary_integer, +OUT run_breakpoint binary_integer, +OUT run_stackdepth binary_integer, +OUT run_reason binary_integer, +OUT pro_namespace oid, +OUT pro_name varchar2(30), +OUT pro_owner oid) +AS 'MODULE_PATHNAME', 'gms_debug_get_runtime_info' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE or REPLACE FUNCTION gms_debug.get_runtime_info( +IN info_requested binary_integer, +OUT run_info gms_debug.runtime_info) +returns binary_integer AS $$ +DECLARE + err_code binary_integer; + run_line binary_integer; + run_terminated binary_integer; + run_breakpoint binary_integer; + run_stackdepth binary_integer; + run_interpreterdepth binary_integer; + run_reason binary_integer; + pro_namespace oid; + pro_name varchar2(30); + pro_owner oid; + pro_ownername varchar2(30); + pro gms_debug.program_info; +BEGIN + set behavior_compat_options=''; + gms_debug.get_debug_runtime_info(info_requested, err_code, run_line, + run_breakpoint, run_stackdepth, run_reason, pro_namespace, + pro_name, pro_owner); + set behavior_compat_options='proc_outparam_override'; + select usename from pg_user where usesysid = pro_owner into pro_ownername; + run_info.line# = run_line; + run_info.terminated = 0; + run_info.breakpoint = run_breakpoint; + run_info.stackdepth = run_stackdepth; + run_info.interpreterdepth = -1; + run_info.reason = run_reason; + pro.namespace = pro_namespace; + pro.name = pro_name; + pro.owner = pro_ownername; + pro.dblink = ''; + pro.line# = run_line; + pro.libunittype = 0; + pro.entrypointname = ''; + run_info.program = pro; + return err_code; +END; +$$ language plpgsql; + +CREATE or REPLACE FUNCTION gms_debug.debug_off() +returns void +AS 'MODULE_PATHNAME', 'gms_debug_off' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE or REPLACE FUNCTION gms_debug.detach_session() +returns void +AS 'MODULE_PATHNAME', 'gms_debug_detach_session' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE or REPLACE PROCEDURE gms_debug.probe_version(OUT major binary_integer, OUT minor binary_integer) +AS +BEGIN + major := 1; + minor := 0; +END; + +CREATE FUNCTION gms_debug.success() RETURNS int AS $$ +BEGIN +return 0; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.error_illegal_line() RETURNS int AS $$ +BEGIN +return -1; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.error_bad_handle() RETURNS int AS $$ +BEGIN +return -2; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.error_already_exists() RETURNS int AS $$ +BEGIN +return -3; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.error_communication() RETURNS int AS $$ +BEGIN +return -4; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.info_getstackdepth() RETURNS int AS $$ +BEGIN +return 0; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.info_getbreakpoint() RETURNS int AS $$ +BEGIN +return 2; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.info_getlineinfo() RETURNS int AS $$ +BEGIN +return 4; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.break_next_line() RETURNS int AS $$ +BEGIN +return 2; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.break_any_call() RETURNS int AS $$ +BEGIN +return 4; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.break_any_return() RETURNS int AS $$ +BEGIN +return 8; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.break_return() RETURNS int AS $$ +BEGIN +return 16; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.break_exception() RETURNS int AS $$ +BEGIN +return 32; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.break_handler() RETURNS int AS $$ +BEGIN +return 64; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.abort_execution() RETURNS int AS $$ +BEGIN +return 128; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_none() RETURNS int AS $$ +BEGIN +return 0; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_breakpoint() RETURNS int AS $$ +BEGIN +return 1; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_enter() RETURNS int AS $$ +BEGIN +return 2; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_return() RETURNS int AS $$ +BEGIN +return 3; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_finish() RETURNS int AS $$ +BEGIN +return 4; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_line() RETURNS int AS $$ +BEGIN +return 5; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_interrupt() RETURNS int AS $$ +BEGIN +return 6; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_exception() RETURNS int AS $$ +BEGIN +return 7; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_exit() RETURNS int AS $$ +BEGIN +return 8; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_knl_exit() RETURNS int AS $$ +BEGIN +return 9; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_handler() RETURNS int AS $$ +BEGIN +return 10; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_timeout() RETURNS int AS $$ +BEGIN +return 11; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_instantiate() RETURNS int AS $$ +BEGIN +return 12; +END; +$$ language plpgsql IMMUTABLE; + +CREATE FUNCTION gms_debug.reason_abort() RETURNS int AS $$ +BEGIN +return 13; +END; +$$ language plpgsql IMMUTABLE; + + + + +-- gms_debug package end \ No newline at end of file diff --git a/contrib/gms_debug/gms_debug.control b/contrib/gms_debug/gms_debug.control new file mode 100644 index 000000000..93639b926 --- /dev/null +++ b/contrib/gms_debug/gms_debug.control @@ -0,0 +1,5 @@ +# gms_debug extension +comment = 'provides the interface to initiate and control the debugging of PL/SQL applications' +default_version = '1.0' +module_pathname = '$libdir/gms_debug' +relocatable = true diff --git a/contrib/gms_debug/gms_debug.cpp b/contrib/gms_debug/gms_debug.cpp new file mode 100644 index 000000000..d8353e958 --- /dev/null +++ b/contrib/gms_debug/gms_debug.cpp @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openGauss is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * -------------------------------------------------------------------------------------- + * + * gms_debug.cpp + * gms_debug can effectively estimate statistical data. + * + * + * IDENTIFICATION + * contrib/gms_debug/gms_debug.cpp + * + * -------------------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "utils/plpgsql_domain.h" +#include "commands/copy.h" +#include "funcapi.h" +#include "utils/plpgsql.h" +#include +#include "utils/pl_debug.h" +#include "lib/stringinfo.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "catalog/pg_authid.h" +#include "miscadmin.h" +#include "gms_debug.h" + +#include + +PG_MODULE_MAGIC; + +PG_FUNCTION_INFO_V1(gms_debug_attach_session); +PG_FUNCTION_INFO_V1(gms_debug_detach_session); +PG_FUNCTION_INFO_V1(gms_debug_get_runtime_info); +PG_FUNCTION_INFO_V1(gms_debug_initialize); +PG_FUNCTION_INFO_V1(gms_debug_off); +PG_FUNCTION_INFO_V1(gms_debug_continue); +PG_FUNCTION_INFO_V1(gms_debug_set_breakpoint); + + +static void gms_attach_session(int commidx, uint64 sid) +{ + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[commidx]; + AutoMutexLock debuglock(&debug_comm->mutex); + debuglock.lock(); + if (debug_comm->Used()) { + if (debug_comm->hasClient()) { + debuglock.unLock(); + ereport(ERROR, (errmodule(MOD_PLDEBUGGER), errcode(ERRCODE_TARGET_SERVER_ALREADY_ATTACHED), + (errmsg("target session already attached on other client.")))); + } + if (debug_comm->hasClientErrorOccured || debug_comm->hasServerErrorOccured) { + debuglock.unLock(); + ereport(ERROR, (errmodule(MOD_PLDEBUGGER), errcode(ERRCODE_INVALID_OPERATION), + (errmsg("target function is not running in expected way.")))); + } + debug_comm->clientId = sid; + } else { + debuglock.unLock(); + ereport(ERROR, (errmodule(MOD_PLDEBUGGER), errcode(ERRCODE_INVALID_OPERATION), + (errmsg("target session should be init first.")))); + } + debuglock.unLock(); +} + +static bool GMSInterfaceCheck(const char* funcname, bool needAttach) +{ +#ifdef ENABLE_MULTIPLE_NODES + PLDEBUG_FEATURE_NOT_SUPPORT_IN_DISTRIBUTED(); +#endif + if (!superuser() && !is_member_of_role(GetUserId(), DEFAULT_ROLE_PLDEBUGGER)) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be system admin to execute", funcname)))); + return false; + } + if (u_sess->plsql_cxt.debug_client != NULL && needAttach){ + int commIdx = u_sess->plsql_cxt.debug_client->comm_idx; + CHECK_DEBUG_COMM_VALID(commIdx); + /* if current debug index is not myself during debug, clean up my self */ + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[commIdx]; + DebugClientInfo* client = u_sess->plsql_cxt.debug_client; + AutoMutexLock debuglock(&debug_comm->mutex); + debuglock.lock(); + if (debug_comm == nullptr) { + client->comm_idx = -1; + MemoryContextDelete(client->context); + u_sess->plsql_cxt.debug_client = NULL; + debuglock.unLock(); + ereport(ERROR, + (errcode(ERRCODE_TARGET_SERVER_NOT_ATTACHED), + errmsg("must attach a execute func before execute", funcname), + errhint("attach a execute func and retry"))); + return false; + } + if (!debug_comm->isRunning()) { + debuglock.unLock(); + return false; + } + debuglock.unLock(); + } + return true; +} + +/* +* This function initializes the target session for debugging. +*/ +Datum gms_debug_initialize(PG_FUNCTION_ARGS) +{ + StringInfoData buf; + bool found = false; + // TupleDesc tupdesc; + int commIdx = -1; + const int DEBUG_TURN_ON_ATTR_NUM = 1; + GMSInterfaceCheck("gms_debug.initialize", false); + if (unlikely(u_sess->plsql_cxt.debug_proc_htbl == NULL)) { + init_pldebug_htcl(); + } + /* return nodename & socket idx as port */ + if (!PG_ARGISNULL(0)) { + // check if the debug_session_id valid + char *debug_session_id = text_to_cstring(PG_GETARG_VARCHAR_PP(0)); + char *psave = NULL; + char *nodename = strtok_r(debug_session_id, "-", &psave); + char *fir = AssignStr(psave, false); + char *new_fir = TrimStr(fir); + if (new_fir == NULL) { + ereport(ERROR, ( (errmsg("invalid debug_session_id %s", debug_session_id)))); + } + commIdx = pg_strtoint32(new_fir); + if (commIdx < 0 || commIdx >= PG_MAX_DEBUG_CONN) { + ereport(ERROR, ( (errmsg("invalid debug_session_id %s", debug_session_id)))); + } + if (!AcquireDebugCommIdx(commIdx)) { + ereport(ERROR, + ( errmsg("debug_session_id %s has already been used", debug_session_id))); + } + } else { + commIdx = GetValidDebugCommIdx(); + if (commIdx == -1) { + ereport(ERROR, ( + (errmsg("max debug function is %d, turn_on function is out of range", PG_MAX_DEBUG_CONN)))); + } + } + + SetDebugCommGmsUsed(commIdx, true); + // dms_debug indicates that session debugging functionality should be enabled. + u_sess->plsql_cxt.gms_debug_idx = commIdx; + initStringInfo(&buf); + // simple concatenate node_name and port with an underscore + appendStringInfo(&buf, "%s-%d", g_instance.attr.attr_common.PGXCNodeName, commIdx); + PG_RETURN_VARCHAR_P(cstring_to_text(buf.data)); +} + +/* +* This procedure notifies the debug session about the target program. +*/ +Datum gms_debug_attach_session(PG_FUNCTION_ARGS) +{ + GMSInterfaceCheck("gms_debug.attach_session", false); + char *debug_session_id = text_to_cstring(PG_GETARG_VARCHAR_PP(0)); + int32 diagnostics = PG_GETARG_INT32(1); + char *psave = NULL; + int commidx = -1; + char *nodename = strtok_r(debug_session_id, "-", &psave); + char *fir = AssignStr(psave, false); + char *new_fir = TrimStr(fir); + if (new_fir == NULL) { + ereport(ERROR, ( (errmsg("invalid debug_session_id %s", debug_session_id)))); + } + commidx = pg_strtoint32(new_fir); + + /* if is attach to some other function, just clean up it */ + clean_up_debug_client(true); + /* this nodename check is only for single node */ + nodename = TrimStr(nodename); + if (nodename == NULL || strcasecmp(nodename, g_instance.attr.attr_common.PGXCNodeName) != 0) { + ereport(ERROR, ( errcode(ERRCODE_AMBIGUOUS_PARAMETER), + (errmsg("wrong debug nodename, should be %s.", g_instance.attr.attr_common.PGXCNodeName)))); + } + if (commidx < 0 || commidx >= PG_MAX_DEBUG_CONN) { + ereport(ERROR, ( errcode(ERRCODE_AMBIGUOUS_PARAMETER), + (errmsg("invalid debug port id %d.", commidx)))); + } + /* only can attach when comm satisfy contidion */ + gms_attach_session(commidx, u_sess->session_id); + u_sess->plsql_cxt.debug_client = InitDebugClient(commidx); + PG_RETURN_VOID(); +} + +/** + * This function sets a breakpoint in a program unit, which persists for the current session. + */ +Datum gms_debug_set_breakpoint(PG_FUNCTION_ARGS) +{ + const int DEBUG_BREAK_TUPLE_ATTR_NUM = 2; + Oid funcOid = PG_GETARG_OID(0); + int32 lineno = PG_GETARG_INT32(1); + int32 fuzzy = PG_GETARG_INT32(2); + int32 iterations = PG_GETARG_INT32(3); + int headerlines = 0; + uint32 nLine = 0; + CodeLine *lines = NULL; + CodeLine cl; + cl.code = NULL; + bool found = false; + DebugClientInfo *client = u_sess->plsql_cxt.debug_client; + StringInfoData str; + + if(client == nullptr) { + ereport(ERROR, + (errcode(ERRCODE_TARGET_SERVER_NOT_ATTACHED), + errmsg("error happened in debug session, please reattach target session and try"))); + } + + TupleDesc tupdesc; + MemoryContext oldcontext = MemoryContextSwitchTo(client->context); + + tupdesc = CreateTemplateTupleDesc(DEBUG_BREAK_TUPLE_ATTR_NUM, false); + TupleDescInitEntry(tupdesc, (AttrNumber)1, "status", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)2, "breakpoint", INT4OID, -1, 0); + TupleDesc tuple_desc = BlessTupleDesc(tupdesc); + + Datum values[DEBUG_BREAK_TUPLE_ATTR_NUM]; + bool nulls[DEBUG_BREAK_TUPLE_ATTR_NUM]; + HeapTuple tuple; + errno_t rc = 0; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + + (void)MemoryContextSwitchTo(oldcontext); + + values[0] = Int32GetDatum(0); + values[1] = Int32GetDatum(-1); + if (OidIsValid(funcOid)) { + bool checked = GMSInterfaceCheck("gms_debug.add_breakpoint", true); + if(!checked) { + ereport(WARNING, + (errcode(ERRCODE_TARGET_SERVER_NOT_ATTACHED), + errmsg("target func not attached"))); + values[0] = Int32GetDatum(ERROR_BAD_HANDLE); + tuple = heap_form_tuple(tuple_desc, values, nulls); + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); + } + } else { + ereport(WARNING, (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("pl debugger only support function with language plpgsql"), + errdetail("the given function is %lu", funcOid), + errcause("pl debugger do not support the given function"), + erraction("use pl debugger with only plpgsql function"))); + values[0] = Int32GetDatum(ERROR_BAD_HANDLE); + tuple = heap_form_tuple(tuple_desc, values, nulls); + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); + } + + if (unlikely(u_sess->plsql_cxt.debug_proc_htbl == NULL)) { + init_pldebug_htcl(); + } + + PlDebugEntry *entry = + (PlDebugEntry *)hash_search(u_sess->plsql_cxt.debug_proc_htbl, (void *)(&funcOid), HASH_ENTER, &found); + entry->key = funcOid; + if (!found) { + entry->commIdx = client->comm_idx; + entry->func = NULL; + } + initStringInfo(&str); + uint64 sid = ENABLE_THREAD_POOL ? u_sess->session_id : t_thrd.proc_cxt.MyProcPid; + appendStringInfo(&str, "%lu:%u:%d:%s", sid, funcOid, lineno, cl.code == NULL ? "NULL" : cl.code); + debug_client_send_msg(client, GMS_DEBUG_ADDBREAKPOINT_HEADER, str.data, str.len); + debug_client_rec_msg(client); + int32 ans = pg_strtoint32(client->rec_buffer); + pfree_ext(lines); + pfree_ext(str.data); + + if (ans == ADD_BP_ERR_ALREADY_EXISTS) { + ereport(WARNING, (errcode(ERRCODE_WARNING), + errmsg("the given line number already contains a valid breakpoint."))); + values[0] = Int32GetDatum(ERROR_ALREADY_EXISTS); + tuple = heap_form_tuple(tuple_desc, values, nulls); + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); + } else if (ans == ADD_BP_ERR_OUT_OF_RANGE) { + ereport(WARNING, (errcode(ERRCODE_WARNING), + errmsg("lineno must be within the range of [1, MaxLineNumber]." ))); + values[0] = Int32GetDatum(ERROR_ILLEGAL_LINE); + tuple = heap_form_tuple(tuple_desc, values, nulls); + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); + } else if (ans == ADD_BP_ERR_INVALID_BP_POS) { + ereport(WARNING, (errcode(ERRCODE_WARNING), + errmsg("the given line number does not name a valid breakpoint."))); + values[0] = Int32GetDatum(ERROR_ILLEGAL_LINE); + tuple = heap_form_tuple(tuple_desc, values, nulls); + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); + } + values[0] = Int32GetDatum(0); + values[1] = Int32GetDatum(ans); + tuple = heap_form_tuple(tuple_desc, values, nulls); + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); +} + +static char* parse_and_valid(char **psave, const char* rec_buf) +{ + char *fir = strtok_r(NULL, ":", psave); + char *new_fir = TrimStr(fir); + if (new_fir == NULL) { + ReportInvalidMsg(rec_buf); + return NULL; + } + return new_fir; +} + +static Datum build_runtime_info(DebugClientInfo *client, int err_code) +{ + const int DEBUG_RUNTIME_TUPLE_ATTR_NUM = 8; + int i = 0; + TupleDesc tupdesc; + MemoryContext oldcontext = MemoryContextSwitchTo(client->context); + + tupdesc = CreateTemplateTupleDesc(DEBUG_RUNTIME_TUPLE_ATTR_NUM, false); + TupleDescInitEntry(tupdesc, (AttrNumber)++i, "err_code", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)++i, "run_line", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)++i, "run_breakpoint", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)++i, "run_stackdepth", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)++i, "run_reason", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)++i, "pro_namespace", OIDOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)++i, "pro_name", TEXTOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber)++i, "pro_owner", OIDOID, -1, 0); + TupleDesc tuple_desc = BlessTupleDesc(tupdesc); + + /* Received buffer will be in the form of */ + char *psave = NULL; + char *fir = strtok_r(client->rec_buffer, ":", &psave); + const int int64Size = 10; + Oid namespaceoid; + Oid funcoid; + Oid owneroid; + int run_line = -1; + int run_breakpoint = -1; + int run_stackdepth = -1; + int run_reason = 0; + char *pro_name = NULL; + Datum values[DEBUG_RUNTIME_TUPLE_ATTR_NUM]; + bool nulls[DEBUG_RUNTIME_TUPLE_ATTR_NUM]; + HeapTuple tuple; + errno_t rc = 0; + + if(err_code == 0) { + char *new_fir = TrimStr(fir); + if (new_fir == NULL) { + ReportInvalidMsg(client->rec_buffer); + PG_RETURN_DATUM(0); + } + funcoid = (Oid)pg_strtouint64(new_fir, NULL, int64Size); + new_fir = parse_and_valid(&psave, client->rec_buffer); + CHECK_RETURN_DATUM(new_fir); + namespaceoid = (Oid)pg_strtouint64(new_fir, NULL, int64Size); + new_fir = parse_and_valid(&psave, client->rec_buffer); + CHECK_RETURN_DATUM(new_fir); + owneroid = (Oid)pg_strtouint64(new_fir, NULL, int64Size); + new_fir = parse_and_valid(&psave, client->rec_buffer); + CHECK_RETURN_DATUM(new_fir); + run_line = pg_strtoint32(new_fir); + new_fir = parse_and_valid(&psave, client->rec_buffer); + CHECK_RETURN_DATUM(new_fir); + run_breakpoint = pg_strtoint32(new_fir); + new_fir = parse_and_valid(&psave, client->rec_buffer); + CHECK_RETURN_DATUM(new_fir); + run_stackdepth = pg_strtoint32(new_fir); + new_fir = parse_and_valid(&psave, client->rec_buffer); + CHECK_RETURN_DATUM(new_fir); + run_reason = pg_strtoint32(new_fir); + pro_name = AssignStr(psave, false); + } + + (void)MemoryContextSwitchTo(oldcontext); + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + + i = 0; + values[i++] = Int32GetDatum(err_code); + values[i++] = Int32GetDatum(run_line); + values[i++] = Int32GetDatum(run_breakpoint); + values[i++] = Int32GetDatum(run_stackdepth); + values[i++] = Int32GetDatum(run_reason); + values[i++] = ObjectIdGetDatum(namespaceoid); + values[i++] = CStringGetTextDatum(pro_name); + values[i++] = ObjectIdGetDatum(owneroid); + tuple = heap_form_tuple(tuple_desc, values, nulls); + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); +} + +/** + * This function passes the given breakflags + * (a mask of the events that are of interest) to Probe in the target process. + * It tells Probe to continue execution of the target process, + * and it waits until the target process runs to completion or signals an event. + * If info_requested is not NULL, then calls GET_RUNTIME_INFO. + */ +Datum gms_debug_continue(PG_FUNCTION_ARGS) +{ + DebugClientInfo *client = u_sess->plsql_cxt.debug_client; + if(client == nullptr) { + ereport(ERROR, + (errcode(ERRCODE_TARGET_SERVER_NOT_ATTACHED), + errmsg("error happened in debug session, please reattach target session and try"))); + } + const int DEBUG_ACTION_ATTR_NUM = 5; + const char actions[DEBUG_ACTION_ATTR_NUM] = { + GMS_DEBUG_CONTINUE_HEADER, + GMS_DEBUG_NEXT_HEADER, + GMS_DEBUG_STEP_INTO_HEADER, + GMS_DEBUG_FINISH_HEADER, + GMS_DEBUG_ABORT_HEADER + }; + int err_code = 0; + bool checked = GMSInterfaceCheck("gms_debug.continue", true); + if(!checked) { + ereport(WARNING, + (errcode(ERRCODE_TARGET_SERVER_NOT_ATTACHED), + errmsg("target func not attached"))); + err_code = ERROR_FUNC_NOT_ATTACHED; + return build_runtime_info(client,err_code); + } + int32 breakflags = PG_GETARG_INT32(0); + int32 info_requested = PG_GETARG_INT32(1); + char action; + std::bitset breakflags_bitset(breakflags); + for (int i = 0; i < breakflags_bitset.size(); ++i) { + if (breakflags_bitset.test(i)) { + action = actions[i]; + } + } + debug_client_send_msg(client, action, NULL, 0); + debug_client_rec_msg(client); + return build_runtime_info(client,err_code); +} + +/*** + * This function returns information about the current program. + * It is only needed if the info_requested parameter to SYNCHRONIZE + * or CONTINUE was set to 0. + */ +Datum gms_debug_get_runtime_info(PG_FUNCTION_ARGS) +{ + int err_code = 0; + DebugClientInfo *client = u_sess->plsql_cxt.debug_client; + if(client == nullptr) { + ereport(ERROR, + (errcode(ERRCODE_TARGET_SERVER_NOT_ATTACHED), + errmsg("error happened in debug session, please reattach target session and try"))); + } + bool checked = GMSInterfaceCheck("gms_debug.runtime_info",true); + if(!checked) { + ereport(WARNING, + (errcode(ERRCODE_TARGET_SERVER_NOT_ATTACHED), + errmsg("target func not attached"))); + err_code = ERROR_FUNC_NOT_ATTACHED; + return build_runtime_info(client, err_code); + } + int32 info_requested = PG_GETARG_INT32(0); + debug_client_send_msg(client, GMS_DEBUG_RUNTIMEINFO_HEADER, NULL, 0); + debug_client_rec_msg(client); + return build_runtime_info(client, err_code); +} + +/*** + * This procedure notifies the target session that + * debugging should no longer take place in that session. + * It is not necessary to call this function before ending the session. + */ +Datum gms_debug_off(PG_FUNCTION_ARGS) +{ + GMSInterfaceCheck("gms_debug.debug_off",false); + // dms_debug indicates that session debugging functionality should be enabled. + SetDebugCommGmsUsed(u_sess->plsql_cxt.gms_debug_idx, false); + u_sess->plsql_cxt.gms_debug_idx = -1; + + PG_RETURN_VOID(); +} + +/*** + * This procedure stops debugging the target program. + * This procedure may be called at any time, + * but it does not notify the target session that the debug session is detaching itself, + * and it does not terminate execution of the target session. + * Therefore, care should be taken to ensure that the target session does not hang itself. + */ +Datum gms_debug_detach_session(PG_FUNCTION_ARGS) +{ + DebugClientInfo *client = u_sess->plsql_cxt.debug_client; + if(client == nullptr) { + ereport(ERROR, + (errcode(ERRCODE_TARGET_SERVER_NOT_ATTACHED), + errmsg("error happened in debug session, please reattach target session and try"))); + } + GMSInterfaceCheck("gms_debug.detach_session",true); + debug_client_send_msg(client, DEBUG_ABORT_HEADER, NULL, 0); + debug_client_rec_msg(client); + clean_up_debug_client(); + PG_RETURN_VOID(); +} diff --git a/contrib/gms_debug/gms_debug.h b/contrib/gms_debug/gms_debug.h new file mode 100644 index 000000000..41dffca3b --- /dev/null +++ b/contrib/gms_debug/gms_debug.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------------------------------------* + * gms_debug.h + * + * Definition about gms_debug package. + * + * IDENTIFICATION + * contrib/gms_debug/gms_debug.h + * + * --------------------------------------------------------------------------------------- + */ +#ifndef GMS_DEBUG_H +#define GMS_DEBUG_H + +#define BREAK_NEXT_LINE 2 +#define BREAK_ANY_CALL 4 +#define BREAK_ANY_RETURN 8 +#define BREAK_RETURN 16 +#define BREAK_EXCEPTION 32 +#define BREAK_HANDLER 64 +#define BREAK_ABORT_EXECUTION 128 + +#define ERROR_ILLEGAL_LINE -1 +#define ERROR_BAD_HANDLE -2 +#define ERROR_ALREADY_EXISTS -3 +#define ERROR_COMMUNICATION -4 +#define ERROR_FUNC_NOT_ATTACHED -9 + +/* from gms_debug.cpp */ +extern "C" Datum gms_debug_attach_session(PG_FUNCTION_ARGS); +extern "C" Datum gms_debug_detach_session(PG_FUNCTION_ARGS); +extern "C" Datum gms_debug_get_runtime_info(PG_FUNCTION_ARGS); +extern "C" Datum gms_debug_probe_version(PG_FUNCTION_ARGS); +extern "C" Datum gms_debug_initialize(PG_FUNCTION_ARGS); +extern "C" Datum gms_debug_on(PG_FUNCTION_ARGS); +extern "C" Datum gms_debug_off(PG_FUNCTION_ARGS); +extern "C" Datum gms_debug_continue(PG_FUNCTION_ARGS); +extern "C" Datum gms_debug_set_breakpoint(PG_FUNCTION_ARGS); + + +#define CHECK_RETURN_DATUM(mask_string) \ + do { \ + if (mask_string == NULL) \ + PG_RETURN_DATUM(0); \ + } while (0) + +#endif diff --git a/contrib/gms_debug/sql/gms_debugger_client1.sql b/contrib/gms_debug/sql/gms_debugger_client1.sql new file mode 100644 index 000000000..9e872bb7e --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_client1.sql @@ -0,0 +1,138 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test1; + +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +CREATE or REPLACE FUNCTION gms_info() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.get_runtime_info(1, run_info); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_breakpoint('test_debug', 0); -- negative + +select gms_breakpoint('test_debug', 15); -- ok + +select gms_breakpoint('test_debug', 17); -- invalid +select gms_breakpoint('test_debug', 22); -- ok +select gms_breakpoint('test_debug', 15); -- ok + +select gms_next(); + +select gms_next(); + +select gms_next(); + +select gms_next(); + +select gms_continue(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/contrib/gms_debug/sql/gms_debugger_client2.sql b/contrib/gms_debug/sql/gms_debugger_client2.sql new file mode 100644 index 000000000..20ca1912f --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_client2.sql @@ -0,0 +1,82 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test2; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_next(); + +select gms_next(); + +select gms_next(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/contrib/gms_debug/sql/gms_debugger_client3.sql b/contrib/gms_debug/sql/gms_debugger_client3.sql new file mode 100644 index 000000000..9f067ba96 --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_client3.sql @@ -0,0 +1,82 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test3; + +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_next(); + +select gms_next(); + +select gms_continue(); + +select gms_next(); + +select gms_debug.detach_session(); diff --git a/contrib/gms_debug/sql/gms_debugger_client4.sql b/contrib/gms_debug/sql/gms_debugger_client4.sql new file mode 100644 index 000000000..fdbd70829 --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_client4.sql @@ -0,0 +1,88 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test4; + +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_continue(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/contrib/gms_debug/sql/gms_debugger_client5.sql b/contrib/gms_debug/sql/gms_debugger_client5.sql new file mode 100644 index 000000000..c3b848c5b --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_client5.sql @@ -0,0 +1,116 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test5; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_finish() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 8, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_next(); + +select gms_next(); + +select gms_next(); + +select gms_step(); + +select gms_finish(); + +select gms_finish(); + +select gms_debug.detach_session(); diff --git a/contrib/gms_debug/sql/gms_debugger_client6.sql b/contrib/gms_debug/sql/gms_debugger_client6.sql new file mode 100644 index 000000000..c5fd2985b --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_client6.sql @@ -0,0 +1,99 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test6; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_finish() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 8, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_breakpoint('test_debug', 31); +select gms_breakpoint('test_debug', 44); + +select gms_finish(); + +select gms_finish(); + +select gms_finish(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/contrib/gms_debug/sql/gms_debugger_client7.sql b/contrib/gms_debug/sql/gms_debugger_client7.sql new file mode 100644 index 000000000..c073663ca --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_client7.sql @@ -0,0 +1,103 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test7; + +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_breakpoint('test_debug_recursive', 5); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_continue(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/contrib/gms_debug/sql/gms_debugger_server1.sql b/contrib/gms_debug/sql/gms_debugger_server1.sql new file mode 100644 index 000000000..8a47326ee --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_server1.sql @@ -0,0 +1,95 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test1 cascade; +create schema gms_debugger_test1; +set search_path = gms_debugger_test1; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); + +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; + +create table show_code_table(lineno int, code text, canBreak bool); + +do $$ +declare + funcoid oid; +begin + select oid from pg_proc into funcoid where proname = 'test_debug'; + INSERT INTO show_code_table SELECT * FROM dbe_pldebugger.info_code(funcoid); +end; +$$; + +select * from show_code_table; + +-- attach fail (target not turned on) +select * from gms_debug.attach_session('datanode1-0'); + +-- turn off without turn on +select * from gms_debug.debug_off(); + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +-- start debug - 1st run +select * from test_debug(4); + +-- start debug - 2nd run - to be aborted +select * from test_debug(4); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test1 cascade; diff --git a/contrib/gms_debug/sql/gms_debugger_server2.sql b/contrib/gms_debug/sql/gms_debugger_server2.sql new file mode 100644 index 000000000..559a9cf57 --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_server2.sql @@ -0,0 +1,32 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test2 cascade; +create schema gms_debugger_test2; +set search_path = gms_debugger_test2; + +-- commit/rollback in procedure +create table tb1(a int); +create or replace procedure test_debug2 as +begin + insert into tb1 values (1000); + commit; + insert into tb1 values (2000); + rollback; +end; +/ + + +-- start debug +select * from gms_debug.initialize(); + +select pg_sleep(1); + +-- start debug - 1st run +select * from test_debug2(); + +-- start debug - 2nd run - to be aborted +select * from test_debug2(); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test2 cascade; diff --git a/contrib/gms_debug/sql/gms_debugger_server3.sql b/contrib/gms_debug/sql/gms_debugger_server3.sql new file mode 100644 index 000000000..830599f5a --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_server3.sql @@ -0,0 +1,39 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test3 cascade; +create schema gms_debugger_test3; +set search_path = gms_debugger_test3; + +-- test for implicit variables +CREATE OR REPLACE function test_debug3(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + ELSE + b := 999; + END CASE; + raise info 'pi_return : %',pi_return ; + return b; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +-- start debug - 1st run +select * from test_debug3(1); + +-- start debug - 2nd run - to be aborted +select * from test_debug3(1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test3 cascade; diff --git a/contrib/gms_debug/sql/gms_debugger_server4.sql b/contrib/gms_debug/sql/gms_debugger_server4.sql new file mode 100644 index 000000000..607e20d76 --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_server4.sql @@ -0,0 +1,97 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test4 cascade; +create schema gms_debugger_test4; +set search_path = gms_debugger_test4; + +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); + +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; + +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +select * from test_debug4(1); + +-- test with client error in exception +select * from test_debug4(1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test4 cascade; diff --git a/contrib/gms_debug/sql/gms_debugger_server5.sql b/contrib/gms_debug/sql/gms_debugger_server5.sql new file mode 100644 index 000000000..1a4ac60b9 --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_server5.sql @@ -0,0 +1,97 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test5 cascade; +create schema gms_debugger_test5; +set search_path = gms_debugger_test5; + +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); + +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; + +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +select * from test_debug4(1); + +-- test with client error in exception +select * from test_debug4(1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test5 cascade; diff --git a/contrib/gms_debug/sql/gms_debugger_server6.sql b/contrib/gms_debug/sql/gms_debugger_server6.sql new file mode 100644 index 000000000..d1ba00029 --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_server6.sql @@ -0,0 +1,97 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test6 cascade; +create schema gms_debugger_test6; +set search_path = gms_debugger_test6; + +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); + +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; + +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +select * from test_debug4(1); + +-- test with client error in exception +select * from test_debug4(1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test6 cascade; diff --git a/contrib/gms_debug/sql/gms_debugger_server7.sql b/contrib/gms_debug/sql/gms_debugger_server7.sql new file mode 100644 index 000000000..3d1625c6a --- /dev/null +++ b/contrib/gms_debug/sql/gms_debugger_server7.sql @@ -0,0 +1,29 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test7 cascade; +create schema gms_debugger_test7; +set search_path = gms_debugger_test7; + +create or replace function test_debug_recursive (ct int, pr int) +returns table (counter int, product int) +language plpgsql +as $$ +begin + return query select ct, pr; + if ct < 5 then + return query select * from test_debug_recursive(ct+ 1, pr * (ct+ 1)); + end if; +end $$; + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +select * from test_debug_recursive (1, 1); + +-- test with client error in exception +select * from test_debug_recursive (1, 1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test7 cascade; diff --git a/src/common/backend/utils/cache/lsyscache.cpp b/src/common/backend/utils/cache/lsyscache.cpp index 5026d8905..420471207 100644 --- a/src/common/backend/utils/cache/lsyscache.cpp +++ b/src/common/backend/utils/cache/lsyscache.cpp @@ -1396,7 +1396,25 @@ Oid get_func_namespace(Oid funcid) return InvalidOid; } } - +/* + * get_func_owner + * + * Returns the pg_user OID associated with a given function. + */ +Oid get_func_owner(Oid funcid) +{ + HeapTuple tp; + tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (HeapTupleIsValid(tp)) { + Form_pg_proc functup = (Form_pg_proc)GETSTRUCT(tp); + Oid result; + result = functup->proowner; + ReleaseSysCache(tp); + return result; + } else { + return InvalidOid; + } +} /* * get_func_rettype * Given procedure id, return the function's result type. diff --git a/src/common/pl/plpgsql/src/pl_debugger.cpp b/src/common/pl/plpgsql/src/pl_debugger.cpp index 666634c20..d209f3f1d 100644 --- a/src/common/pl/plpgsql/src/pl_debugger.cpp +++ b/src/common/pl/plpgsql/src/pl_debugger.cpp @@ -28,6 +28,7 @@ #include "optimizer/pgxcship.h" #include "nodes/readfuncs.h" #include "utils/plpgsql.h" +#include "utils/pl_debug.h" #include "utils/memutils.h" #include "utils/syscache.h" #include "utils/builtins.h" @@ -52,15 +53,15 @@ static bool ActiveBPInFunction(DebugInfo* debug, Oid funcOid); static bool IsBreakPointExisted(DebugInfo* debug, Oid funcOid, int lineno, bool ignoreDisabled); static char* ResizeDebugCommBufferIfNecessary(char* buffer, int* oldSize, int needSize); static void set_debugger_procedure_state(int commIdx, bool state); +static bool is_need_wait(DebugInfo* debug, PLpgSQL_execstate* estate); /* send/rec msg for server */ -static void debug_server_rec_msg(DebugInfo* debug, char* firstChar); -static void debug_server_send_msg(DebugInfo* debug, const char* msg, int msg_len); /* server manage client msg */ static void debug_server_attach(DebugInfo* debug, PLpgSQL_execstate* estate); static void debug_server_local_variables(DebugInfo* debug); static void debug_server_abort(DebugInfo* debug); static void debug_server_add_breakpoint(DebugInfo* debug); +static void gms_debug_server_add_breakpoint(DebugInfo* debug); static void debug_server_delete_breakpoint(DebugInfo* debug); static void debug_server_enable_breakpoint(DebugInfo* debug); static void debug_server_disable_breakpoint(DebugInfo* debug); @@ -69,7 +70,7 @@ static void debug_server_backtrace(); static void debug_server_set_variable(DebugInfo* debug, PLpgSQL_execstate* estate); static void debug_server_info_code(DebugInfo* debug); static char* debug_server_add_breakpoint_invalid(DebugInfo* debug, bool* valid, int lineno, int* newIndex, char* query, char* maskquery); - +static void gms_debug_server_runtime_info(DebugInfo* debug, PLpgSQL_execstate* estate, int reason); /* close each debug function's resource if necessary */ void PlDebugerCleanUp(int code, Datum arg) { @@ -147,6 +148,7 @@ void check_debug(PLpgSQL_function* func, PLpgSQL_execstate* estate) bool need_continue_into = u_sess->plsql_cxt.cur_debug_server != NULL && ActiveBPInFunction(u_sess->plsql_cxt.cur_debug_server, func->fn_oid); bool is_stepinto = u_sess->plsql_cxt.has_step_into; + int gms_debug_idx = u_sess->plsql_cxt.gms_debug_idx; PlDebugEntry* entry = has_debug_func(func->fn_oid, &found); if ((found && u_sess->plsql_cxt.cur_debug_server == NULL) || is_stepinto || need_continue_into) { #ifndef ENABLE_MULTIPLE_NODES @@ -183,8 +185,14 @@ void check_debug(PLpgSQL_function* func, PLpgSQL_execstate* estate) /* set inner debug's state */ if (is_stepinto) { func->debug->cur_opt = DEBUG_STEP_INTO_HEADER_AFTER; + if (gms_debug_idx >= 0) { + func->debug->cur_opt = GMS_DEBUG_STEP_INTO_HEADER_AFTER; + } } else if (need_continue_into) { func->debug->cur_opt = DEBUG_CONTINUE_HEADER_AFTER; + if (gms_debug_idx >= 0) { + func->debug->cur_opt = GMS_DEBUG_CONTINUE_HEADER_AFTER; + } } } else { if (entry != NULL) { @@ -285,6 +293,18 @@ bool handle_debug_msg(DebugInfo* debug, char* firstChar, PLpgSQL_execstate* esta *firstChar = DEBUG_NOTHING_HEADER; need_wait = !send_cur_info(debug, estate, true); break; + case GMS_DEBUG_NEXT_HEADER: + /* wait after execute this sql */ + debug->stop_next_stmt = true; + need_wait = false; + *firstChar = GMS_DEBUG_NEXT_HEADER_AFTER; + break; + case GMS_DEBUG_NEXT_HEADER_AFTER: + *firstChar = DEBUG_NOTHING_HEADER; + debug->stop_next_stmt = true; + need_wait = !is_need_wait(debug, estate); + gms_debug_server_runtime_info(debug, estate, DEBUG_REASON_LINE); + break; case DEBUG_STEP_INTO_HEADER: /* calling inner func if has one */ debug->stop_next_stmt = true; @@ -298,11 +318,31 @@ bool handle_debug_msg(DebugInfo* debug, char* firstChar, PLpgSQL_execstate* esta u_sess->plsql_cxt.has_step_into = false; need_wait = !send_cur_info(debug, estate, true); break; + case GMS_DEBUG_STEP_INTO_HEADER: + /* calling inner func if has one */ + debug->stop_next_stmt = true; + u_sess->plsql_cxt.has_step_into = true; + need_wait = false; + *firstChar = GMS_DEBUG_STEP_INTO_HEADER_AFTER; + break; + case GMS_DEBUG_STEP_INTO_HEADER_AFTER: + /* if don't have inner func, same as next; if is inner func, return first line like attach */ + *firstChar = DEBUG_NOTHING_HEADER; + u_sess->plsql_cxt.has_step_into = false; + debug->stop_next_stmt = true; + need_wait = !is_need_wait(debug, estate); + gms_debug_server_runtime_info(debug, estate, DEBUG_REASON_ENTER); + break; case DEBUG_FINISH_HEADER: debug->stop_next_stmt = false; need_wait = false; *firstChar = DEBUG_FINISH_HEADER_AFTER; break; + case GMS_DEBUG_FINISH_HEADER: + debug->stop_next_stmt = false; + need_wait = false; + *firstChar = GMS_DEBUG_FINISH_HEADER_AFTER; + break; case DEBUG_ABORT_HEADER: /* set abort flag incase has outter function */ *firstChar = DEBUG_ABORT_HEADER_AFTER; @@ -320,11 +360,23 @@ bool handle_debug_msg(DebugInfo* debug, char* firstChar, PLpgSQL_execstate* esta need_wait = false; *firstChar = DEBUG_CONTINUE_HEADER_AFTER; break; + case GMS_DEBUG_CONTINUE_HEADER: + debug->stop_next_stmt = false; + need_wait = false; + *firstChar = GMS_DEBUG_CONTINUE_HEADER_AFTER; + break; case DEBUG_FINISH_HEADER_AFTER: case DEBUG_CONTINUE_HEADER_AFTER: *firstChar = DEBUG_NOTHING_HEADER; need_wait = !send_cur_info(debug, estate, false); break; + case GMS_DEBUG_FINISH_HEADER_AFTER: + case GMS_DEBUG_CONTINUE_HEADER_AFTER: + *firstChar = DEBUG_NOTHING_HEADER; + debug->stop_next_stmt = false; + need_wait = !is_need_wait(debug, estate); + gms_debug_server_runtime_info(debug, estate, DEBUG_REASON_ENTER); + break; case DEBUG_ADDBREAKPOINT_HEADER: debug_server_add_breakpoint(debug); break; @@ -349,6 +401,12 @@ bool handle_debug_msg(DebugInfo* debug, char* firstChar, PLpgSQL_execstate* esta case DEBUG_INFOCODE_HEADER: debug_server_info_code(debug); break; + case GMS_DEBUG_RUNTIMEINFO_HEADER: + gms_debug_server_runtime_info(debug, estate, DEBUG_REASON_NONE); + break; + case GMS_DEBUG_ADDBREAKPOINT_HEADER: + gms_debug_server_add_breakpoint(debug); + break; default: ereport(ERROR, (errmodule(MOD_PLDEBUGGER), (errmsg("received unknown plsql debug client msg: %c", *firstChar)))); @@ -375,7 +433,7 @@ void server_pass_upper_debug_opt(DebugInfo* debug) erraction("Contact Huawei Engineer."))); } outer_debug->cur_opt = debug->cur_opt; - if (debug->cur_opt == DEBUG_FINISH_HEADER_AFTER) { + if (debug->cur_opt == DEBUG_FINISH_HEADER_AFTER || debug->cur_opt == GMS_DEBUG_FINISH_HEADER_AFTER) { outer_debug->stop_next_stmt = true; } } @@ -412,6 +470,18 @@ void server_send_end_msg(DebugInfo* debug) } } +void server_send_gms_end_msg(DebugInfo* debug) +{ + if (debug == u_sess->plsql_cxt.cur_debug_server && IS_AFTER_OPT(debug->cur_opt)) { + MemoryContext old_cxt = MemoryContextSwitchTo(debug->debug_cxt); + /* set procedure end */ + set_debugger_procedure_state(debug->comm->comm_idx, false); + gms_debug_server_runtime_info(debug, NULL, DEBUG_REASON_NONE); + MemoryContextSwitchTo(old_cxt); + debug->cur_opt = DEBUG_NOTHING_HEADER; + } +} + void debug_server_attach(DebugInfo* debug, PLpgSQL_execstate* estate) { MemoryContext old_context = MemoryContextSwitchTo(debug->debug_cxt); @@ -932,6 +1002,19 @@ static bool get_cur_info(StringInfo str, PLpgSQL_execstate* estate, DebugInfo* d return isend; } +static bool is_need_wait(DebugInfo* debug, PLpgSQL_execstate* estate) +{ + bool isend = false; + char* query = get_stmt_query(estate->err_stmt); + if (!query) { + if (debug->debugStackIdx == 0) { + debug->stop_next_stmt = false; + isend = true; + } + } + return isend; +} + template char* GetSqlString(PLpgSQL_stmt* stmt) { @@ -1011,6 +1094,18 @@ static bool IsBreakPointExisted(DebugInfo* debug, Oid funcOid, int lineno, bool return false; } +static PLDebug_breakPoint* GetBreakPoint(DebugInfo* debug, Oid funcOid, int lineno, bool ignoreDisabled) +{ + ListCell* lc = NULL; + foreach(lc, debug->bp_list) { + PLDebug_breakPoint* bp = (PLDebug_breakPoint*)lfirst(lc); + if (bp->funcoid == funcOid && bp->lineno == lineno && !bp->deleted && (bp->active || !ignoreDisabled)) { + return bp; + } + } + return NULL; +} + static void debug_server_add_breakpoint(DebugInfo* debug) { /* Validity of the parameter are checked on client side */ @@ -1424,6 +1519,25 @@ bool delete_debug_func(Oid key) return found; } +void add_gms_debug_func(Oid funcOid, int commIdx) +{ + if (unlikely(u_sess->plsql_cxt.debug_proc_htbl == NULL)) { + ereport(ERROR, + (errmodule(MOD_PLDEBUGGER), errcode(ERRCODE_UNEXPECTED_NULL_VALUE), + errmsg("Get null pointer for variable name"), + errcause("Debugger session is not properly initialized."), + erraction("Please initialize debug session, and try again."))); + } + bool found = false; + PlDebugEntry* entry = (PlDebugEntry*)hash_search(u_sess->plsql_cxt.debug_proc_htbl, + (void*)(&funcOid), HASH_ENTER, &found); + entry->key = funcOid; + if (!found) { + entry->commIdx = commIdx; + entry->func = NULL; + } +} + void ReleaseDebugCommIdx(int idx) { if (idx < 0 || idx >= PG_MAX_DEBUG_CONN) @@ -1484,6 +1598,33 @@ int GetValidDebugCommIdx() return idx; } +bool AcquireDebugCommIdx(int commIdx) +{ + bool rc = false; + (void)LWLockAcquire(PldebugLock, LW_EXCLUSIVE); + if (!g_instance.pldebug_cxt.debug_comm[commIdx].Used()) { + PlDebuggerComm* comm = &g_instance.pldebug_cxt.debug_comm[commIdx]; + comm->hasUsed = true; + comm->hasClientFlushed = false; + comm->hasServerFlushed = false; + comm->IsServerWaited = false; + comm->IsClientWaited = false; + comm->hasClientFlushed = false; + comm->hasServerFlushed = false; + comm->hasServerErrorOccured = false; + comm->hasClientErrorOccured = false; + comm->isProcdeureRunning = false; + comm->serverId = ENABLE_THREAD_POOL ? u_sess->session_id : t_thrd.proc_cxt.MyProcPid; + comm->clientId = 0; + comm->bufLen = 0; + comm->bufSize = 0; + comm->buffer = NULL; /* init buffer when need use it */ + rc = true; + } + LWLockRelease(PldebugLock); + return rc; +} + bool WakeUpReceiver(int commIdx, bool isClient) { PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[commIdx]; @@ -1719,7 +1860,7 @@ void RecvUnixMsg(const char* buf, int bufLen, char* destBuf, int destLen) } /* buffer contains : buffer size + buffer msg */ -static void debug_server_send_msg(DebugInfo* debug, const char* msg, int msg_len) +void debug_server_send_msg(DebugInfo* debug, const char* msg, int msg_len) { MemoryContext old_context = MemoryContextSwitchTo(debug->debug_cxt); @@ -1750,7 +1891,7 @@ static void debug_server_send_msg(DebugInfo* debug, const char* msg, int msg_len (void)MemoryContextSwitchTo(old_context); } -static void debug_server_rec_msg(DebugInfo* debug, char* firstChar) +void debug_server_rec_msg(DebugInfo* debug, char* firstChar) { MemoryContext old_context = MemoryContextSwitchTo(debug->debug_cxt); char* copyBuf = NULL; @@ -1893,3 +2034,119 @@ static void debug_server_info_code(DebugInfo* debug) pfree(buf); } + +static void gms_debug_server_runtime_info(DebugInfo* debug, PLpgSQL_execstate* estate, int reason) +{ + Oid funcoid = debug->func->fn_oid; + char* funcname = NULL; + if (OidIsValid(funcoid)) { + funcname = get_func_name(funcoid); + } else { + funcname = pstrdup("anonymous block"); + } + Oid pkgoid = debug->func->pkg_oid; + Assert(funcname != NULL); + int lineno = -1; + if (estate != NULL) { + lineno = estate->err_stmt->lineno; + } else { + lineno = debug->cur_stmt->lineno; + } + int breakpoint = -1; + Oid namespaceoid = get_func_namespace(funcoid); + int owneroid = get_func_owner(funcoid); + int stackdepth = debug->debugStackIdx; + char* pkgname = NULL; + if (pkgoid != InvalidOid) { + pkgname = GetPackageName(pkgoid); + } + char* pkgfuncname = quote_qualified_identifier(pkgname, funcname); + PLDebug_breakPoint* bp = GetBreakPoint(debug, funcoid, lineno, true); + if(bp) { + breakpoint = bp->bpIndex; + reason = DEBUG_REASON_BREAKPOINT; + } + StringInfoData str; + initStringInfo(&str); + appendStringInfo(&str, "%u:%u:%u:%d:%d:%d:%d:%s", funcoid, namespaceoid, owneroid, lineno, breakpoint, stackdepth, reason, pkgfuncname); + debug_server_send_msg(debug, str.data, str.len); + pfree(str.data); + pfree(pkgfuncname); + pfree(funcname); +} + +static void gms_debug_server_add_breakpoint(DebugInfo* debug) +{ + /* Validity of the parameter are checked on client side */ + MemoryContext old_context = MemoryContextSwitchTo(debug->debug_cxt); + + /* Received buffer will be in the form of */ + char* psave = NULL; + char* fir = strtok_r(debug->comm->rec_buffer, ":", &psave); + const int int64Size = 10; + char* new_fir = TrimStr(fir); + if (new_fir == NULL) { + ReportInvalidMsg(debug->comm->rec_buffer); + return; + } + uint64 clientSessionId = (uint64)pg_strtouint64(new_fir, NULL, int64Size); + fir = strtok_r(NULL, ":", &psave); + new_fir = TrimStr(fir); + if (new_fir == NULL) { + ReportInvalidMsg(debug->comm->rec_buffer); + return; + } + Oid funcOid = (Oid)pg_strtouint64(new_fir, NULL, int64Size); + fir = strtok_r(NULL, ":", &psave); + new_fir = TrimStr(fir); + if (new_fir == NULL) { + ReportInvalidMsg(debug->comm->rec_buffer); + return; + } + int lineno = (int)pg_strtouint64(new_fir, NULL, int64Size); + char* query = pstrdup(psave); + char* maskquery = (query == NULL) ? NULL : maskPassword(query); + if (maskquery != NULL && maskquery != query) { + pfree_ext(query); + query = maskquery; + } + fir = strtok_r(NULL, ":", &psave); + new_fir = TrimStr(fir); + if (new_fir == NULL) { + ReportInvalidMsg(debug->comm->rec_buffer); + return; + } + /* set comm's session id */ + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[debug->comm->comm_idx]; + AutoMutexLock debuglock(&debug_comm->mutex); + debuglock.lock(); + debug_comm->clientId = clientSessionId; + debuglock.unLock(); + + int newIndex = -1; + bool valid = true; + if (funcOid == InvalidOid) { + query = debug_server_add_breakpoint_invalid(debug, &valid, lineno, &newIndex, query, maskquery); + } + + if (valid && !IsBreakPointExisted(debug, funcOid, lineno, false)) { + PLDebug_breakPoint* bp = (PLDebug_breakPoint*)makeNode(PLDebug_breakPoint); + bp->bpIndex = list_length(debug->bp_list); + bp->funcoid = funcOid; + bp->lineno = lineno; + bp->active = true; + bp->deleted = false; + bp->query = query; + debug->bp_list = lappend(debug->bp_list, bp); + newIndex = bp->bpIndex; + } + + (void)MemoryContextSwitchTo(old_context); + + StringInfoData str; + initStringInfo(&str); + appendStringInfo(&str, "%d", newIndex); + debug_server_send_msg(debug, str.data, str.len); + pfree(str.data); + str.data = NULL; +} diff --git a/src/common/pl/plpgsql/src/pl_debugger_api.cpp b/src/common/pl/plpgsql/src/pl_debugger_api.cpp index a1e95a493..058397564 100644 --- a/src/common/pl/plpgsql/src/pl_debugger_api.cpp +++ b/src/common/pl/plpgsql/src/pl_debugger_api.cpp @@ -28,6 +28,7 @@ #include "funcapi.h" #include "utils/acl.h" #include "utils/plpgsql.h" +#include "utils/pl_debug.h" #include /* @@ -47,29 +48,18 @@ dbe_pldebugger.step */ -typedef struct { - char* nodename; - int port; - Oid funcoid; -} DebuggerServerInfo; - /* send/rec msg for client */ -static void debug_client_rec_msg(DebugClientInfo* client); -static void debug_client_send_msg(DebugClientInfo* client, char first_char, char* msg, int msg_len); static Datum get_info_local_data(const char* var_name, const int frameno, FunctionCallInfo fcinfo, bool show_all); static Datum get_tuple_lineno_and_query(DebugClientInfo* client); static void InterfaceCheck(const char* funcname, bool needAttach = true); static PlDebugEntry* add_debug_func(Oid key); static void* get_debug_entries(uint32* num); -static DebugClientInfo* InitDebugClient(int comm_idx); static void* debug_client_split_breakpoints_msg(uint32* num); static void* debug_client_split_localvariables_msg(uint32 *num); static void* debug_client_split_backtrace_msg(uint32* num); static void* debug_client_split_infocode_msg(uint32* num); static List* collect_breakable_line_oid(Oid funcOid); -static void init_pldebug_htcl(); -static bool CheckPlpgsqlFunc(Oid funcoid, bool report_error = true); static Datum get_tuple_lineno_and_query(DebugClientInfo* client) { @@ -132,7 +122,7 @@ static Datum get_tuple_lineno_and_query(DebugClientInfo* client) PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); } -static void check_debugger_valid(int commidx) +void check_debugger_valid(int commidx) { PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[commidx]; AutoMutexLock debuglock(&debug_comm->mutex); @@ -161,6 +151,31 @@ static void check_debugger_valid(int commidx) debuglock.unLock(); } +void attach_session(int commidx, uint64 sid) +{ + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[commidx]; + AutoMutexLock debuglock(&debug_comm->mutex); + debuglock.lock(); + if (debug_comm->Used()) { + if (debug_comm->hasClient()) { + debuglock.unLock(); + ereport(ERROR, (errmodule(MOD_PLDEBUGGER), errcode(ERRCODE_TARGET_SERVER_ALREADY_ATTACHED), + (errmsg("procedure has already attached on other client.")))); + } + if (debug_comm->hasClientErrorOccured || debug_comm->hasServerErrorOccured) { + debuglock.unLock(); + ereport(ERROR, (errmodule(MOD_PLDEBUGGER), errcode(ERRCODE_INVALID_OPERATION), + (errmsg("procedure is not running in expected way.")))); + } + debug_comm->clientId = sid; + } else { + debuglock.unLock(); + ereport(ERROR, (errmodule(MOD_PLDEBUGGER), errcode(ERRCODE_INVALID_OPERATION), + (errmsg("Corresponding procedure not turn on yet.")))); + } + debuglock.unLock(); +} + /* * dbe_pldebugger.attach * attach debug client to debug server @@ -1045,7 +1060,7 @@ static void InterfaceCheck(const char* funcname, bool needAttach) } } -static void debug_client_rec_msg(DebugClientInfo* client) +void debug_client_rec_msg(DebugClientInfo* client) { CHECK_DEBUG_COMM_VALID(client->comm_idx); @@ -1129,7 +1144,7 @@ static void* get_debug_entries(uint32* num) return entry_array; } -static DebugClientInfo* InitDebugClient(int comm_idx) +DebugClientInfo* InitDebugClient(int comm_idx) { MemoryContext debug_context = AllocSetContextCreate(u_sess->cache_mem_cxt, "ClientDebugContext", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); @@ -1364,7 +1379,7 @@ CodeLine* debug_show_code_worker(Oid funcid, uint32* num, int* headerlines) return ret; } -static void debug_client_send_msg(DebugClientInfo* client, char first_char, char* msg, int msg_len) +void debug_client_send_msg(DebugClientInfo* client, char first_char, char* msg, int msg_len) { MemoryContext old_context = MemoryContextSwitchTo(client->context); const int EXTRA_LEN = 4; @@ -1396,7 +1411,7 @@ static void debug_client_send_msg(DebugClientInfo* client, char first_char, char } /* debug_proc_htbl function */ -static void init_pldebug_htcl() +void init_pldebug_htcl() { if (u_sess->plsql_cxt.debug_proc_htbl) return; @@ -1420,7 +1435,7 @@ static void init_pldebug_htcl() hash_create("Debug Func Table", debugSize, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); } -static bool CheckPlpgsqlFunc(Oid funcoid, bool report_error) +bool CheckPlpgsqlFunc(Oid funcoid, bool report_error) { char* langname = get_func_langname(funcoid); if (strcmp(langname, "plpgsql") != 0) { @@ -1556,3 +1571,18 @@ List* collect_breakable_line(PLpgSQL_function* func) collect_breakable_line_walker(block->body, &lines); return lines; } + +bool SetDebugCommGmsUsed(int commIdx, bool flag) +{ + bool rc = false; + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[commIdx]; + AutoMutexLock debuglock(&debug_comm->mutex); + debuglock.lock(); + if (debug_comm->Used()) { + debug_comm->isGmsDebug = flag; + rc = true; + } + debuglock.unLock(); + return rc; +} + diff --git a/src/common/pl/plpgsql/src/pl_exec.cpp b/src/common/pl/plpgsql/src/pl_exec.cpp index 17821a996..71f20f99f 100644 --- a/src/common/pl/plpgsql/src/pl_exec.cpp +++ b/src/common/pl/plpgsql/src/pl_exec.cpp @@ -1696,6 +1696,10 @@ Datum plpgsql_exec_function(PLpgSQL_function* func, if (u_sess->dolphin_errdata_ctx.handler_active) u_sess->dolphin_errdata_ctx.handler_active = false; #ifndef ENABLE_MULTIPLE_NODES + int gms_debug_idx = u_sess->plsql_cxt.gms_debug_idx; + if (gms_debug_idx >= 0) { + add_gms_debug_func(func->fn_oid, gms_debug_idx); + } check_debug(func, &estate); bool isExecAutoFunc = u_sess->is_autonomous_session == true && u_sess->SPI_cxt._connected == 0; bool isInitPkg = u_sess->plsql_cxt.curr_compile_context != NULL && diff --git a/src/common/pl/plpgsql/src/pl_handler.cpp b/src/common/pl/plpgsql/src/pl_handler.cpp index f50fdf882..d4fc2d8d8 100755 --- a/src/common/pl/plpgsql/src/pl_handler.cpp +++ b/src/common/pl/plpgsql/src/pl_handler.cpp @@ -799,7 +799,6 @@ Datum plpgsql_call_handler(PG_FUNCTION_ARGS) int pkgDatumsNumber = 0; bool savedisAllowCommitRollback = true; bool enableProcCoverage = u_sess->attr.attr_common.enable_proc_coverage; - /* Check if type body exists if using type method */ HeapTuple proc_tup = NULL; @@ -1062,7 +1061,12 @@ Datum plpgsql_call_handler(PG_FUNCTION_ARGS) /* debug finished, close debug resource */ if (func->debug) { /* if debuger is waiting for end msg, send end */ - server_send_end_msg(func->debug); + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[func->debug->comm->comm_idx]; + if (debug_comm->isGmsRunning()) { + server_send_gms_end_msg(func->debug); + } else { + server_send_end_msg(func->debug); + } /* pass opt to upper debug function */ server_pass_upper_debug_opt(func->debug); clean_up_debug_server(func->debug, false, true); @@ -1098,7 +1102,13 @@ Datum plpgsql_call_handler(PG_FUNCTION_ARGS) /* debug finished, close debug resource */ if (func->debug) { /* if debuger is waiting for end msg, send end */ - server_send_end_msg(func->debug); + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[func->debug->comm->comm_idx]; + /* if debuger is waiting for end msg, send end */ + if (debug_comm->isGmsRunning()) { + server_send_gms_end_msg(func->debug); + } else { + server_send_end_msg(func->debug); + } /* pass opt to upper debug function */ server_pass_upper_debug_opt(func->debug); clean_up_debug_server(func->debug, false, true); @@ -1389,8 +1399,13 @@ Datum plpgsql_inline_handler(PG_FUNCTION_ARGS) #ifndef ENABLE_MULTIPLE_NODES /* debug finished, close debug resource */ if (func->debug) { + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[func->debug->comm->comm_idx]; /* if debuger is waiting for end msg, send end */ - server_send_end_msg(func->debug); + if (debug_comm->isGmsRunning()) { + server_send_gms_end_msg(func->debug); + } else { + server_send_end_msg(func->debug); + } /* pass opt to upper debug function */ server_pass_upper_debug_opt(func->debug); clean_up_debug_server(func->debug, false, true); @@ -1435,7 +1450,13 @@ Datum plpgsql_inline_handler(PG_FUNCTION_ARGS) /* debug finished, close debug resource */ if (func->debug) { /* if debuger is waiting for end msg, send end */ - server_send_end_msg(func->debug); + PlDebuggerComm* debug_comm = &g_instance.pldebug_cxt.debug_comm[func->debug->comm->comm_idx]; + /* if debuger is waiting for end msg, send end */ + if (debug_comm->isGmsRunning()) { + server_send_gms_end_msg(func->debug); + } else { + server_send_end_msg(func->debug); + } /* pass opt to upper debug function */ server_pass_upper_debug_opt(func->debug); clean_up_debug_server(func->debug, false, true); diff --git a/src/gausskernel/process/threadpool/knl_session.cpp b/src/gausskernel/process/threadpool/knl_session.cpp index 3895433b2..4ef1147f4 100755 --- a/src/gausskernel/process/threadpool/knl_session.cpp +++ b/src/gausskernel/process/threadpool/knl_session.cpp @@ -840,6 +840,7 @@ static void knl_u_plpgsql_init(knl_u_plpgsql_context* plsql_cxt) plsql_cxt->rendezvousHash = NULL; plsql_cxt->debug_proc_htbl = NULL; plsql_cxt->debug_client = NULL; + plsql_cxt->gms_debug_idx = -1; plsql_cxt->has_step_into = false; plsql_cxt->cur_debug_server = NULL; plsql_cxt->dbe_output_buffer_limit = DEFAULT_DBE_BUFFER_LIMIT; diff --git a/src/include/knl/knl_instance.h b/src/include/knl/knl_instance.h index 9b28402af..4a916d381 100755 --- a/src/include/knl/knl_instance.h +++ b/src/include/knl/knl_instance.h @@ -1147,6 +1147,8 @@ typedef struct PlDebuggerComm { volatile bool hasClientErrorOccured; /* true during debug procedure is running */ volatile bool isProcdeureRunning; + /* true if pacakge gms_debug used */ + volatile bool isGmsDebug; volatile uint32 lock; /* 1 means locked, 0 means unlock */ volatile uint64 serverId; volatile uint64 clientId; @@ -1168,6 +1170,11 @@ typedef struct PlDebuggerComm { pg_memory_barrier(); return isProcdeureRunning; } + inline bool isGmsRunning() + { + pg_memory_barrier(); + return isGmsDebug; + } } PlDebuggerComm; typedef struct knl_g_pldebug_context { diff --git a/src/include/knl/knl_session.h b/src/include/knl/knl_session.h index 7a8b50848..17d6ece19 100644 --- a/src/include/knl/knl_session.h +++ b/src/include/knl/knl_session.h @@ -1651,6 +1651,11 @@ typedef struct knl_u_plpgsql_context { bool has_step_into; + /*This is a temporary comm_id, because when gms_debug is enabled, + it does not immediately bind to a specific function, + so a temporary id is needed as a transition.*/ + int gms_debug_idx; + /* dbe.output buffer limit */ uint32 dbe_output_buffer_limit; Oid running_pkg_oid; diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index f68deba93..a6ac45f3b 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -74,6 +74,7 @@ extern RegProcedure get_oprrest(Oid opno); extern RegProcedure get_oprjoin(Oid opno); extern char* get_func_name(Oid funcid); extern Oid get_func_namespace(Oid funcid); +extern Oid get_func_owner(Oid funcid); extern Oid get_func_rettype(Oid funcid); extern int get_func_nargs(Oid funcid); extern Oid get_func_signature(Oid funcid, Oid** argtypes, int* nargs); diff --git a/src/include/utils/pl_debug.h b/src/include/utils/pl_debug.h new file mode 100644 index 000000000..aea2e5444 --- /dev/null +++ b/src/include/utils/pl_debug.h @@ -0,0 +1,53 @@ +/* --------------------------------------------------------------------------------------- + * + * pl_debug.h + * Declarations for operations on log sequence numbers (LSNs) of + * PostgreSQL. + * + * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/include/utils/pl_debug.h + * + * --------------------------------------------------------------------------------------- + */ +#ifndef PL_DEBUG_H +#define PL_DEBUG_H + +#include "utils/plpgsql.h" + +#define DEBUG_REASON_NONE 0 +#define DEBUG_REASON_INTERPRETER_STARTING 1 +#define DEBUG_REASON_BREAKPOINT 2 +#define DEBUG_REASON_ENTER 3 +#define DEBUG_REASON_RETURN 4 +#define DEBUG_REASON_FINISH 5 +#define DEBUG_REASON_LINE 6 +#define DEBUG_REASON_INTERRUPT 7 +#define DEBUG_REASON_EXCEPTION 8 +#define DEBUG_REASON_EXIT 9 +#define DEBUG_REASON_KNL_EXIT 10 +#define DEBUG_REASON_HANDLER 11 +#define DEBUG_REASON_TIMEOUT 12 +#define DEBUG_REASON_INSTANTIATE 13 +#define DEBUG_REASON_ABORT 14 + +typedef struct { + char* nodename; + int port; + Oid funcoid; +} DebuggerServerInfo; + +extern void debug_server_rec_msg(DebugInfo* debug, char* firstChar); +extern void debug_server_send_msg(DebugInfo* debug, const char* msg, int msg_len); +extern void debug_client_rec_msg(DebugClientInfo* client); +extern void debug_client_send_msg(DebugClientInfo* client, char first_char, char* msg, int msg_len); +extern void init_pldebug_htcl(); +extern bool AcquireDebugCommIdx(int commIdx); +extern void check_debugger_valid(int commidx); +extern void attach_session(int commidx, uint64 sid); +extern bool CheckPlpgsqlFunc(Oid funcoid, bool report_error = true); +extern DebugClientInfo* InitDebugClient(int comm_idx); +#endif /* PL_DEBUG_H */ diff --git a/src/include/utils/plpgsql.h b/src/include/utils/plpgsql.h index ecadc93ed..9c334f63e 100644 --- a/src/include/utils/plpgsql.h +++ b/src/include/utils/plpgsql.h @@ -1685,6 +1685,18 @@ const char DEBUG_STEP_INTO_HEADER_AFTER = 'S'; const char DEBUG_BACKTRACE_HEADER = 't'; const char DEBUG_SET_VARIABLE_HEADER = 'h'; const char DEBUG_INFOCODE_HEADER = 'i'; +const char GMS_DEBUG_NEXT_HEADER = 'd'; +const char GMS_DEBUG_NEXT_HEADER_AFTER = 'D'; +const char GMS_DEBUG_STEP_INTO_HEADER = 'j'; +const char GMS_DEBUG_STEP_INTO_HEADER_AFTER = 'J'; +const char GMS_DEBUG_FINISH_HEADER = 'k'; +const char GMS_DEBUG_FINISH_HEADER_AFTER = 'K'; +const char GMS_DEBUG_ABORT_HEADER = 'l'; +const char GMS_DEBUG_ABORT_HEADER_AFTER = 'L'; +const char GMS_DEBUG_CONTINUE_HEADER = 'o'; +const char GMS_DEBUG_CONTINUE_HEADER_AFTER = 'O'; +const char GMS_DEBUG_RUNTIMEINFO_HEADER = 'm'; +const char GMS_DEBUG_ADDBREAKPOINT_HEADER = 'x'; /* server return message */ const int DEBUG_SERVER_SUCCESS = 0; @@ -1778,6 +1790,7 @@ void server_pass_upper_debug_opt(DebugInfo* debug); void clean_up_debug_client(bool hasError = false); void clean_up_debug_server(DebugInfo* debug, bool sessClose, bool hasError); void server_send_end_msg(DebugInfo* debug); +void server_send_gms_end_msg(DebugInfo* debug); int GetValidDebugCommIdx(); void WaitSendMsg(int commIdx, bool isClient, char** destBuffer, int* destLen); bool WakeUpReceiver(int commIdx, bool isClient); @@ -1786,11 +1799,13 @@ extern void ReportInvalidMsg(const char* buf); extern char* AssignStr(char* src, bool copy = true); extern PlDebugEntry* has_debug_func(Oid key, bool* found); extern bool delete_debug_func(Oid key); +extern void add_gms_debug_func(Oid funcOid, int commIdx); extern void RecvUnixMsg(const char* buf, int bufLen, char* destBuf, int destLen); extern char* ResizeDebugBufferIfNecessary(char* buffer, int* oldSize, int needSize); extern void ReleaseDebugCommIdx(int idx); extern void SendUnixMsg(int socket, const char* val, int len, bool is_client); extern List* collect_breakable_line(PLpgSQL_function* func); +extern bool SetDebugCommGmsUsed(int commIdx, bool flag); /********************************************************************** * Function declarations diff --git a/src/test/regress/expected/gms_debugger_client1.out b/src/test/regress/expected/gms_debugger_client1.out new file mode 100644 index 000000000..b02d884ff --- /dev/null +++ b/src/test/regress/expected/gms_debugger_client1.out @@ -0,0 +1,269 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test1; +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_info() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.get_runtime_info(1, run_info); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_breakpoint('test_debug', 0); -- negative +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 15); -- ok +NOTICE: ret= 1 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 17); -- invalid +NOTICE: ret= 2 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 22); -- ok +NOTICE: ret= 3 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 15); -- ok +WARNING: the given line number already contains a valid breakpoint. +CONTEXT: SQL statement "CALL gms_debug.add_breakpoint(proid,line#,breakpoint#,fuzzy,iterations,sts)" +PL/pgSQL function gms_debug.set_breakpoint(gms_debug.program_info,integer,integer,integer) line 8 at SQL statement +SQL statement "CALL gms_debug.set_breakpoint(pro_info,lineno,bkline,1,1)" +PL/pgSQL function gms_breakpoint(text,integer) line 8 at assignment +referenced column: gms_breakpoint +NOTICE: ret= -1 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= -3 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 16 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 19 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 19 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= 3 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 22 +CONTEXT: referenced column: gms_next +NOTICE: reason= 2 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 46 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 46 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_debug.detach_session(); +connection to server was lost diff --git a/src/test/regress/expected/gms_debugger_client2.out b/src/test/regress/expected/gms_debugger_client2.out new file mode 100644 index 000000000..845517858 --- /dev/null +++ b/src/test/regress/expected/gms_debugger_client2.out @@ -0,0 +1,144 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test2; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 3 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 4 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 5 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 0 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_debug.detach_session(); + detach_session +---------------- + +(1 row) + diff --git a/src/test/regress/expected/gms_debugger_client3.out b/src/test/regress/expected/gms_debugger_client3.out new file mode 100644 index 000000000..58f388db4 --- /dev/null +++ b/src/test/regress/expected/gms_debugger_client3.out @@ -0,0 +1,144 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test3; +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 6 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 10 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 14 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 6 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_debug.detach_session(); + detach_session +---------------- + +(1 row) + diff --git a/src/test/regress/expected/gms_debugger_client4.out b/src/test/regress/expected/gms_debugger_client4.out new file mode 100644 index 000000000..0024bb853 --- /dev/null +++ b/src/test/regress/expected/gms_debugger_client4.out @@ -0,0 +1,192 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test4; +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_step +NOTICE: line= 6 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_step +NOTICE: line= 7 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_step +NOTICE: line= 15 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_step +NOTICE: line= 16 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_step +NOTICE: line= 19 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 14 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 3 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 15 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_debug.detach_session(); + detach_session +---------------- + +(1 row) + diff --git a/src/test/regress/expected/gms_debugger_client5.out b/src/test/regress/expected/gms_debugger_client5.out new file mode 100644 index 000000000..6a030edd3 --- /dev/null +++ b/src/test/regress/expected/gms_debugger_client5.out @@ -0,0 +1,200 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test5; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_finish() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 8, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 6 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 7 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_next(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_next +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_next +NOTICE: line= 14 +CONTEXT: referenced column: gms_next +NOTICE: reason= 6 +CONTEXT: referenced column: gms_next +NOTICE: ret= 0 +CONTEXT: referenced column: gms_next + gms_next +---------- + +(1 row) + +select gms_step(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_step +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_step +NOTICE: line= 15 +CONTEXT: referenced column: gms_step +NOTICE: reason= 3 +CONTEXT: referenced column: gms_step +NOTICE: ret= 0 +CONTEXT: referenced column: gms_step + gms_step +---------- + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_finish +NOTICE: line= 15 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 0 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_finish +NOTICE: line= 15 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 0 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_debug.detach_session(); +connection to server was lost diff --git a/src/test/regress/expected/gms_debugger_client6.out b/src/test/regress/expected/gms_debugger_client6.out new file mode 100644 index 000000000..f58a3ac9c --- /dev/null +++ b/src/test/regress/expected/gms_debugger_client6.out @@ -0,0 +1,177 @@ +-- wait for server establishment +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; +set search_path = gms_debugger_test6; +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; +CREATE or REPLACE FUNCTION gms_finish() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 8, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + attach_session +---------------- + +(1 row) + +select pg_sleep(3); + pg_sleep +---------- + +(1 row) + +select gms_breakpoint('test_debug', 31); +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_breakpoint('test_debug', 44); +NOTICE: ret= 1 +CONTEXT: referenced column: gms_breakpoint +NOTICE: ret= 0 +CONTEXT: referenced column: gms_breakpoint + gms_breakpoint +---------------- + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= 0 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_finish +NOTICE: line= 31 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 2 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= 1 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 1 +CONTEXT: referenced column: gms_finish +NOTICE: line= 44 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 2 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_finish(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_finish +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_finish +NOTICE: line= 14 +CONTEXT: referenced column: gms_finish +NOTICE: reason= 3 +CONTEXT: referenced column: gms_finish +NOTICE: ret= 0 +CONTEXT: referenced column: gms_finish + gms_finish +------------ + +(1 row) + +select gms_continue(); +NOTICE: breakpoint= -1 +CONTEXT: referenced column: gms_continue +NOTICE: stackdepth= 0 +CONTEXT: referenced column: gms_continue +NOTICE: line= 15 +CONTEXT: referenced column: gms_continue +NOTICE: reason= 0 +CONTEXT: referenced column: gms_continue +NOTICE: ret= 0 +CONTEXT: referenced column: gms_continue + gms_continue +-------------- + +(1 row) + +select gms_debug.detach_session(); + detach_session +---------------- + +(1 row) + diff --git a/src/test/regress/expected/gms_debugger_server1.out b/src/test/regress/expected/gms_debugger_server1.out new file mode 100644 index 000000000..5e9758952 --- /dev/null +++ b/src/test/regress/expected/gms_debugger_server1.out @@ -0,0 +1,181 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test1 cascade; +NOTICE: schema "gms_debugger_test1" does not exist, skipping +create schema gms_debugger_test1; +set search_path = gms_debugger_test1; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; +create table show_code_table(lineno int, code text, canBreak bool); +do $$ +declare + funcoid oid; +begin + select oid from pg_proc into funcoid where proname = 'test_debug'; + INSERT INTO show_code_table SELECT * FROM dbe_pldebugger.info_code(funcoid); +end; +$$; +select * from show_code_table; + lineno | code | canbreak +--------+----------------------------------------------------------------------------------+---------- + | CREATE OR REPLACE FUNCTION gms_debugger_test1.test_debug(x integer) | f + | RETURNS SETOF test | f + | LANGUAGE plpgsql | f + | NOT FENCED NOT SHIPPABLE | f + 1 | AS $function$ | f + 2 | DECLARE | f + 3 | sql_stmt VARCHAR2(500); | f + 4 | r test%rowtype; | f + 5 | rec record; | f + 6 | b_tmp text; | f + 7 | cnt int; | f + 8 | a_tmp int; | f + 9 | cur refcursor; | f + 10 | n_tmp NUMERIC(24,6); | f + 11 | t_tmp tsquery; | f + 12 | CURSOR cur_arg(criterion INTEGER) IS | f + 13 | SELECT * FROM test WHERE a < criterion; | f + 14 | BEGIN | f + 15 | cnt := 0; | t + 16 | FOR r IN SELECT * FROM test | t + 17 | WHERE a > x | f + 18 | LOOP | f + 19 | RETURN NEXT r; | t + 20 | END LOOP; | f + 21 | | f + 22 | FOR rec in SELECT * FROM test | t + 23 | WHERE a < x | f + 24 | LOOP | f + 25 | RETURN NEXT rec; | t + 26 | END LOOP; | f + 27 | | f + 28 | FORALL index_1 IN 0..1 | t + 29 | INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); | t + 30 | | f + 31 | SELECT b FROM test where a = 7 INTO b_tmp; | t + 32 | sql_stmt := 'select a from test where b = :1;'; | t + 33 | OPEN cur FOR sql_stmt USING b_tmp; | t + 34 | IF cur%isopen then LOOP | t + 35 | FETCH cur INTO a_tmp; | t + 36 | EXIT WHEN cur%notfound; | t + 37 | END LOOP; | f + 38 | END IF; | f + 39 | CLOSE cur; | t + 40 | WHILE cnt < 3 LOOP | t + 41 | cnt := cnt + 1; | t + 42 | END LOOP; | f + 43 | | f + 44 | RAISE INFO 'cnt is %', cnt; | t + 45 | | f + 46 | RETURN; | t + 47 | | f + 48 | END | f + 49 | $function$; | f +(53 rows) + +-- attach fail (target not turned on) +select * from gms_debug.attach_session('datanode1-0'); +ERROR: target session should be init first. +-- turn off without turn on +select * from gms_debug.debug_off(); +ERROR: target session should be init first +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +-- start debug - 1st run +select * from test_debug(4); +INFO: cnt is 3 + a | b | c +---+---------------------+-------------------------- + 5 | Fasting | Sat Mar 02 00:00:00 2030 + 7 | Treasures in Heaven | Mon Apr 02 00:00:00 2040 + 2 | Giving to the Needy | Sun Feb 02 00:00:00 2020 + 3 | Prayer | Thu Dec 02 00:00:00 2021 +(4 rows) + +-- start debug - 2nd run - to be aborted +select * from test_debug(4); +INFO: cnt is 3 + a | b | c +---+-----------------------+-------------------------- + 5 | Fasting | Sat Mar 02 00:00:00 2030 + 7 | Treasures in Heaven | Mon Apr 02 00:00:00 2040 + 2 | Giving to the Needy | Sun Feb 02 00:00:00 2020 + 3 | Prayer | Thu Dec 02 00:00:00 2021 + 0 | Happy Children's Day! | Tue Jun 01 00:00:00 2021 + 1 | Happy Children's Day! | Tue Jun 01 00:00:00 2021 +(6 rows) + +drop schema gms_debugger_test1 cascade; +NOTICE: drop cascades to 8 other objects +DETAIL: drop cascades to table test +drop cascades to function test_debug(integer) +drop cascades to table show_code_table +drop cascades to function gms_breakpoint(text,integer) +drop cascades to function gms_continue() +drop cascades to function gms_next() +drop cascades to function gms_step() +drop cascades to function gms_info() diff --git a/src/test/regress/expected/gms_debugger_server2.out b/src/test/regress/expected/gms_debugger_server2.out new file mode 100644 index 000000000..e3106e5bb --- /dev/null +++ b/src/test/regress/expected/gms_debugger_server2.out @@ -0,0 +1,46 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test2 cascade; +NOTICE: schema "gms_debugger_test2" does not exist, skipping +create schema gms_debugger_test2; +set search_path = gms_debugger_test2; +-- commit/rollback in procedure +create table tb1(a int); +create or replace procedure test_debug2 as +begin + insert into tb1 values (1000); + commit; + insert into tb1 values (2000); + rollback; +end; +/ +-- start debug +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +-- start debug - 1st run +select * from test_debug2(); + test_debug2 +------------- + +(1 row) + +-- start debug - 2nd run - to be aborted +select * from test_debug2(); +ERROR: receive abort message +CONTEXT: PL/pgSQL function test_debug2() line 2 at SQL statement +drop schema gms_debugger_test2 cascade; +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to table tb1 +drop cascades to function test_debug2() +drop cascades to function gms_continue() +drop cascades to function gms_next() diff --git a/src/test/regress/expected/gms_debugger_server3.out b/src/test/regress/expected/gms_debugger_server3.out new file mode 100644 index 000000000..245965bf2 --- /dev/null +++ b/src/test/regress/expected/gms_debugger_server3.out @@ -0,0 +1,53 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test3 cascade; +NOTICE: schema "gms_debugger_test3" does not exist, skipping +create schema gms_debugger_test3; +set search_path = gms_debugger_test3; +-- test for implicit variables +CREATE OR REPLACE function test_debug3(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + ELSE + b := 999; + END CASE; + raise info 'pi_return : %',pi_return ; + return b; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +-- start debug - 1st run +select * from test_debug3(1); + test_debug3 +------------- + 101 +(1 row) + +-- start debug - 2nd run - to be aborted +select * from test_debug3(1); +ERROR: receive abort message +CONTEXT: PL/pgSQL function test_debug3(integer) line 13 at assignment +drop schema gms_debugger_test3 cascade; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to function test_debug3(integer) +drop cascades to function gms_next() +drop cascades to function gms_continue() diff --git a/src/test/regress/expected/gms_debugger_server4.out b/src/test/regress/expected/gms_debugger_server4.out new file mode 100644 index 000000000..16213e6d2 --- /dev/null +++ b/src/test/regress/expected/gms_debugger_server4.out @@ -0,0 +1,114 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test4 cascade; +NOTICE: schema "gms_debugger_test4" does not exist, skipping +create schema gms_debugger_test4; +set search_path = gms_debugger_test4; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +select * from test_debug4(1); +INFO: cnt is 3 +CONTEXT: SQL statement "call test_debug(a)" +PL/pgSQL function test_debug4(integer) line 7 at SQL statement + test_debug4 +------------- + 101 +(1 row) + +-- test with client error in exception +select * from test_debug4(1); +ERROR: receive abort message +CONTEXT: PL/pgSQL function test_debug4(integer) line 14 at assignment +drop schema gms_debugger_test4 cascade; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to table test +drop cascades to function test_debug(integer) +drop cascades to function test_debug4(integer) +drop cascades to function gms_step() +drop cascades to function gms_continue() diff --git a/src/test/regress/expected/gms_debugger_server5.out b/src/test/regress/expected/gms_debugger_server5.out new file mode 100644 index 000000000..c3518210d --- /dev/null +++ b/src/test/regress/expected/gms_debugger_server5.out @@ -0,0 +1,122 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test5 cascade; +NOTICE: schema "gms_debugger_test5" does not exist, skipping +create schema gms_debugger_test5; +set search_path = gms_debugger_test5; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +select * from test_debug4(1); +INFO: cnt is 3 +CONTEXT: SQL statement "call test_debug(a)" +PL/pgSQL function test_debug4(integer) line 7 at SQL statement + test_debug4 +------------- + 101 +(1 row) + +-- test with client error in exception +select * from test_debug4(1); +INFO: cnt is 3 +CONTEXT: SQL statement "call test_debug(a)" +PL/pgSQL function test_debug4(integer) line 7 at SQL statement + test_debug4 +------------- + 101 +(1 row) + +drop schema gms_debugger_test5 cascade; +NOTICE: drop cascades to 7 other objects +DETAIL: drop cascades to table test +drop cascades to function test_debug(integer) +drop cascades to function test_debug4(integer) +drop cascades to function gms_continue() +drop cascades to function gms_step() +drop cascades to function gms_next() +drop cascades to function gms_finish() diff --git a/src/test/regress/expected/gms_debugger_server6.out b/src/test/regress/expected/gms_debugger_server6.out new file mode 100644 index 000000000..1a78fa213 --- /dev/null +++ b/src/test/regress/expected/gms_debugger_server6.out @@ -0,0 +1,114 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test6 cascade; +NOTICE: schema "gms_debugger_test6" does not exist, skipping +create schema gms_debugger_test6; +set search_path = gms_debugger_test6; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ +select * from gms_debug.initialize(); + initialize +------------- + datanode1-0 +(1 row) + +select pg_sleep(1); + pg_sleep +---------- + +(1 row) + +select * from test_debug4(1); +INFO: cnt is 3 +CONTEXT: SQL statement "call test_debug(a)" +PL/pgSQL function test_debug4(integer) line 7 at SQL statement + test_debug4 +------------- + 101 +(1 row) + +-- test with client error in exception +select * from test_debug4(1); +ERROR: Debug Comm 0 has been released or not turned on yet. +drop schema gms_debugger_test6 cascade; +NOTICE: drop cascades to 6 other objects +DETAIL: drop cascades to table test +drop cascades to function test_debug(integer) +drop cascades to function test_debug4(integer) +drop cascades to function gms_continue() +drop cascades to function gms_breakpoint(text,integer) +drop cascades to function gms_finish() diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index eebfc20b5..b8f0cc210 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -86,7 +86,7 @@ test: plpgsql_sql_with_proc_keyword test: plsql_show_all_error b_pg_plsql_show_all_error test: pldeveloper_gs_source test: index_advisor -#test: pl_debugger_server pl_debugger_client +#test: gms_debugger_server1 gms_debugger_client1 test: update_for_wait_s1 update_for_wait_s2 test: plan_hint plan_hint_set plan_hint_no_expand plan_hint_iud null_test_opt deserialize_func test: large_sequence int16 gs_dump_sequence diff --git a/src/test/regress/pg_regress.cpp b/src/test/regress/pg_regress.cpp index 825c7ef19..c7724dd17 100644 --- a/src/test/regress/pg_regress.cpp +++ b/src/test/regress/pg_regress.cpp @@ -360,7 +360,7 @@ static const int UPGRADE_GRAY_OBSERVE_STAGE_ROLLBACK = 4; static const int UPGRADE_FINASH = 5; static _stringlist* loadlanguage = NULL; static _stringlist* loadextension = NULL; -static int max_connections = 0; +static int max_connections = 10; static char* encoding = NULL; static _stringlist* schedulelist = NULL; static int upgrade_cn_num = 1; diff --git a/src/test/regress/sql/gms_debugger_client1.sql b/src/test/regress/sql/gms_debugger_client1.sql new file mode 100644 index 000000000..9e872bb7e --- /dev/null +++ b/src/test/regress/sql/gms_debugger_client1.sql @@ -0,0 +1,138 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test1; + +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +CREATE or REPLACE FUNCTION gms_info() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.get_runtime_info(1, run_info); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_breakpoint('test_debug', 0); -- negative + +select gms_breakpoint('test_debug', 15); -- ok + +select gms_breakpoint('test_debug', 17); -- invalid +select gms_breakpoint('test_debug', 22); -- ok +select gms_breakpoint('test_debug', 15); -- ok + +select gms_next(); + +select gms_next(); + +select gms_next(); + +select gms_next(); + +select gms_continue(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/src/test/regress/sql/gms_debugger_client2.sql b/src/test/regress/sql/gms_debugger_client2.sql new file mode 100644 index 000000000..20ca1912f --- /dev/null +++ b/src/test/regress/sql/gms_debugger_client2.sql @@ -0,0 +1,82 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test2; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_next(); + +select gms_next(); + +select gms_next(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/src/test/regress/sql/gms_debugger_client3.sql b/src/test/regress/sql/gms_debugger_client3.sql new file mode 100644 index 000000000..9f067ba96 --- /dev/null +++ b/src/test/regress/sql/gms_debugger_client3.sql @@ -0,0 +1,82 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test3; + +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_next(); + +select gms_next(); + +select gms_continue(); + +select gms_next(); + +select gms_debug.detach_session(); diff --git a/src/test/regress/sql/gms_debugger_client4.sql b/src/test/regress/sql/gms_debugger_client4.sql new file mode 100644 index 000000000..fdbd70829 --- /dev/null +++ b/src/test/regress/sql/gms_debugger_client4.sql @@ -0,0 +1,88 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test4; + +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_continue(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/src/test/regress/sql/gms_debugger_client5.sql b/src/test/regress/sql/gms_debugger_client5.sql new file mode 100644 index 000000000..c3b848c5b --- /dev/null +++ b/src/test/regress/sql/gms_debugger_client5.sql @@ -0,0 +1,116 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test5; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_next() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 2, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_finish() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 8, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_next(); + +select gms_next(); + +select gms_next(); + +select gms_step(); + +select gms_finish(); + +select gms_finish(); + +select gms_debug.detach_session(); diff --git a/src/test/regress/sql/gms_debugger_client6.sql b/src/test/regress/sql/gms_debugger_client6.sql new file mode 100644 index 000000000..c5fd2985b --- /dev/null +++ b/src/test/regress/sql/gms_debugger_client6.sql @@ -0,0 +1,99 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test6; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_finish() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 8, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_breakpoint('test_debug', 31); +select gms_breakpoint('test_debug', 44); + +select gms_finish(); + +select gms_finish(); + +select gms_finish(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/src/test/regress/sql/gms_debugger_client7.sql b/src/test/regress/sql/gms_debugger_client7.sql new file mode 100644 index 000000000..c073663ca --- /dev/null +++ b/src/test/regress/sql/gms_debugger_client7.sql @@ -0,0 +1,103 @@ +-- wait for server establishment + +CREATE OR REPLACE FUNCTION wait_for_gms_debug_extension() +RETURNS BOOLEAN AS $$ +DECLARE + extension_exists BOOLEAN; +BEGIN + -- 初始化变量 + extension_exists := FALSE; + + -- 循环查询扩展是否存在 + WHILE NOT extension_exists LOOP + -- 查询扩展是否存在 + PERFORM 1 FROM pg_extension WHERE extname = 'gms_debug'; + IF FOUND THEN + -- 如果扩展存在,则退出循环 + extension_exists := TRUE; + ELSE + -- 如果扩展不存在,则等待一段时间再重试 + PERFORM pg_sleep(1); -- 等待1秒 + END IF; + END LOOP; + + -- 返回扩展存在的标志 + RETURN extension_exists; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN + IF wait_for_gms_debug_extension() THEN + -- 扩展存在,执行下一步操作 + END IF; +END $$; + +set search_path = gms_debugger_test7; + +CREATE or REPLACE FUNCTION gms_step() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 4, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_continue() +returns void as $$ +declare + run_info gms_debug.runtime_info; + ret binary_integer; +begin + ret := gms_debug.continue(run_info, 0, 2); + RAISE NOTICE 'breakpoint= %', run_info.breakpoint; + RAISE NOTICE 'stackdepth= %', run_info.stackdepth; + RAISE NOTICE 'line= %', run_info.line#; + RAISE NOTICE 'reason= %', run_info.reason; + RAISE NOTICE 'ret= %',ret; +end; +$$ LANGUAGE plpgsql; + +CREATE or REPLACE FUNCTION gms_breakpoint(funcname text, lineno int) +returns void as $$ +declare + pro_info gms_debug.program_info; + bkline binary_integer; + ret binary_integer; +begin + pro_info.name := funcname; + ret := gms_debug.set_breakpoint(pro_info, lineno, bkline,1,1); + RAISE NOTICE 'ret= %', ret; + RAISE NOTICE 'ret= %', bkline; +end; +$$ LANGUAGE plpgsql; + +-- attach debug server +select * from gms_debug.attach_session('datanode1-0'); + +select pg_sleep(3); + +select gms_breakpoint('test_debug_recursive', 5); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_step(); + +select gms_continue(); + +select gms_continue(); + +select gms_debug.detach_session(); diff --git a/src/test/regress/sql/gms_debugger_server1.sql b/src/test/regress/sql/gms_debugger_server1.sql new file mode 100644 index 000000000..8a47326ee --- /dev/null +++ b/src/test/regress/sql/gms_debugger_server1.sql @@ -0,0 +1,95 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test1 cascade; +create schema gms_debugger_test1; +set search_path = gms_debugger_test1; +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); + +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; + +create table show_code_table(lineno int, code text, canBreak bool); + +do $$ +declare + funcoid oid; +begin + select oid from pg_proc into funcoid where proname = 'test_debug'; + INSERT INTO show_code_table SELECT * FROM dbe_pldebugger.info_code(funcoid); +end; +$$; + +select * from show_code_table; + +-- attach fail (target not turned on) +select * from gms_debug.attach_session('datanode1-0'); + +-- turn off without turn on +select * from gms_debug.debug_off(); + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +-- start debug - 1st run +select * from test_debug(4); + +-- start debug - 2nd run - to be aborted +select * from test_debug(4); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test1 cascade; diff --git a/src/test/regress/sql/gms_debugger_server2.sql b/src/test/regress/sql/gms_debugger_server2.sql new file mode 100644 index 000000000..559a9cf57 --- /dev/null +++ b/src/test/regress/sql/gms_debugger_server2.sql @@ -0,0 +1,32 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test2 cascade; +create schema gms_debugger_test2; +set search_path = gms_debugger_test2; + +-- commit/rollback in procedure +create table tb1(a int); +create or replace procedure test_debug2 as +begin + insert into tb1 values (1000); + commit; + insert into tb1 values (2000); + rollback; +end; +/ + + +-- start debug +select * from gms_debug.initialize(); + +select pg_sleep(1); + +-- start debug - 1st run +select * from test_debug2(); + +-- start debug - 2nd run - to be aborted +select * from test_debug2(); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test2 cascade; diff --git a/src/test/regress/sql/gms_debugger_server3.sql b/src/test/regress/sql/gms_debugger_server3.sql new file mode 100644 index 000000000..830599f5a --- /dev/null +++ b/src/test/regress/sql/gms_debugger_server3.sql @@ -0,0 +1,39 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test3 cascade; +create schema gms_debugger_test3; +set search_path = gms_debugger_test3; + +-- test for implicit variables +CREATE OR REPLACE function test_debug3(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + ELSE + b := 999; + END CASE; + raise info 'pi_return : %',pi_return ; + return b; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +-- start debug - 1st run +select * from test_debug3(1); + +-- start debug - 2nd run - to be aborted +select * from test_debug3(1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test3 cascade; diff --git a/src/test/regress/sql/gms_debugger_server4.sql b/src/test/regress/sql/gms_debugger_server4.sql new file mode 100644 index 000000000..607e20d76 --- /dev/null +++ b/src/test/regress/sql/gms_debugger_server4.sql @@ -0,0 +1,97 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test4 cascade; +create schema gms_debugger_test4; +set search_path = gms_debugger_test4; + +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); + +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; + +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +select * from test_debug4(1); + +-- test with client error in exception +select * from test_debug4(1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test4 cascade; diff --git a/src/test/regress/sql/gms_debugger_server5.sql b/src/test/regress/sql/gms_debugger_server5.sql new file mode 100644 index 000000000..1a4ac60b9 --- /dev/null +++ b/src/test/regress/sql/gms_debugger_server5.sql @@ -0,0 +1,97 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test5 cascade; +create schema gms_debugger_test5; +set search_path = gms_debugger_test5; + +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); + +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; + +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +select * from test_debug4(1); + +-- test with client error in exception +select * from test_debug4(1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test5 cascade; diff --git a/src/test/regress/sql/gms_debugger_server6.sql b/src/test/regress/sql/gms_debugger_server6.sql new file mode 100644 index 000000000..d1ba00029 --- /dev/null +++ b/src/test/regress/sql/gms_debugger_server6.sql @@ -0,0 +1,97 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test6 cascade; +create schema gms_debugger_test6; +set search_path = gms_debugger_test6; + +create table test(a int, b varchar(40), c timestamp); +insert into test values (2, 'Giving to the Needy', '2020-02-02'); +insert into test values (3, 'Prayer', '2021-12-02'); +insert into test values (5, 'Fasting', '2030-03-02'); +insert into test values (7, 'Treasures in Heaven', '2040-04-02'); + +CREATE OR REPLACE FUNCTION test_debug(x int) RETURNS SETOF test AS +$BODY$ +DECLARE + sql_stmt VARCHAR2(500); + r test%rowtype; + rec record; + b_tmp text; + cnt int; + a_tmp int; + cur refcursor; + n_tmp NUMERIC(24,6); + t_tmp tsquery; + CURSOR cur_arg(criterion INTEGER) IS + SELECT * FROM test WHERE a < criterion; +BEGIN + cnt := 0; + FOR r IN SELECT * FROM test + WHERE a > x + LOOP + RETURN NEXT r; + END LOOP; + + FOR rec in SELECT * FROM test + WHERE a < x + LOOP + RETURN NEXT rec; + END LOOP; + + FORALL index_1 IN 0..1 + INSERT INTO test VALUES (index_1, 'Happy Children''s Day!', '2021-6-1'); + + SELECT b FROM test where a = 7 INTO b_tmp; + sql_stmt := 'select a from test where b = :1;'; + OPEN cur FOR sql_stmt USING b_tmp; + IF cur%isopen then LOOP + FETCH cur INTO a_tmp; + EXIT WHEN cur%notfound; + END LOOP; + END IF; + CLOSE cur; + WHILE cnt < 3 LOOP + cnt := cnt + 1; + END LOOP; + + RAISE INFO 'cnt is %', cnt; + + RETURN; + +END +$BODY$ +LANGUAGE plpgsql; + +-- test for step into +CREATE OR REPLACE FUNCTION test_debug4(a in integer) return integer +AS +declare +b int; +BEGIN + CASE a + WHEN 1 THEN + b := 111; + call test_debug(a); + ELSE + b := 999; + END CASE; + return b; + raise info 'pi_return : %',pi_return ; + EXCEPTION WHEN others THEN + b := 101; + return b; +END; +/ + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +select * from test_debug4(1); + +-- test with client error in exception +select * from test_debug4(1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test6 cascade; diff --git a/src/test/regress/sql/gms_debugger_server7.sql b/src/test/regress/sql/gms_debugger_server7.sql new file mode 100644 index 000000000..3d1625c6a --- /dev/null +++ b/src/test/regress/sql/gms_debugger_server7.sql @@ -0,0 +1,29 @@ +-- setups +create extension if not exists gms_debug; +drop schema if exists gms_debugger_test7 cascade; +create schema gms_debugger_test7; +set search_path = gms_debugger_test7; + +create or replace function test_debug_recursive (ct int, pr int) +returns table (counter int, product int) +language plpgsql +as $$ +begin + return query select ct, pr; + if ct < 5 then + return query select * from test_debug_recursive(ct+ 1, pr * (ct+ 1)); + end if; +end $$; + +select * from gms_debug.initialize(); + +select pg_sleep(1); + +select * from test_debug_recursive (1, 1); + +-- test with client error in exception +select * from test_debug_recursive (1, 1); + +select * from gms_debug.debug_off(); + +drop schema gms_debugger_test7 cascade;