From 12be4f3c8120a479ee6979d628f85390ecde0ee5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=A2=85=E7=A8=8B?= <517719039@qq.com>
Date: Wed, 6 Nov 2024 20:40:07 +0800
Subject: [PATCH] =?UTF-8?q?GMS=5FXMLGEN=E9=AB=98=E7=BA=A7=E5=8C=85?=
=?UTF-8?q?=E5=85=BC=E5=AE=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
GNUmakefile.in | 1 +
build/script/aarch64_opengauss_list | 3 +
.../opengauss_release_list_ubuntu_single | 3 +
build/script/x86_64_opengauss_list | 3 +
contrib/CMakeLists.txt | 8 +-
contrib/Makefile | 2 +
contrib/gms_xmlgen/CMakeLists.txt | 21 +
contrib/gms_xmlgen/Makefile | 25 +
contrib/gms_xmlgen/data/dummy.txt | 1 +
contrib/gms_xmlgen/expected/gms_xmlgen.out | 3260 +++++++++++++++++
contrib/gms_xmlgen/gms_xmlgen--1.0.sql | 192 +
contrib/gms_xmlgen/gms_xmlgen.control | 5 +
contrib/gms_xmlgen/gms_xmlgen.cpp | 1381 +++++++
contrib/gms_xmlgen/gms_xmlgen.h | 77 +
contrib/gms_xmlgen/sql/gms_xmlgen.sql | 1182 ++++++
src/common/backend/utils/adt/xml.cpp | 2 +-
.../optimizer/commands/dropcmds.cpp | 1 +
src/include/utils/xml.h | 2 +-
18 files changed, 6166 insertions(+), 3 deletions(-)
create mode 100644 contrib/gms_xmlgen/CMakeLists.txt
create mode 100644 contrib/gms_xmlgen/Makefile
create mode 100644 contrib/gms_xmlgen/data/dummy.txt
create mode 100644 contrib/gms_xmlgen/expected/gms_xmlgen.out
create mode 100644 contrib/gms_xmlgen/gms_xmlgen--1.0.sql
create mode 100644 contrib/gms_xmlgen/gms_xmlgen.control
create mode 100644 contrib/gms_xmlgen/gms_xmlgen.cpp
create mode 100644 contrib/gms_xmlgen/gms_xmlgen.h
create mode 100644 contrib/gms_xmlgen/sql/gms_xmlgen.sql
diff --git a/GNUmakefile.in b/GNUmakefile.in
index 7b904a345..76f1c063d 100644
--- a/GNUmakefile.in
+++ b/GNUmakefile.in
@@ -107,6 +107,7 @@ install:
@if test -d contrib/gms_stats; then $(MAKE) -C contrib/gms_stats $@; fi
@if test -d contrib/gms_tcp; then $(MAKE) -C contrib/gms_tcp $@; fi
@if test -d contrib/gms_profiler; then $(MAKE) -C contrib/gms_profiler $@; fi
+ @if test -d contrib/gms_xmlgen; then $(MAKE) -C contrib/gms_xmlgen $@; fi
@if test -d contrib/gms_output; then $(MAKE) -C contrib/gms_output $@; fi
@if test -d contrib/timescaledb; then (./contrib/timescaledb/run_to_build.sh && $(MAKE) -C contrib/timescaledb/build $@); fi
@if test -d contrib/chparser; then \
diff --git a/build/script/aarch64_opengauss_list b/build/script/aarch64_opengauss_list
index 0bbf5b388..ce787a7c0 100644
--- a/build/script/aarch64_opengauss_list
+++ b/build/script/aarch64_opengauss_list
@@ -133,6 +133,8 @@
./share/postgresql/extension/gms_profiler.control
./share/postgresql/extension/gms_sql--1.0.sql
./share/postgresql/extension/gms_sql.control
+./share/postgresql/extension/gms_xmlgen--1.0.sql
+./share/postgresql/extension/gms_xmlgen.control
./share/postgresql/timezone/GB-Eire
./share/postgresql/timezone/Turkey
./share/postgresql/timezone/Kwajalein
@@ -838,6 +840,7 @@
./lib/postgresql/gms_tcp.so
./lib/postgresql/gms_profiler.so
./lib/postgresql/gms_sql.so
+./lib/postgresql/gms_xmlgen.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 7f37aa985..9e772726a 100644
--- a/build/script/opengauss_release_list_ubuntu_single
+++ b/build/script/opengauss_release_list_ubuntu_single
@@ -121,6 +121,8 @@
./share/postgresql/extension/gms_sql.control
./share/postgresql/extension/gms_tcp--1.0.sql
./share/postgresql/extension/gms_tcp.control
+./share/postgresql/extension/gms_xmlgen--1.0.sql
+./share/postgresql/extension/gms_xmlgen.control
./share/postgresql/timezone/GB-Eire
./share/postgresql/timezone/Turkey
./share/postgresql/timezone/Kwajalein
@@ -808,6 +810,7 @@
./lib/postgresql/gms_profiler.so
./lib/postgresql/gms_sql.so
./lib/postgresql/gms_tcp.so
+./lib/postgresql/gms_xmlgen.so
./lib/libpljava.so
./lib/libpq.a
./lib/libpq.so
diff --git a/build/script/x86_64_opengauss_list b/build/script/x86_64_opengauss_list
index 2807efe99..e3e71c76d 100644
--- a/build/script/x86_64_opengauss_list
+++ b/build/script/x86_64_opengauss_list
@@ -133,6 +133,8 @@
./share/postgresql/extension/gms_profiler.control
./share/postgresql/extension/gms_sql--1.0.sql
./share/postgresql/extension/gms_sql.control
+./share/postgresql/extension/gms_xmlgen--1.0.sql
+./share/postgresql/extension/gms_xmlgen.control
./share/postgresql/timezone/GB-Eire
./share/postgresql/timezone/Turkey
./share/postgresql/timezone/Kwajalein
@@ -838,6 +840,7 @@
./lib/postgresql/gms_output.so
./lib/postgresql/gms_profiler.so
./lib/postgresql/gms_sql.so
+./lib/postgresql/gms_xmlgen.so
./lib/libpljava.so
./lib/libpq.a
./lib/libpq.so
diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt
index 5ae029022..bbf7a935b 100644
--- a/contrib/CMakeLists.txt
+++ b/contrib/CMakeLists.txt
@@ -34,6 +34,13 @@ set(CMAKE_MODULE_PATH
${CMAKE_CURRENT_SOURCE_DIR}/gms_tcp
)
+if("${USE_LIBXML}" STREQUAL "ON")
+ INCLUDE_DIRECTORIES(${LIBXML_INCLUDE_PATH}/libxml2)
+ INCLUDE_DIRECTORIES(${LIBXML_LIB_PATH})
+ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/gms_xmlgen)
+ add_subdirectory(gms_xmlgen)
+endif()
+
add_subdirectory(hstore)
add_subdirectory(test_decoding)
add_subdirectory(mppdb_decoding)
@@ -67,5 +74,4 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/chparser)
add_subdirectory(chparser)
endif()
add_subdirectory(gms_profiler)
-
add_subdirectory(gms_lob)
diff --git a/contrib/Makefile b/contrib/Makefile
index 50d5d9c3b..a33ce11ba 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -70,8 +70,10 @@ endif
ifeq ($(with_libxml),yes)
SUBDIRS += xml2
+SUBDIRS += gms_xmlgen
else
ALWAYS_SUBDIRS += xml2
+ALWAYS_SUBDIRS += gms_xmlgen
endif
ifeq ($(with_selinux),yes)
diff --git a/contrib/gms_xmlgen/CMakeLists.txt b/contrib/gms_xmlgen/CMakeLists.txt
new file mode 100644
index 000000000..bd651ee9e
--- /dev/null
+++ b/contrib/gms_xmlgen/CMakeLists.txt
@@ -0,0 +1,21 @@
+#This is the main CMAKE for build all gms_xmlgen.
+# gms_xmlgen
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} TGT_gms_xmlgen_SRC)
+set(TGT_gms_xmlgen_INC
+ ${PROJECT_OPENGS_DIR}/contrib/gms_xmlgen
+ ${PROJECT_OPENGS_DIR}/contrib
+)
+
+set(gms_xmlgen_DEF_OPTIONS ${MACRO_OPTIONS})
+set(gms_xmlgen_COMPILE_OPTIONS ${OPTIMIZE_OPTIONS} ${OS_OPTIONS} ${PROTECT_OPTIONS} ${WARNING_OPTIONS} ${LIB_SECURE_OPTIONS} ${CHECK_OPTIONS})
+set(gms_xmlgen_LINK_OPTIONS ${LIB_LINK_OPTIONS})
+add_shared_libtarget(gms_xmlgen TGT_gms_xmlgen_SRC TGT_gms_xmlgen_INC "${gms_xmlgen_DEF_OPTIONS}" "${gms_xmlgen_COMPILE_OPTIONS}" "${gms_xmlgen_LINK_OPTIONS}")
+set_target_properties(gms_xmlgen PROPERTIES PREFIX "")
+
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gms_xmlgen.control
+ DESTINATION share/postgresql/extension/
+)
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gms_xmlgen--1.0.sql
+ DESTINATION share/postgresql/extension/
+)
+install(TARGETS gms_xmlgen DESTINATION lib/postgresql)
diff --git a/contrib/gms_xmlgen/Makefile b/contrib/gms_xmlgen/Makefile
new file mode 100644
index 000000000..dcc2fd842
--- /dev/null
+++ b/contrib/gms_xmlgen/Makefile
@@ -0,0 +1,25 @@
+# contrib/gms_xmlgen/Makefile
+MODULE_big = gms_xmlgen
+OBJS = gms_xmlgen.o
+
+EXTENSION = gms_xmlgen
+DATA = gms_xmlgen--1.0.sql
+
+exclude_option = -fPIE
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+override CPPFLAGS := -fstack-protector-strong $(filter-out $(exclude_option),$(CPPFLAGS)) -I$(LIBXML_INCLUDE_PATH)/libxml2
+REGRESS = gms_xmlgen
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/gms_xmlgen
+regress_home = $(top_builddir)/src/test/regress
+REGRESS_OPTS = -c 0 -d 1 -r 1 -p 25633 --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
diff --git a/contrib/gms_xmlgen/data/dummy.txt b/contrib/gms_xmlgen/data/dummy.txt
new file mode 100644
index 000000000..7f015cede
--- /dev/null
+++ b/contrib/gms_xmlgen/data/dummy.txt
@@ -0,0 +1 @@
+The openGauss regression needs this file to run.
\ No newline at end of file
diff --git a/contrib/gms_xmlgen/expected/gms_xmlgen.out b/contrib/gms_xmlgen/expected/gms_xmlgen.out
new file mode 100644
index 000000000..2a8115722
--- /dev/null
+++ b/contrib/gms_xmlgen/expected/gms_xmlgen.out
@@ -0,0 +1,3260 @@
+create extension gms_xmlgen;
+create extension gms_output;
+select gms_output.enable(100000);
+ enable
+--------
+
+(1 row)
+
+create schema gms_xmlgen_test;
+set search_path = gms_xmlgen_test;
+set behavior_compat_options = 'bind_procedure_searchpath';
+-- prepare data
+create table t_types (
+ "integer" integer,
+ "float" float,
+ "numeric" numeric(20, 6),
+ "boolean" boolean,
+ "char" char(20),
+ "varchar" varchar(20),
+ "text" text,
+ "blob" blob,
+ "raw" raw,
+ "date" date,
+ "time" time,
+ "timestamp" timestamp,
+ "json" json,
+ "varchar_array" varchar(20)[]
+);
+insert into t_types
+values(
+ 1,
+ 1.23456,
+ 1.234567,
+ true,
+ '"''<>&char test',
+ 'varchar"''<>&test',
+ 'text test"''<>&',
+ 'ff',
+ hextoraw('ABCD'),
+ '2024-01-02',
+ '18:01:02',
+ '2024-02-03 19:03:04',
+ '{"a" : 1, "b" : 2}',
+ array['abc', '"''<>&', '你好']
+ ),
+ (
+ 2,
+ 2.23456,
+ 2.234567,
+ false,
+ '2"''<>&char test',
+ '2varchar"''<>&test',
+ '2text test"''<>&',
+ 'eeee',
+ hextoraw('ffff'),
+ '2026-03-04',
+ '20:12:13',
+ '2026-05-06 21:13:00',
+ '[9,8,7,6]',
+ array['&^%@', '"''<>&', '<&y''">']
+ ),
+ (
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ );
+-- GMS_XMLGEN.NEWCONTEXT && GMS_XMLGEN.CLOSECONTEXT
+select gms_xmlgen.newcontext('select * from t_types');
+ newcontext
+------------
+ 1
+(1 row)
+
+select gms_xmlgen.getxml(1);
+ getxml
+--------------------------------------------------------------
+ +
+ +
+ +
+ 1 +
+ 1.23456 +
+ 1.234567 +
+ true +
+ "'<>&char test +
+ varchar"'<>&test +
+ text test"'<>& +
+ FF +
+ ABCD +
+ 2024-01-02T00:00:00 +
+ +
+ 2024-02-03T19:03:04 +
+ {"a" : 1, "b" : 2} +
+ +
+ abc +
+ "'<>& +
+ 你好 +
+ +
+
+
+ +
+ 2 +
+ 2.23456 +
+ 2.234567 +
+ false +
+ 2"'<>&char test +
+ 2varchar"'<>&test+
+ 2text test"'<>& +
+ EEEE +
+ FFFF +
+ 2026-03-04T00:00:00 +
+ +
+ 2026-05-06T21:13:00 +
+ [9,8,7,6] +
+ +
+ &^%@ +
+ "'<>& +
+ <&y'"> +
+ +
+
+
+ +
+
+
+ +
+
+(1 row)
+
+select gms_xmlgen.closecontext(1);
+ closecontext
+--------------
+
+(1 row)
+
+-- procedure case
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- newcontext by cursor
+DECLARE
+CURSOR xc is select * from t_types;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('xc'::refcursor);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- newcontext by cursor expression
+DECLARE
+CURSOR xc is select "integer", CURSOR(select * from t_types) from t_types;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+gms_xmlgen.closecontext(xml_cxt);
+close xc;
+END;
+/
+-- invalid null parameter
+select gms_xmlgen.newcontext(NULL);
+ERROR: invalid query string
+CONTEXT: referenced column: newcontext
+-- ok for invalid query sql
+select gms_xmlgen.newcontext('aabbccdd');
+ newcontext
+------------
+ 5
+(1 row)
+
+-- ok for closecontext NULL
+select gms_xmlgen.closecontext(NULL);
+ closecontext
+--------------
+
+(1 row)
+
+-- ok for closecontext not exist id
+select gms_xmlgen.closecontext(99);
+ closecontext
+--------------
+
+(1 row)
+
+-- error for closecontext invalid range
+select gms_xmlgen.closecontext(-1);
+ERROR: value is out of range.
+CONTEXT: SQL statement "CALL gms_xmlgen.close_context(ctx)"
+PL/pgSQL function gms_xmlgen.closecontext(gms_xmlgen.ctxhandle) line 2 at PERFORM
+referenced column: closecontext
+select gms_xmlgen.closecontext(4294967296);
+ERROR: value is out of range.
+CONTEXT: SQL statement "CALL gms_xmlgen.close_context(ctx)"
+PL/pgSQL function gms_xmlgen.closecontext(gms_xmlgen.ctxhandle) line 2 at PERFORM
+referenced column: closecontext
+-- GMS_XMLGEN.GETXML
+-- getxml by query
+DECLARE
+xml_output clob;
+BEGIN
+xml_output := gms_xmlgen.getxml('select * from t_types');
+gms_output.put_line(xml_output);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- getxml by cursor
+DECLARE
+CURSOR xc is select * from t_types;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+close xc;
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- getxml by cursor expression
+DECLARE
+CURSOR xc is select "integer", CURSOR(select * from t_types) from t_types;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+close xc;
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ <_x003F_column_x003F_><unnamed portal 6>
+
+
+ 2
+ <_x003F_column_x003F_><unnamed portal 7>
+
+
+ <_x003F_column_x003F_><unnamed portal 8>
+
+
+
+-- getxml by context id
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- getxml by context id with out parameter
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.getxml(xml_cxt, xml_output);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- no result when getxml twice without restartquery
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.getxml(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- invalid null parameter
+select gms_xmlgen.getxml(NULL);
+ERROR: invalid query string
+CONTEXT: referenced column: getxml
+-- invalid query sql
+select gms_xmlgen.getxml('aabbccdd');
+ERROR: syntax error at or near "aabbccdd"
+LINE 1: select gms_xmlgen.getxml('aabbccdd');
+ ^
+CONTEXT: referenced column: getxml
+-- invalid xmlgen context id
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+END;
+/
+ERROR: invalid gms_xmlgen context found
+CONTEXT: PL/pgSQL function inline_code_block line 6 at assignment
+-- invalid xmlgen context id with out parameter
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.getxml(xml_cxt, xml_output);
+gms_output.put_line(xml_output);
+END;
+/
+ERROR: invalid gms_xmlgen context found
+CONTEXT: PL/pgSQL function gms_xmlgen.getxml(gms_xmlgen.ctxhandle,clob,numeric) line 2 at assignment
+SQL statement "CALL gms_xmlgen.getxml(xml_cxt,xml_output)"
+PL/pgSQL function inline_code_block line 6 at SQL statement
+-- GMS_XMLGEN.GETXMLTYPE
+-- getxmltype by query
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_type := gms_xmlgen.getxmltype('select * from t_types');
+gms_output.put_line(xml_type::text);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- getxmltype by query with parameter 2
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_type := gms_xmlgen.getxmltype('select * from t_types', 1);
+gms_output.put_line(xml_type::text);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- getxmltype by context id
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_type := gms_xmlgen.getxmltype(xml_cxt);
+gms_output.put_line(xml_type::text);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- getxmltype by context id with parameter 2
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_type := gms_xmlgen.getxmltype(xml_cxt, 1);
+gms_output.put_line(xml_type::text);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- getxmltype by cursor
+DECLARE
+CURSOR xc is select * from t_types;
+xml_cxt gms_xmlgen.ctxhandle;
+xml_output xmltype;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+xml_output := gms_xmlgen.getxmltype(xml_cxt);
+gms_output.put_line(xml_output::text);
+close xc;
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- getxmltype by cursor expression
+DECLARE
+CURSOR xc is select "integer", CURSOR(select * from t_types) from t_types;
+xml_cxt gms_xmlgen.ctxhandle;
+xml_output xmltype;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+xml_output := gms_xmlgen.getxmltype(xml_cxt);
+gms_output.put_line(xml_output::text);
+close xc;
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ <_x003F_column_x003F_><unnamed portal 19>
+
+
+ 2
+ <_x003F_column_x003F_><unnamed portal 20>
+
+
+ <_x003F_column_x003F_><unnamed portal 21>
+
+
+
+-- invalid null parameter
+select gms_xmlgen.getxmltype(NULL);
+ERROR: invalid query string
+CONTEXT: referenced column: getxmltype
+-- invalid query sql
+select gms_xmlgen.getxmltype('aabbccdd');
+ERROR: syntax error at or near "aabbccdd"
+LINE 1: select gms_xmlgen.getxmltype('aabbccdd');
+ ^
+CONTEXT: referenced column: getxmltype
+-- invalid context id
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+xml_type := gms_xmlgen.getxmltype(xml_cxt);
+END;
+/
+ERROR: invalid gms_xmlgen context found
+CONTEXT: PL/pgSQL function inline_code_block line 6 at assignment
+-- invalid parameter 2 range
+select gms_xmlgen.getxmltype('select * from t_types', -1);
+ERROR: value is out of range.
+CONTEXT: referenced column: getxmltype
+select gms_xmlgen.getxmltype('select * from t_types', 4294967296);
+ERROR: value is out of range.
+CONTEXT: referenced column: getxmltype
+-- GMS_XMLGEN.NEWCONTEXTFROMHIERARCHY
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- with set row set tag
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+gms_xmlgen.setrowsettag(xml_cxt_from_hierarchy, 'TopTag');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+-- error with set row tag
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+gms_xmlgen.setrowtag(xml_cxt_from_hierarchy, 'TopTag');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+ERROR: this operation is invalid in the hierarchy context
+CONTEXT: SQL statement "CALL gms_xmlgen.set_row_tag(ctx,rowTagName)"
+PL/pgSQL function gms_xmlgen.setrowtag(gms_xmlgen.ctxhandle,character varying) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setrowtag(xml_cxt_from_hierarchy,'TopTag')"
+PL/pgSQL function inline_code_block line 9 at PERFORM
+-- error with setmaxrows
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+gms_xmlgen.setmaxrows(xml_cxt_from_hierarchy, 1);
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+ERROR: this operation is invalid in the hierarchy context
+CONTEXT: SQL statement "CALL gms_xmlgen.set_max_rows(ctx,maxrows)"
+PL/pgSQL function gms_xmlgen.setmaxrows(gms_xmlgen.ctxhandle,numeric) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setmaxrows(xml_cxt_from_hierarchy,1)"
+PL/pgSQL function inline_code_block line 9 at PERFORM
+-- error with setskiprows
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+gms_xmlgen.setskiprows(xml_cxt_from_hierarchy, 1);
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+ERROR: this operation is invalid in the hierarchy context
+CONTEXT: SQL statement "CALL gms_xmlgen.set_skip_rows(ctx,skipRows)"
+PL/pgSQL function gms_xmlgen.setskiprows(gms_xmlgen.ctxhandle,numeric) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setskiprows(xml_cxt_from_hierarchy,1)"
+PL/pgSQL function inline_code_block line 9 at PERFORM
+-- invalid null parameter
+select gms_xmlgen.newcontextfromhierarchy(NULL);
+ERROR: invalid query string
+CONTEXT: referenced column: newcontextfromhierarchy
+-- ok for invalid query sql
+select gms_xmlgen.newcontextfromhierarchy('aabbccdd');
+ newcontextfromhierarchy
+-------------------------
+ 23
+(1 row)
+
+-- get xml error with invalid query sql
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('aabbccdd');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+ERROR: syntax error at or near "aabbccdd"
+LINE 1: aabbccdd
+ ^
+QUERY: aabbccdd
+CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
+-- get xml error with not hierarchy query sql
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+ERROR: invalid query result
+CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
+-- GMS_XMLGEN.RESTARTQUERY
+-- get xml twice
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.restartquery(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- ok for restartquery closed context id
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.restartquery(xml_cxt);
+END;
+/
+-- GMS_XMLGEN.SETCONVERTSPECIALCHARS
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setconvertspecialchars(xml_cxt, false);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.setconvertspecialchars(xml_cxt, true);
+gms_xmlgen.restartquery(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- error for missing parameter 2
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setconvertspecialchars(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: function gms_xmlgen.setconvertspecialchars has no enough parameters
+CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 4
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setconvertspecialchars(xml_cxt, true);
+END;
+/
+-- GMS_XMLGEN.SETMAXROWS
+-- set max rows 0
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, 0);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- set max rows 1
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, 1);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: function gms_xmlgen.setmaxrows has no enough parameters
+CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 4
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, -1);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: value is out of range.
+CONTEXT: SQL statement "CALL gms_xmlgen.set_max_rows(ctx,maxrows)"
+PL/pgSQL function gms_xmlgen.setmaxrows(gms_xmlgen.ctxhandle,numeric) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setmaxrows(xml_cxt,-1)"
+PL/pgSQL function inline_code_block line 5 at PERFORM
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, 4294967296);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: value is out of range.
+CONTEXT: SQL statement "CALL gms_xmlgen.set_max_rows(ctx,maxrows)"
+PL/pgSQL function gms_xmlgen.setmaxrows(gms_xmlgen.ctxhandle,numeric) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setmaxrows(xml_cxt,4294967296)"
+PL/pgSQL function inline_code_block line 5 at PERFORM
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setmaxrows(xml_cxt, 1);
+END;
+/
+-- GMS_XMLGEN.SETNULLHANDLING
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt, 0);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.setnullhandling(xml_cxt, 1);
+gms_xmlgen.restartquery(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.setnullhandling(xml_cxt, 2);
+gms_xmlgen.restartquery(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-- other numbers > 2, the same as 0
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt, 3);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- ok for NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+gms_xmlgen.setnullhandling(NULL, 1);
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: function gms_xmlgen.setnullhandling has no enough parameters
+CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 4
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt, -1);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: value is out of range.
+CONTEXT: SQL statement "CALL gms_xmlgen.set_null_handling(ctx,flag)"
+PL/pgSQL function gms_xmlgen.setnullhandling(gms_xmlgen.ctxhandle,numeric) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setnullhandling(xml_cxt,-1)"
+PL/pgSQL function inline_code_block line 5 at PERFORM
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt, 4294967296);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: value is out of range.
+CONTEXT: SQL statement "CALL gms_xmlgen.set_null_handling(ctx,flag)"
+PL/pgSQL function gms_xmlgen.setnullhandling(gms_xmlgen.ctxhandle,numeric) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setnullhandling(xml_cxt,4294967296)"
+PL/pgSQL function inline_code_block line 5 at PERFORM
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setnullhandling(xml_cxt, 1);
+END;
+/
+-- GMS_XMLGEN.SETROWSETTAG
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt, 'test');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- error for setrowsettag NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt, NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: the xml has multiple root nodes
+CONTEXT: PL/pgSQL function inline_code_block line 6 at assignment
+-- ok for setrowsettag NULL with one row
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types where rownum = 1');
+gms_xmlgen.setrowsettag(xml_cxt, NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+-- ok for setrowsettag context id null
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+gms_xmlgen.setrowsettag(NULL, 'test');
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: function gms_xmlgen.setrowsettag has no enough parameters
+CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 4
+-- parameter type error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt, true);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: function gms_xmlgen.setrowsettag(gms_xmlgen.ctxhandle, boolean) does not exist
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+CONTEXT: SQL statement "CALL gms_xmlgen.setrowsettag(xml_cxt,true)"
+PL/pgSQL function inline_code_block line 5 at PERFORM
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setrowsettag(xml_cxt, 'test');
+END;
+/
+-- GMS_XMLGEN.SETROWTAG
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowtag(xml_cxt, 'test');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- ok for setrowtag NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowtag(xml_cxt, NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+-- error for setrowsettag NULL && setrowtag NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt, NULL);
+gms_xmlgen.setrowtag(xml_cxt, NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: the xml has multiple root nodes
+CONTEXT: PL/pgSQL function inline_code_block line 7 at assignment
+-- ok for setrowtag context id null
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+gms_xmlgen.setrowtag(NULL, 'test');
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowtag(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: function gms_xmlgen.setrowtag has no enough parameters
+CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 4
+-- parameter type error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowtag(xml_cxt, true);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: function gms_xmlgen.setrowtag(gms_xmlgen.ctxhandle, boolean) does not exist
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+CONTEXT: SQL statement "CALL gms_xmlgen.setrowtag(xml_cxt,true)"
+PL/pgSQL function inline_code_block line 5 at PERFORM
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setrowtag(xml_cxt, 'test');
+END;
+/
+-- GMS_XMLGEN.SETSKIPROWS
+-- set skip row 0
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 0);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- set skip row 1
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 1);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- set skip row 2
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 2);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+
+
+
+-- ok for setskiprows context id null
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(NULL, 1);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: function gms_xmlgen.setskiprows has no enough parameters
+CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 4
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, -1);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: value is out of range.
+CONTEXT: SQL statement "CALL gms_xmlgen.set_skip_rows(ctx,skipRows)"
+PL/pgSQL function gms_xmlgen.setskiprows(gms_xmlgen.ctxhandle,numeric) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setskiprows(xml_cxt,-1)"
+PL/pgSQL function inline_code_block line 5 at PERFORM
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 4294967296);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+ERROR: value is out of range.
+CONTEXT: SQL statement "CALL gms_xmlgen.set_skip_rows(ctx,skipRows)"
+PL/pgSQL function gms_xmlgen.setskiprows(gms_xmlgen.ctxhandle,numeric) line 2 at PERFORM
+SQL statement "CALL gms_xmlgen.setskiprows(xml_cxt,4294967296)"
+PL/pgSQL function inline_code_block line 5 at PERFORM
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setskiprows(xml_cxt, 1);
+END;
+/
+-- GMS_XMLGEN.USEITEMTAGSFORCOLL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.useitemtagsforcoll(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- ok for useitemtagsforcoll context id NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.useitemtagsforcoll(NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.useitemtagsforcoll(xml_cxt);
+END;
+/
+-- GMS_XMLGEN.USENULLATTRIBUTEINDICATOR
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.usenullattributeindicator(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-- ok for parameter 2 true
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.usenullattributeindicator(xml_cxt, true);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-- ok for parameter 2 false, the result is the same as the true's
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.usenullattributeindicator(xml_cxt, false);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-- ok for usenullattributeindicator context id NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.usenullattributeindicator(NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.usenullattributeindicator(xml_cxt);
+END;
+/
+-- GMS_XMLGEN.GETNUMROWSPROCESSED
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+0
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+3
+-- getnumrowsprocessed with skip rows
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 1);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+0
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+
+
+2
+-- getnumrowsprocessed with max rows
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, 2);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+0
+
+
+
+ 1
+ 1.23456
+ 1.234567
+ true
+ "'<>&char test
+ varchar"'<>&test
+ text test"'<>&
+ FF
+ ABCD
+ 2024-01-02T00:00:00
+
+ 2024-02-03T19:03:04
+ {"a" : 1, "b" : 2}
+
+ abc
+ "'<>&
+ 你好
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+2
+-- getnumrowsprocessed with skip rows && max rows
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 1);
+gms_xmlgen.setmaxrows(xml_cxt, 1);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+0
+
+
+
+ 2
+ 2.23456
+ 2.234567
+ false
+ 2"'<>&char test
+ 2varchar"'<>&test
+ 2text test"'<>&
+ EEEE
+ FFFF
+ 2026-03-04T00:00:00
+
+ 2026-05-06T21:13:00
+ [9,8,7,6]
+
+ &^%@
+ "'<>&
+ <&y'">
+
+
+
+
+1
+-- ok for getnumrowsprocessed context id NULL
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+processed_row := gms_xmlgen.getnumrowsprocessed(NULL);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+processed_row := gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+END;
+/
+-- GMS_XMLGEN.CONVERT
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, NULL);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 0);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 1);
+ convert
+---------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 2);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, -1);
+ convert
+---------
+
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 4294967295);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 4294967296);
+ convert
+---------
+
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2);
+ convert
+-----------------------------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2, NULL);
+ convert
+-----------------------------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2, 0);
+ convert
+-----------------------------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2, 1);
+ convert
+---------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2, 2);
+ convert
+-----------------------------------------------
+ "'<>&
+(1 row)
+
+-- would not convert
+select GMS_XMLGEN.CONVERT('"&ApOS;<&gT;&Amp;'::varchar2, 1);
+ convert
+---------------------------
+ "&ApOS;<&gT;&Amp;
+(1 row)
+
+select pg_typeof(GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 1));
+ pg_typeof
+-------------------
+ character varying
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::clob);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, NULL);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 0);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 1);
+ convert
+---------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 2);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, -1);
+ convert
+---------
+
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 4294967295);
+ convert
+---------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 4294967296);
+ convert
+---------
+
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::clob);
+ convert
+-----------------------------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::clob, NULL);
+ convert
+-----------------------------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::clob, 0);
+ convert
+-----------------------------------------------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::clob, 1);
+ convert
+---------
+ "'<>&
+(1 row)
+
+select GMS_XMLGEN.CONVERT('"'<>&'::clob, 2);
+ convert
+-----------------------------------------------
+ "'<>&
+(1 row)
+
+-- would not convert
+select GMS_XMLGEN.CONVERT('"&ApOS;<&gT;&Amp;'::clob, 1);
+ convert
+---------------------------
+ "&ApOS;<&gT;&Amp;
+(1 row)
+
+select pg_typeof(GMS_XMLGEN.CONVERT('"''<>&'::clob, 1));
+ pg_typeof
+-----------
+ clob
+(1 row)
+
+-- error for NULL
+select GMS_XMLGEN.CONVERT(NULL, 0);
+ERROR: invalid parameter
+CONTEXT: referenced column: convert
+select GMS_XMLGEN.CONVERT(NULL);
+ERROR: invalid parameter
+CONTEXT: referenced column: convert
+-- compatibility tool usecases
+DECLARE ctx GMS_xmlgen.ctxHandle;
+BEGIN ctx := GMS_xmlgen.newContext('select * FROM t_types');
+GMS_xmlgen.closeContext(ctx);
+GMS_output.put_line(ctx::text);
+END;
+/
+73
+DECLARE res XMLType;
+BEGIN res := GMS_XMLGEN.GETXMLTYPE('123');
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE res GMS_XMLGEN.ctxHandle;
+BEGIN res := GMS_XMLGEN.NEWCONTEXTFROMHIERARCHY('123');
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE d varchar2(100);
+a varchar2(100);
+BEGIN d := GMS_XMLGEN.CONVERT(a);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE d number;
+a GMS_XMLGEN.ctxHandle;
+BEGIN d := GMS_XMLGEN.GETNUMROWSPROCESSED(a);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE a GMS_XMLGEN.ctxHandle;
+b clob;
+BEGIN b := GMS_XMLGEN.GETXML(a);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN GMS_XMLGEN.RESTARTQUERY(CTX);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.SETCONVERTSPECIALCHARS(CTX, false);
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN GMS_XMLGEN.SETMAXROWS(CTX, 2);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.SETNULLHANDLING(CTX, 1);
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.SETROWSETTAG(CTX, 'srst');
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.SETROWTAG(CTX, 'srst');
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN GMS_XMLGEN.SETSKIPROWS(CTX, 5);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN GMS_XMLGEN.USEITEMTAGSFORCOLL(CTX);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.USENULLATTRIBUTEINDICATOR(CTX, false);
+END;
+/
+reset search_path;
+drop schema gms_xmlgen_test cascade;
+NOTICE: drop cascades to table gms_xmlgen_test.t_types
diff --git a/contrib/gms_xmlgen/gms_xmlgen--1.0.sql b/contrib/gms_xmlgen/gms_xmlgen--1.0.sql
new file mode 100644
index 000000000..18e94dfd7
--- /dev/null
+++ b/contrib/gms_xmlgen/gms_xmlgen--1.0.sql
@@ -0,0 +1,192 @@
+/* contrib/gms_xmlgen/gms_xmlgen--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION gms_xmlgen" to load this file. \quit
+
+CREATE SCHEMA gms_xmlgen;
+GRANT USAGE ON SCHEMA gms_xmlgen TO PUBLIC;
+
+-- create fake type gms_xmlgen.ctxhandle
+create domain gms_xmlgen.ctxhandle as number;
+
+-- gms_xmlgen.closecontext
+CREATE OR REPLACE FUNCTION gms_xmlgen.close_context(ctx IN gms_xmlgen.ctxhandle)
+RETURNS void
+AS 'MODULE_PATHNAME', 'close_context'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.closecontext(ctx IN gms_xmlgen.ctxhandle)
+AS
+BEGIN
+ gms_xmlgen.close_context(ctx);
+END;
+
+-- gms_xmlgen.convert
+CREATE OR REPLACE FUNCTION gms_xmlgen.convert(xmlData IN VARCHAR2, flag IN NUMBER := 0)
+RETURNS VARCHAR2 PACKAGE
+AS 'MODULE_PATHNAME', 'convert_xml'
+LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE FUNCTION gms_xmlgen.convert(xmlData IN CLOB, flag IN NUMBER := 0)
+RETURNS CLOB PACKAGE
+AS 'MODULE_PATHNAME', 'convert_clob'
+LANGUAGE C IMMUTABLE;
+
+-- gms_xmlgen.getnumrowsprocessed
+CREATE OR REPLACE FUNCTION gms_xmlgen.getnumrowsprocessed(ctx IN gms_xmlgen.ctxhandle)
+RETURNS NUMBER
+AS 'MODULE_PATHNAME', 'get_num_rows_processed'
+LANGUAGE C STRICT VOLATILE;
+
+-- gms_xmlgen.getxml
+CREATE OR REPLACE FUNCTION gms_xmlgen.getxml(sqlQuery IN VARCHAR2, dtdOrSchema IN NUMBER := 0)
+RETURNS CLOB PACKAGE
+AS 'MODULE_PATHNAME', 'get_xml_by_query'
+LANGUAGE C VOLATILE;
+
+CREATE OR REPLACE FUNCTION gms_xmlgen.getxml(ctx IN gms_xmlgen.ctxhandle, dtdOrSchema IN NUMBER := 0)
+RETURNS CLOB PACKAGE
+AS 'MODULE_PATHNAME', 'get_xml_by_ctx_id'
+LANGUAGE C VOLATILE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.getxml(ctx IN gms_xmlgen.ctxhandle, tmpclob INOUT CLOB, dtdOrSchema IN NUMBER := 0)
+PACKAGE
+AS
+BEGIN
+ tmpclob := gms_xmlgen.getxml(ctx, dtdOrSchema);
+END;
+
+-- gms_xmlgen.getxmltype
+CREATE OR REPLACE FUNCTION gms_xmlgen.getxmltype(ctx IN gms_xmlgen.ctxhandle, dtdOrSchema IN NUMBER := 0)
+ RETURNS xmltype PACKAGE
+AS $$
+select xmltype(gms_xmlgen.getxml(ctx, dtdOrSchema))::xmltype;
+$$
+LANGUAGE SQL VOLATILE;
+
+CREATE OR REPLACE FUNCTION gms_xmlgen.getxmltype(sqlQuery IN VARCHAR2, dtdOrSchema IN NUMBER := 0)
+ RETURNS xmltype PACKAGE
+AS $$
+select xmltype(gms_xmlgen.getxml(sqlQuery, dtdOrSchema))::xmltype;
+$$
+LANGUAGE SQL VOLATILE;
+
+-- gms_xmlgen.newcontext
+CREATE OR REPLACE FUNCTION gms_xmlgen.newcontext(queryString IN VARCHAR2)
+RETURNS gms_xmlgen.ctxhandle PACKAGE
+AS 'MODULE_PATHNAME', 'new_context_by_query'
+LANGUAGE C VOLATILE;
+
+CREATE OR REPLACE FUNCTION gms_xmlgen.newcontext(queryString IN SYS_REFCURSOR)
+RETURNS gms_xmlgen.ctxhandle PACKAGE
+AS 'MODULE_PATHNAME', 'new_context_by_cursor'
+LANGUAGE C VOLATILE;
+
+-- gms_xmlgen.newcontextfromhierarchy
+CREATE OR REPLACE FUNCTION gms_xmlgen.newcontextfromhierarchy(queryString IN VARCHAR2)
+RETURNS gms_xmlgen.ctxhandle
+AS 'MODULE_PATHNAME', 'new_context_from_hierarchy'
+LANGUAGE C VOLATILE;
+
+-- gms_xmlgen.restartquery
+CREATE OR REPLACE FUNCTION gms_xmlgen.restart_query(ctx IN gms_xmlgen.ctxhandle)
+RETURNS gms_xmlgen.ctxhandle
+AS 'MODULE_PATHNAME', 'restart_query'
+LANGUAGE C STRICT VOLATILE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.restartquery(ctx IN gms_xmlgen.ctxhandle)
+AS
+BEGIN
+ gms_xmlgen.restart_query(ctx);
+END;
+
+-- gms_xmlgen.setconvertspecialchars
+CREATE OR REPLACE FUNCTION gms_xmlgen.set_convert_special_chars(ctx IN gms_xmlgen.ctxhandle, is_convert IN boolean)
+RETURNS void
+AS 'MODULE_PATHNAME', 'set_convert_special_chars'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.setconvertspecialchars(ctx IN gms_xmlgen.ctxhandle, is_convert IN boolean)
+AS
+BEGIN
+ gms_xmlgen.set_convert_special_chars(ctx, is_convert);
+END;
+
+-- gms_xmlgen.setmaxrows
+CREATE OR REPLACE FUNCTION gms_xmlgen.set_max_rows(ctx IN gms_xmlgen.ctxhandle, maxrows IN NUMBER)
+RETURNS void
+AS 'MODULE_PATHNAME', 'set_max_rows'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.setmaxrows(ctx IN gms_xmlgen.ctxhandle, maxrows IN NUMBER)
+AS
+BEGIN
+ gms_xmlgen.set_max_rows(ctx, maxrows);
+END;
+
+-- gms_xmlgen.setnullhandling
+CREATE OR REPLACE FUNCTION gms_xmlgen.set_null_handling(ctx IN gms_xmlgen.ctxhandle, flag IN NUMBER)
+RETURNS void
+AS 'MODULE_PATHNAME', 'set_null_handling'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.setnullhandling(ctx IN gms_xmlgen.ctxhandle, flag IN NUMBER)
+AS
+BEGIN
+ gms_xmlgen.set_null_handling(ctx, flag);
+END;
+
+-- gms_xmlgen.setrowsettag
+CREATE OR REPLACE FUNCTION gms_xmlgen.set_row_set_tag(ctx IN gms_xmlgen.ctxhandle, rowSetTagName IN VARCHAR2)
+RETURNS void
+AS 'MODULE_PATHNAME', 'set_row_set_tag'
+LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.setrowsettag(ctx IN gms_xmlgen.ctxhandle, rowSetTagName IN VARCHAR2)
+AS
+BEGIN
+ gms_xmlgen.set_row_set_tag(ctx, rowSetTagName);
+END;
+
+-- gms_xmlgen.setrowtag
+CREATE OR REPLACE FUNCTION gms_xmlgen.set_row_tag(ctx IN gms_xmlgen.ctxhandle, rowTagName IN VARCHAR2)
+RETURNS void
+AS 'MODULE_PATHNAME', 'set_row_tag'
+LANGUAGE C IMMUTABLE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.setrowtag(ctx IN gms_xmlgen.ctxhandle, rowTagName IN VARCHAR2)
+AS
+BEGIN
+ gms_xmlgen.set_row_tag(ctx, rowTagName);
+END;
+
+-- gms_xmlgen.setskiprows
+CREATE OR REPLACE FUNCTION gms_xmlgen.set_skip_rows(ctx IN gms_xmlgen.ctxhandle, skipRows IN NUMBER)
+RETURNS void
+AS 'MODULE_PATHNAME', 'set_skip_rows'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.setskiprows(ctx IN gms_xmlgen.ctxhandle, skipRows IN NUMBER)
+AS
+BEGIN
+ gms_xmlgen.set_skip_rows(ctx, skipRows);
+END;
+
+-- gms_xmlgen.useitemtagsforcoll
+CREATE OR REPLACE FUNCTION gms_xmlgen.use_item_tags_for_coll(ctx IN gms_xmlgen.ctxhandle)
+RETURNS void
+AS 'MODULE_PATHNAME', 'use_item_tags_for_coll'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE OR REPLACE PROCEDURE gms_xmlgen.useitemtagsforcoll(ctx IN gms_xmlgen.ctxhandle)
+AS
+BEGIN
+ gms_xmlgen.use_item_tags_for_coll(ctx);
+END;
+
+-- gms_xmlgen.usenullattributeindicator
+CREATE OR REPLACE PROCEDURE gms_xmlgen.usenullattributeindicator(ctx IN gms_xmlgen.ctxhandle, attrind IN BOOLEAN := TRUE)
+AS
+BEGIN
+ gms_xmlgen.set_null_handling(ctx, 1);
+END;
diff --git a/contrib/gms_xmlgen/gms_xmlgen.control b/contrib/gms_xmlgen/gms_xmlgen.control
new file mode 100644
index 000000000..dc38eee26
--- /dev/null
+++ b/contrib/gms_xmlgen/gms_xmlgen.control
@@ -0,0 +1,5 @@
+# gms_xmlgen extension
+comment = 'Converting the results of a SQL query to a canonical XML format'
+default_version = '1.0'
+module_pathname = '$libdir/gms_xmlgen'
+relocatable = true
diff --git a/contrib/gms_xmlgen/gms_xmlgen.cpp b/contrib/gms_xmlgen/gms_xmlgen.cpp
new file mode 100644
index 000000000..4125180e4
--- /dev/null
+++ b/contrib/gms_xmlgen/gms_xmlgen.cpp
@@ -0,0 +1,1381 @@
+#include "postgres.h"
+#include "knl/knl_variable.h"
+#include "fmgr.h"
+#include "funcapi.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/int16.h"
+#include "utils/int8.h"
+#include "utils/memutils.h"
+#include "utils/numeric.h"
+#include "utils/numeric_gs.h"
+#include "utils/portal.h"
+#include "utils/xml.h"
+#include "executor/spi.h"
+#include "tcop/tcopprot.h"
+#include "commands/extension.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "gms_xmlgen.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(ctxhandle_in);
+PG_FUNCTION_INFO_V1(ctxhandle_out);
+PG_FUNCTION_INFO_V1(close_context);
+PG_FUNCTION_INFO_V1(convert_xml);
+PG_FUNCTION_INFO_V1(convert_clob);
+PG_FUNCTION_INFO_V1(get_num_rows_processed);
+PG_FUNCTION_INFO_V1(get_xml_by_ctx_id);
+PG_FUNCTION_INFO_V1(get_xml_by_query);
+PG_FUNCTION_INFO_V1(new_context_by_query);
+PG_FUNCTION_INFO_V1(new_context_by_cursor);
+PG_FUNCTION_INFO_V1(new_context_from_hierarchy);
+PG_FUNCTION_INFO_V1(restart_query);
+PG_FUNCTION_INFO_V1(set_convert_special_chars);
+PG_FUNCTION_INFO_V1(set_max_rows);
+PG_FUNCTION_INFO_V1(set_null_handling);
+PG_FUNCTION_INFO_V1(set_row_set_tag);
+PG_FUNCTION_INFO_V1(set_row_tag);
+PG_FUNCTION_INFO_V1(set_skip_rows);
+PG_FUNCTION_INFO_V1(use_item_tags_for_coll);
+
+static uint32 g_xmlgen_extension_id;
+
+#define TOP_CONTEXT ((XMLGenTopContext *)(u_sess->attr.attr_common.extension_session_vars_array[g_xmlgen_extension_id]))
+
+void set_extension_index(uint32 index)
+{
+ g_xmlgen_extension_id = index;
+}
+
+void init_session_vars(void)
+{
+ RepallocSessionVarsArrayIfNecessary();
+
+ XMLGenTopContext *g_xmlgen_mem_ctx =
+ (XMLGenTopContext *)MemoryContextAllocZero(u_sess->self_mem_cxt, sizeof(XMLGenTopContext));
+
+ g_xmlgen_mem_ctx->memctx = u_sess->self_mem_cxt;
+ g_xmlgen_mem_ctx->xmlgen_context_list = NIL;
+ g_xmlgen_mem_ctx->xmlgen_context_id = 0;
+
+ u_sess->attr.attr_common.extension_session_vars_array[g_xmlgen_extension_id] = g_xmlgen_mem_ctx;
+}
+
+/**
+ * get XMLGenContext by ctx_id
+ * @param {int64} ctx_id
+ * @return {XMLGenContext} ctx
+ */
+static XMLGenContext *get_xmlgen_context(int64 ctx_id)
+{
+ if (TOP_CONTEXT->xmlgen_context_list == NIL) {
+ return NULL;
+ }
+
+ ListCell *lc = NULL;
+ foreach (lc, TOP_CONTEXT->xmlgen_context_list) {
+ XMLGenContext *ctx = (XMLGenContext *)lfirst(lc);
+ if (ctx->ctx_id == ctx_id) {
+ return ctx;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * free XMLGenContext
+ * @param {XMLGenContext*} ctx - XMLGenContext pointer
+ * @return {void}
+ */
+static void free_xmlgen_context(XMLGenContext *ctx)
+{
+ if (ctx->memctx != NULL) {
+ MemoryContextDelete(ctx->memctx);
+ }
+
+ MemoryContext oldcontext = MemoryContextSwitchTo(TOP_CONTEXT->memctx);
+
+ TOP_CONTEXT->xmlgen_context_list = list_delete(TOP_CONTEXT->xmlgen_context_list, (void *)ctx);
+
+ MemoryContextSwitchTo(oldcontext);
+ MemoryContextReset(ctx->memctx);
+ pfree_ext(ctx);
+}
+
+/**
+ * unescape XML data
+ * @param {const char *} str - escaped string data
+ * @return {char *} result - unescaped string data
+ */
+static char *convert_decode(const char *str)
+{
+ StringInfoData buf;
+ const char *p = NULL;
+ int charlen = 0;
+
+ initStringInfo(&buf);
+ p = str;
+ while (*p) {
+ if (*p == '&') {
+ if (strncmp(p, "<", 4) == 0) {
+ appendStringInfoString(&buf, "<");
+ charlen = 4;
+ } else if (strncmp(p, ">", 4) == 0) {
+ appendStringInfoString(&buf, ">");
+ charlen = 4;
+ } else if (strncmp(p, """, 6) == 0) {
+ appendStringInfoString(&buf, "\"");
+ charlen = 6;
+ } else if (strncmp(p, "'", 6) == 0) {
+ appendStringInfoString(&buf, "'");
+ charlen = 6;
+ } else if (strncmp(p, "&", 5) == 0) {
+ appendStringInfoString(&buf, "&");
+ charlen = 5;
+ } else {
+ appendStringInfoString(&buf, "&");
+ charlen = 1;
+ }
+ } else {
+ charlen = pg_mblen(p);
+ for (int i = 0; i < charlen; i++) {
+ appendStringInfoCharMacro(&buf, p[i]);
+ }
+ }
+ p += charlen;
+ }
+ return buf.data;
+}
+
+/**
+ * escape XML data
+ * @param {const char *} str - unescaped string data
+ * @return {char *} result - escaped string data
+ */
+static char *convert_encode(const char *str)
+{
+ StringInfoData buf;
+ const char *p = NULL;
+ int charlen = 0;
+
+ initStringInfo(&buf);
+ p = str;
+ while (*p) {
+ charlen = pg_mblen(p);
+ switch (*p) {
+ case '&':
+ appendStringInfoString(&buf, "&");
+ break;
+ case '<':
+ appendStringInfoString(&buf, "<");
+ break;
+ case '>':
+ appendStringInfoString(&buf, ">");
+ break;
+ case '"':
+ appendStringInfoString(&buf, """);
+ break;
+ case '\'':
+ appendStringInfoString(&buf, "'");
+ break;
+ default: {
+ for (int i = 0; i < charlen; i++) {
+ appendStringInfoCharMacro(&buf, p[i]);
+ }
+ break;
+ }
+ }
+ p += charlen;
+ }
+ return buf.data;
+}
+
+/**
+ * map date value to xml value
+ * @param {Datum} value - bytea datum value
+ * @return {char *} result - xml value
+ */
+static char *map_date_value_to_xml(Datum value)
+{
+ DateADT date;
+ struct pg_tm tm;
+ char buf[MAXDATELEN + 1];
+
+ date = DatumGetDateADT(value);
+ if (DATE_NOT_FINITE(date)) {
+ ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("date out of range"),
+ errdetail("XML does not support infinite date values.")));
+ }
+ j2date(date + POSTGRES_EPOCH_JDATE, &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
+ EncodeDateOnly(&tm, USE_XSD_DATES, buf);
+
+ return pstrdup(buf);
+}
+
+/**
+ * map timestamp value to xml value
+ * @param {Datum} value - bytea datum value
+ * @return {char *} result - xml value
+ */
+static char *map_timestamp_value_to_xml(Datum value, bool is_tz)
+{
+ TimestampTz timestamp;
+ struct pg_tm tm;
+ int tz;
+ fsec_t fsec;
+ const char *tzn = NULL;
+ char buf[MAXDATELEN + 1];
+
+ timestamp = DatumGetTimestamp(value);
+
+ if (TIMESTAMP_NOT_FINITE(timestamp)) {
+ ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"),
+ errdetail("XML does not support infinite timestamp values.")));
+ } else if (timestamp2tm(timestamp, is_tz ? &tz : NULL, &tm, &fsec, is_tz ? &tzn : NULL, NULL) == 0) {
+ EncodeDateTime(&tm, fsec, is_tz, is_tz ? tz : 0, is_tz ? tzn : NULL, USE_XSD_DATES, buf);
+ } else {
+ ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range")));
+ }
+
+ return pstrdup(buf);
+}
+
+/**
+ * map bytea value to xml value
+ * @param {Datum} value - bytea datum value
+ * @return {char *} result - xml value
+ */
+static char *map_bytea_value_to_xml(Datum value)
+{
+ bytea *bstr = DatumGetByteaPP(value);
+ volatile xmlBufferPtr buf = NULL;
+ volatile xmlTextWriterPtr writer = NULL;
+ char *result = NULL;
+
+ PG_TRY();
+ {
+ buf = xmlBufferCreate();
+ if (buf == NULL) {
+ ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("could not allocate xmlBuffer")));
+ }
+ writer = xmlNewTextWriterMemory(buf, 0);
+ if (writer == NULL) {
+ xmlBufferFree(buf);
+ ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("could not allocate xmlTextWriter")));
+ }
+ if (u_sess->attr.attr_common.xmlbinary == XMLBINARY_BASE64) {
+ xmlTextWriterWriteBase64(writer, VARDATA_ANY(bstr), 0, VARSIZE_ANY_EXHDR(bstr));
+ } else {
+ xmlTextWriterWriteBinHex(writer, VARDATA_ANY(bstr), 0, VARSIZE_ANY_EXHDR(bstr));
+ }
+ /* we MUST do this now to flush data out to the buffer */
+ xmlFreeTextWriter(writer);
+ writer = NULL;
+
+ result = pstrdup((const char *)xmlBufferContent(buf));
+ }
+ PG_CATCH();
+ {
+ if (writer) {
+ xmlFreeTextWriter(writer);
+ }
+ if (buf) {
+ xmlBufferFree(buf);
+ }
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ xmlBufferFree(buf);
+
+ return result;
+}
+
+/**
+ * map array value to xml value
+ * @param {Datum} value - array datum value
+ * @param {bool} xml_escape_strings - is convert special chars
+ * @param {char *} array_tag_suffix - array tag suffix
+ * @param {int} indent_level - indentation level
+ * @return {char *} result - xml value
+ */
+char *map_array_value_to_xml(Datum value, bool xml_escape_strings, char *array_tag_suffix, int indent_level)
+{
+ ArrayType *array = NULL;
+ Oid elmtype;
+ int16 elmlen;
+ bool elmbyval = false;
+ char elmalign;
+ int num_elems;
+ Datum *elem_values = NULL;
+ bool *elem_nulls = NULL;
+ StringInfoData buf;
+ int i;
+
+ array = DatumGetArrayTypeP(value);
+ elmtype = ARR_ELEMTYPE(array);
+ get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
+ const char *type_name = get_typename(elmtype);
+ const char *item_suffix = array_tag_suffix == NULL ? "" : array_tag_suffix;
+
+ deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, &elem_values, &elem_nulls, &num_elems);
+
+ initStringInfo(&buf);
+
+ for (i = 0; i < num_elems; i++) {
+ if (elem_nulls[i])
+ continue;
+ appendStringInfoString(&buf, "\n");
+ appendStringInfoSpaces(&buf, (indent_level + 1) * SPACE_PER_INDENTATION);
+ appendStringInfo(&buf, "<%s%s>", type_name, item_suffix);
+ appendStringInfoString(&buf, map_sql_value_to_xml(elem_values[i], elmtype, xml_escape_strings, array_tag_suffix,
+ indent_level + 1));
+ appendStringInfo(&buf, "%s%s>", type_name, item_suffix);
+ }
+ appendStringInfoString(&buf, "\n");
+ appendStringInfoSpaces(&buf, indent_level * SPACE_PER_INDENTATION);
+ pfree_ext(elem_values);
+ pfree_ext(elem_nulls);
+
+ return buf.data;
+}
+
+/**
+ * map sql column value to xml value
+ * @param {Datum} value - column value
+ * @param {Oid} type - column type
+ * @param {bool} xml_escape_strings - is convert special chars
+ * @param {char *} array_tag_suffix - array tag suffix
+ * @param {int} indent_level - indentation level
+ * @return {char *} result - xml value
+ */
+static char *map_sql_value_to_xml(Datum value, Oid type, bool xml_escape_strings, char *array_tag_suffix,
+ int indent_level)
+{
+ if (type_is_array_domain(type)) {
+ return map_array_value_to_xml(value, xml_escape_strings, array_tag_suffix, indent_level);
+ } else {
+ switch (type) {
+ case BOOLOID:
+ return (char *)(DatumGetBool(value) ? "true" : "false");
+ case DATEOID:
+ return map_date_value_to_xml(value);
+ case TIMESTAMPOID:
+ return map_timestamp_value_to_xml(value, false);
+ case TIMESTAMPTZOID:
+ return map_timestamp_value_to_xml(value, true);
+ case BYTEAOID:
+ return map_bytea_value_to_xml(value);
+ default: {
+ Oid typeOut;
+ bool isvarlena = false;
+ char *str = NULL;
+
+ getTypeOutputInfo(type, &typeOut, &isvarlena);
+ str = OidOutputFunctionCall(typeOut, value);
+
+ if (type == XMLOID || !xml_escape_strings) {
+ return str;
+ }
+
+ return convert_encode(str);
+ }
+ }
+ }
+}
+
+/**
+ * convert datum to int
+ * @param {Datum} datum
+ * @return {Oid} type - datum type
+ */
+static int convert_datum_to_int(Datum datum, Oid type)
+{
+ Oid type_out;
+ bool is_varlena = false;
+ getTypeOutputInfo(type, &type_out, &is_varlena);
+ char *str = OidOutputFunctionCall(type_out, datum);
+ int result = 0;
+ return sscanf_s(str, "%d", &result) == 1 ? result : -1;
+}
+
+/**
+ * add node to hierarchy xml
+ * @param {xmlNodePtr} pre_node
+ * @param {xmlNodePtr} cur_node
+ * @param {int} pre_level
+ * @param {int} cur_level
+ * @return {xmlNodePtr} return cur_node, or NULL otherwise
+ */
+static xmlNodePtr add_node_to_hierarchy_xml(xmlNodePtr pre_node, xmlNodePtr cur_node, int pre_level, int cur_level)
+{
+ if (pre_node == NULL) {
+ return NULL;
+ }
+ xmlNodePtr pre_node_inner_pointer = pre_node;
+ int pre_level_inner = pre_level;
+ if (cur_level > pre_level_inner + 1) {
+ return NULL;
+ }
+ int64 i = MAX_DEPTH;
+ while (i > 0) {
+ if (cur_level == pre_level_inner + 1) {
+ xmlAddChild(pre_node_inner_pointer, cur_node);
+ return cur_node;
+ }
+ if (pre_node_inner_pointer->parent == NULL) {
+ return NULL;
+ }
+ pre_node_inner_pointer = pre_node_inner_pointer->parent;
+ pre_level_inner -= 1;
+ i--;
+ }
+ return NULL;
+}
+
+/**
+ * convert hierarchy sql row to xml element
+ * @param {int64} count_rows
+ * @return {xmlDocPtr} result
+ */
+static xmlDocPtr SPI_hierarchy_sql_row_to_xml_element(int64 count_rows)
+{
+ xmlDocPtr result = xmlNewDoc(BAD_CAST "1.0");
+ bool is_first_node = true;
+ bool is_null = false;
+ int pre_level = -1;
+ xmlNodePtr pre_node = NULL;
+ int cur_level = -1;
+ xmlNodePtr cur_node = NULL;
+ Datum level_datum = 0;
+
+ for (int64 i = 0; i < count_rows; i++) {
+ level_datum = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, &is_null);
+ if (is_null) {
+ if (result != NULL) {
+ xmlFreeDoc(result);
+ }
+ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid hierarchy query result")));
+ }
+ cur_level = convert_datum_to_int(level_datum, SPI_gettypeid(SPI_tuptable->tupdesc, 1));
+ if (cur_level < 0) {
+ if (result != NULL) {
+ xmlFreeDoc(result);
+ }
+ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid hierarchy query level")));
+ }
+
+ Datum xml_node = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 2, &is_null);
+ if (is_null) {
+ char *xml_name = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, 2), true, false);
+ cur_node = xmlNewNode(0, BAD_CAST xml_name);
+ } else {
+ char *xml_value = map_sql_value_to_xml(xml_node, SPI_gettypeid(SPI_tuptable->tupdesc, 2), false);
+ xmlDocPtr doc_from_xml_value = xmlParseMemory(xml_value, (int)(strlen(xml_value)));
+ cur_node = xmlCopyNode(xmlDocGetRootElement(doc_from_xml_value), 1);
+ xmlFreeDoc(doc_from_xml_value);
+ }
+ if (is_first_node) {
+ xmlNodePtr old_node = xmlDocSetRootElement(result, cur_node);
+ xmlFreeNode(old_node);
+ is_first_node = false;
+ } else {
+ xmlNodePtr cur_node_tmp = add_node_to_hierarchy_xml(pre_node, cur_node, pre_level, cur_level);
+ if (cur_node_tmp == NULL) {
+ xmlFreeNode(cur_node);
+ xmlFreeDoc(result);
+ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid hierarchy query node")));
+ }
+ cur_node = cur_node_tmp;
+ }
+ pre_node = cur_node;
+ pre_level = cur_level;
+ }
+ return result;
+}
+
+/**
+ * convert hierarchy sql row to string
+ * @param {int64} count_rows
+ * @param {StringInfo} result
+ * @param {char *} row_set_tag - row-set tag name
+ * @return {void}
+ */
+static void SPI_hierarchy_sql_row_to_string(int64 count_rows, StringInfo result, char *row_set_tag)
+{
+ xmlDocPtr xml_doc = SPI_hierarchy_sql_row_to_xml_element(count_rows);
+ if (row_set_tag != NULL) {
+ xmlNodePtr cur_node = xmlNewNode(0, BAD_CAST row_set_tag);
+ xmlNodePtr old_node = xmlDocSetRootElement(xml_doc, cur_node);
+ xmlAddChild(cur_node, old_node);
+ }
+ xmlChar *out_buf = NULL;
+ int out_len = 0;
+ xmlDocDumpFormatMemoryEnc(xml_doc, &out_buf, &out_len, "utf-8", 1);
+ appendStringInfo(result, "%s", out_buf);
+ xmlFree(out_buf);
+ xmlFreeDoc(xml_doc);
+}
+
+/**
+ * handle query to xml hierarchy
+ * @param {XMLGenContext *} ctx
+ * @return {StringInfo} result
+ */
+static StringInfo query_to_xml_hierarchy(XMLGenContext *ctx)
+{
+ StringInfo result = makeStringInfo();
+ const char *query = (const char *)ctx->query_string;
+ if (SPI_connect() != SPI_OK_CONNECT) {
+ SPI_finish();
+ ereport(ERROR, (errcode(ERRCODE_SPI_CONNECTION_FAILURE), errmsg("SPI_connect failed")));
+ }
+
+ if (SPI_execute(query, true, 0) != SPI_OK_SELECT) {
+ SPI_finish();
+ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("fail to execute query")));
+ }
+
+ if (SPI_tuptable->tupdesc->natts != 2 ||
+ (SPI_gettypeid(SPI_tuptable->tupdesc, 1) != NUMERICOID && SPI_gettypeid(SPI_tuptable->tupdesc, 1) != INT4OID) ||
+ (SPI_gettypeid(SPI_tuptable->tupdesc, 2) != XMLOID &&
+ strcasecmp(SPI_gettype(SPI_tuptable->tupdesc, 2), XMLTYPE_STR) != 0)) {
+ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid query result")));
+ }
+
+ int64 count_rows = (int64)SPI_processed;
+ SPI_hierarchy_sql_row_to_string(count_rows, result,
+ ctx->is_hierarchy_set_row_set_tag ? ctx->row_set_tag_name : NULL);
+ ctx->processed_rows = count_rows;
+ SPI_finish();
+ return result;
+}
+
+/**
+ * generate xml doc root element start
+ * @param {StringInfo} result
+ * @param {const char*} eltname - root element name
+ * @param {int64} null_flag
+ * @return {void}
+ */
+static void xml_root_element_start(StringInfo result, const char *eltname, int64 null_flag)
+{
+ if (eltname == NULL) {
+ return;
+ }
+ appendStringInfo(result, XML_HEADER);
+ appendStringInfo(result, "<%s", eltname);
+ if (null_flag == NULL_FLAG_NULL_ATTR) {
+ appendStringInfo(result, " %s", XML_XSI_ATTR);
+ }
+
+ appendStringInfo(result, ">\n");
+}
+
+/**
+ * generate xml doc root element end
+ * @param {StringInfo} result
+ * @param {const char*} eltname - root element name
+ * @return {void}
+ */
+static void xml_root_element_end(StringInfo result, const char *eltname)
+{
+ if (eltname == NULL) {
+ return;
+ }
+ appendStringInfo(result, "%s>\n", eltname);
+}
+
+/**
+ * convert sql row to xml elemeny by SPI
+ * @param {int64} rownum - convert data from this rownum
+ * @param {StringInfo} result
+ * @param {int64} null_flag - how to display null element
+ * @param {char *} row_tag - row tag name
+ * @param {bool} is_convert_special_chars - is convert special chars
+ * @param {char *} item_tag - tag suffix for array item
+ * @param {int64} indentation_level - indentations before data
+ * @return {void}
+ */
+static void SPI_sql_row_to_xml_element(int64 rownum, StringInfo result, int64 null_flag, char *row_tag,
+ bool is_convert_special_chars, char *item_tag, int64 indentation_level)
+{
+ if (row_tag != NULL) {
+ if (result->len == 0) {
+ appendStringInfo(result, XML_HEADER);
+ appendStringInfo(result, "<%s", row_tag);
+ if (null_flag == NULL_FLAG_NULL_ATTR) {
+ appendStringInfo(result, " %s", XML_XSI_ATTR);
+ }
+ appendStringInfo(result, ">\n");
+ } else {
+ appendStringInfoSpaces(result, indentation_level * SPACE_PER_INDENTATION);
+ appendStringInfo(result, "<%s>\n", row_tag);
+ }
+ }
+
+ for (int i = 1; i <= SPI_tuptable->tupdesc->natts; i++) {
+ char *colname = map_sql_identifier_to_xml_name(SPI_fname(SPI_tuptable->tupdesc, i), true, false);
+ bool isnull = false;
+ Datum colval = SPI_getbinval(SPI_tuptable->vals[rownum], SPI_tuptable->tupdesc, i, &isnull);
+
+ if (isnull) {
+ if (colname == NULL) {
+ continue;
+ }
+ if (null_flag == NULL_FLAG_NULL_ATTR) {
+ appendStringInfoSpaces(result, indentation_level * SPACE_PER_INDENTATION);
+ appendStringInfo(result, "%s%s<%s%s xsi:nil=\"true\"/>\n", result->len == 0 ? XML_HEADER : "",
+ row_tag == NULL ? "" : " ", result->len == 0 ? XML_XSI_ATTR : "", colname);
+ } else if (null_flag == NULL_FLAG_EMPTY_TAG) {
+ appendStringInfoSpaces(result, indentation_level * SPACE_PER_INDENTATION);
+ appendStringInfo(result, "%s%s<%s/>\n", result->len == 0 ? XML_HEADER : "", row_tag == NULL ? "" : " ",
+ colname);
+ }
+ } else if (colname != NULL) {
+ appendStringInfoSpaces(result, indentation_level * SPACE_PER_INDENTATION);
+ appendStringInfo(result, "%s%s<%s%s>%s%s>\n", result->len == 0 ? XML_HEADER : "",
+ row_tag == NULL ? "" : " ", colname,
+ result->len == 0 && null_flag == NULL_FLAG_NULL_ATTR ? XML_XSI_ATTR : "",
+ map_sql_value_to_xml(colval, SPI_gettypeid(SPI_tuptable->tupdesc, i),
+ is_convert_special_chars, item_tag, indentation_level + 1),
+ colname);
+ }
+ }
+
+ if (row_tag != NULL) {
+ appendStringInfoSpaces(result, indentation_level * SPACE_PER_INDENTATION);
+ appendStringInfo(result, "%s>\n", row_tag);
+ }
+}
+
+/**
+ * check the xml is valid, fail if has no root element or has multiple ones
+ * @param {const XMLGenContext *} ctx - xmlgen contxt pointer
+ * @param {int64} rows - rows in xml
+ * @param {int64} columns - columns in a row
+ * @return {void}
+ */
+static void check_xml_valid(const XMLGenContext *ctx, int64 rows, int64 columns)
+{
+ if (ctx->row_set_tag_name == NULL && (rows != 1 || (ctx->row_tag_name == NULL && columns != 1))) {
+ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("the xml has multiple root nodes")));
+ }
+}
+
+/**
+ * convert query to xml
+ * @param {const XMLGenContext *} ctx - xmlgen contxt pointer
+ * @return {StringInfo} result
+ */
+static StringInfo query_to_xml_flat(XMLGenContext *ctx)
+{
+ StringInfo result = NULL;
+ int64 skip_rows = ctx->skip_rows;
+ errno_t et = EOK;
+ char *row_tag = NULL;
+ char *row_set_tag = NULL;
+ Portal portal = NULL;
+
+ if (ctx->row_tag_name != NULL) {
+ row_tag = static_cast(palloc(sizeof(char) * (strlen(ctx->row_tag_name) + 1)));
+ et = strcpy_s(row_tag, strlen(ctx->row_tag_name) + 1, ctx->row_tag_name);
+ securec_check(et, "\0", "\0");
+ }
+
+ if (ctx->row_set_tag_name != NULL) {
+ row_set_tag = static_cast(palloc(sizeof(char) * (strlen(ctx->row_set_tag_name) + 1)));
+ et = strcpy_s(row_set_tag, strlen(ctx->row_set_tag_name) + 1, ctx->row_set_tag_name);
+ securec_check(et, "\0", "\0");
+ }
+
+ int64 null_flag = ctx->null_flag;
+ int64 processed_rows = 0;
+ int64 count_rows;
+ ctx->processed_rows = 0;
+ result = makeStringInfo();
+
+ if (SPI_connect() != SPI_OK_CONNECT) {
+ pfree_ext(row_tag);
+ pfree_ext(row_set_tag);
+ SPI_finish();
+ ereport(ERROR, (errcode(ERRCODE_SPI_CONNECTION_FAILURE), errmsg("SPI_connect failed")));
+ }
+
+ if (ctx->is_cursor || ctx->skip_rows != 0 || ctx->max_rows != 0) {
+ if (ctx->is_cursor) {
+ portal = SPI_cursor_find((const char *)ctx->query_string);
+ } else {
+ SPIPlanPtr plan = NULL;
+ if ((plan = SPI_prepare((const char *)ctx->query_string, 0, NULL)) == NULL) {
+ SPI_finish();
+ ereport(ERROR, (errcode(ERRCODE_SPI_PREPARE_FAILURE), errmsg("SPI_prepare failed")));
+ }
+
+ if ((portal = SPI_cursor_open(NULL, plan, NULL, NULL, false)) == NULL) {
+ SPI_finish();
+ ereport(ERROR, (errcode(ERRCODE_SPI_CONNECTION_FAILURE), errmsg("SPI_cursor_open failed")));
+ }
+ }
+ if (portal == NULL) {
+ pfree_ext(row_tag);
+ pfree_ext(row_set_tag);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_CURSOR), errmsg("cursor \"%s\" does not exist", ctx->query_string)));
+ }
+
+ if (ctx->max_rows < 0) {
+ SPI_cursor_fetch(portal, true, LONG_MAX);
+ } else {
+ SPI_cursor_fetch(portal, true, ctx->skip_rows + ctx->max_rows);
+ }
+ } else {
+ if (SPI_execute(ctx->query_string, true, 0) != SPI_OK_SELECT) {
+ pfree_ext(row_tag);
+ pfree_ext(row_set_tag);
+ SPI_finish();
+ ereport(ERROR, (errcode(ERRCODE_SPI_CONNECTION_FAILURE), errmsg("fail to execute query")));
+ }
+ }
+
+ count_rows = (int64)SPI_processed;
+ check_xml_valid(ctx, count_rows, SPI_tuptable->tupdesc->natts);
+ int64 start_now = skip_rows + ctx->current_row;
+ if (ctx->max_rows != 0 && count_rows > start_now) {
+ xml_root_element_start(result, row_set_tag == NULL ? row_tag : row_set_tag, null_flag);
+ for (int64 i = skip_rows; i < count_rows; i++) {
+ SPI_sql_row_to_xml_element(i, result, null_flag, (row_set_tag == NULL || row_tag == NULL) ? NULL : row_tag,
+ ctx->is_convert_special_chars, ctx->item_tag_name,
+ (row_set_tag == NULL && row_tag == NULL) ? 0 : 1);
+ processed_rows++;
+ }
+ ctx->processed_rows = processed_rows;
+ ctx->current_row += processed_rows + skip_rows;
+ xml_root_element_end(result, row_set_tag == NULL ? row_tag : row_set_tag);
+ }
+
+ if (!ctx->is_cursor && portal != NULL) {
+ SPI_cursor_close(portal);
+ }
+ SPI_finish();
+ pfree_ext(row_tag);
+ pfree_ext(row_set_tag);
+
+ return result;
+}
+
+/**
+ * top functin to handle converting query to xml
+ * @param {const XMLGenContext *} ctx - xmlgen contxt pointer
+ * @return {StringInfo} result
+ */
+static StringInfo query_to_xml_internal(XMLGenContext *ctx)
+{
+ if (ctx->is_from_hierarchy) {
+ return query_to_xml_hierarchy(ctx);
+ } else {
+ return query_to_xml_flat(ctx);
+ }
+}
+
+/**
+ * top functin to handle converting query to xml
+ * @param {const XMLGenContext *} ctx - xmlgen contxt pointer
+ * @return {StringInfo} result
+ */
+static void check_query_string_valid(char *query_str)
+{
+ if (query_str == NULL) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid query string")));
+ }
+ List *queryTree = pg_parse_query(query_str);
+ if (queryTree == NULL || queryTree->length != 1) {
+ ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("can not parse sql: %s", query_str)));
+ }
+}
+
+/**
+ * create a new XMLGenContext with specific id
+ * @param {int64} ctx_id
+ * @param {XMLGenContext *} result
+ */
+static XMLGenContext *create_xmlgen_context(int64 ctx_id)
+{
+
+ if (TOP_CONTEXT == NULL) {
+ init_session_vars();
+ }
+
+ MemoryContext oldcontext = MemoryContextSwitchTo(TOP_CONTEXT->memctx);
+ XMLGenContext *ctx = static_cast(palloc0(sizeof(XMLGenContext)));
+ ctx->memctx = AllocSetContextCreate(TOP_CONTEXT->memctx, "XMLGENContextMemory", ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
+ MemoryContextSwitchTo(oldcontext);
+
+ oldcontext = MemoryContextSwitchTo(ctx->memctx);
+
+ ctx->ctx_id = ctx_id;
+ ctx->query_string = NULL;
+ ctx->null_flag = 0;
+ ctx->row_tag_name = "ROW";
+ ctx->row_set_tag_name = "ROWSET";
+ ctx->is_convert_special_chars = true;
+ ctx->max_rows = -1;
+ ctx->current_row = 0;
+ ctx->skip_rows = 0;
+ ctx->processed_rows = 0;
+ ctx->is_from_hierarchy = false;
+ ctx->is_hierarchy_set_row_set_tag = false;
+ ctx->is_cursor = false;
+ ctx->is_query = false;
+ ctx->item_tag_name = NULL;
+
+ MemoryContextSwitchTo(oldcontext);
+ return ctx;
+}
+
+/**
+ * create a new XMLGenContext by sequnce, and add it to global context list
+ * @param {char *} query_str
+ * @param {bool} is_from_herarchy
+ * @param {bool} is_cursor
+ */
+static int64 new_xmlgen_context(char *query_str, bool is_from_hierarchy, bool is_cursor)
+{
+ MemoryContext oldcontext = NULL;
+ int64 new_ctx_id = ++(TOP_CONTEXT->xmlgen_context_id);
+ check_uint_valid(new_ctx_id);
+ XMLGenContext *ctx = create_xmlgen_context(new_ctx_id);
+
+ oldcontext = MemoryContextSwitchTo(ctx->memctx);
+ char *qs = static_cast(palloc(sizeof(char) * strlen(query_str) + 1));
+ errno_t et = strcpy_s(qs, strlen(query_str) + 1, query_str);
+ securec_check(et, "\0", "\0");
+ ctx->query_string = qs;
+ MemoryContextSwitchTo(oldcontext);
+ ctx->is_from_hierarchy = is_from_hierarchy;
+ ctx->is_cursor = is_cursor;
+ if (TOP_CONTEXT->xmlgen_context_list != NIL && !IsA(TOP_CONTEXT->xmlgen_context_list, List)) {
+ TOP_CONTEXT->xmlgen_context_list = NIL;
+ }
+
+ oldcontext = MemoryContextSwitchTo(TOP_CONTEXT->memctx);
+ TOP_CONTEXT->xmlgen_context_list = lappend(TOP_CONTEXT->xmlgen_context_list, (void *)ctx);
+ MemoryContextSwitchTo(oldcontext);
+
+ return new_ctx_id;
+}
+
+/**
+ * cast string to ctxhandle
+ * @param {string} str
+ * @return {ctxhandle} result
+ */
+Datum ctxhandle_in(PG_FUNCTION_ARGS)
+{
+ char *str = PG_GETARG_CSTRING(0);
+ int64 result;
+ (void)scanint8(str, false, &result);
+
+ PG_RETURN_NUMERIC(convert_int64_to_numeric(result, 0));
+}
+
+/**
+ * cast ctxhandle to string
+ * @param {ctxhandle} val
+ * @return {string} result
+ */
+Datum ctxhandle_out(PG_FUNCTION_ARGS)
+{
+ Numeric num = PG_GETARG_NUMERIC(0);
+ char *str = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num)));
+ PG_RETURN_CSTRING(str);
+}
+
+/**
+ * close xmlgen context and release all resources, including the SQL cursor and bind and define buffers.
+ * @param {ctxhandle} ctx - the context handle to close.
+ * @return {void}
+ */
+Datum close_context(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ PG_RETURN_VOID();
+ }
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+
+ if (ctx != NULL) {
+ free_xmlgen_context(ctx);
+ }
+ PG_RETURN_VOID();
+}
+
+/**
+ * converts the xml data into the escaped or unescapes XML equivalent,
+ * and returns XML clob data in endcoded or decoded format.
+ * @param {varchar2} xmlData - the xml clob data to encoded or decoded.
+ * @param {number} flag - the flag setting. 1 for decode, others for encode(default).
+ * @return {void}
+ */
+Datum convert_xml(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid parameter")));
+ }
+ int64 flag = 0;
+ if (!PG_ARGISNULL(1)) {
+ flag = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(1));
+ }
+
+ if (flag < 0 || flag > UINT_MAX) {
+ PG_RETURN_NULL();
+ }
+
+ char *xmlstr = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ char *str = NULL;
+
+ if (flag == 1) {
+ str = convert_decode((const char *)xmlstr);
+ } else {
+ str = convert_encode((const char *)xmlstr);
+ }
+
+ VarChar *result = (VarChar *)cstring_to_text((const char *)str);
+ pfree(str);
+ pfree(xmlstr);
+ PG_RETURN_VARCHAR_P(result);
+}
+
+/**
+ * converts the xml data into the escaped or unescapes XML equivalent,
+ * and returns XML clob data in endcoded or decoded format.
+ * @param {clob} xmlData - the xml clob data to encoded or decoded.
+ * @param {number} flag - the flag setting; 1 for decode, others for encode.
+ * @return {clob} result
+ */
+Datum convert_clob(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid parameter")));
+ }
+ int64 flag = 0;
+ if (!PG_ARGISNULL(1)) {
+ flag = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(1));
+ }
+
+ char *xmlstr = text_to_cstring((const text *)PG_GETARG_BYTEA_PP(0));
+ if (flag < 0 || flag > UINT_MAX) {
+ pfree(xmlstr);
+ PG_RETURN_NULL();
+ }
+
+ char *str = NULL;
+
+ if (flag == 1) {
+ str = convert_decode((const char *)xmlstr);
+ } else {
+ str = convert_encode((const char *)xmlstr);
+ }
+
+ xmltype *result = (xmltype *)cstring_to_text((const char *)str);
+ pfree(str);
+ pfree(xmlstr);
+ PG_RETURN_XML_P(result);
+}
+
+/**
+ * retrieves the number of SQL rows processed when generating the xml using the getxml functions call.
+ * @param {ctxhandle} ctx_id
+ * @return {number} result
+ */
+Datum get_num_rows_processed(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx == NULL) {
+ PG_RETURN_NULL();
+ }
+ PG_RETURN_NUMERIC(convert_int64_to_numeric(ctx->processed_rows, 0));
+}
+
+/**
+ * gets xml document by context id
+ * @param {ctxhandle} ctx_id
+ * @param {number} dtdOrSchema - a flag to generate a DTD or a schema, default 0
+ * @return {clob} result - xml data
+ */
+Datum get_xml_by_ctx_id(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid gms_xmlgen context id")));
+ }
+
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ int64 dtd_or_schema = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(1));
+ check_uint_valid(dtd_or_schema);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx == NULL || ctx->query_string == NULL) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid gms_xmlgen context found")));
+ }
+ StringInfo si = NULL;
+ xmltype *res = NULL;
+ si = query_to_xml_internal(ctx);
+ res = stringinfo_to_xmltype(si);
+ DestroyStringInfo(si);
+ PG_RETURN_XML_P(res);
+}
+
+/**
+ * gets xml document by query string
+ * @param {varchar2} query string
+ * @param {number} dtdOrSchema - a flag to generate a DTD or a schema, default 0
+ * @return {clob} result - xml data
+ */
+Datum get_xml_by_query(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid query string")));
+ }
+ char *query_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ check_query_string_valid(query_str);
+ int64 dtd_or_schema = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(1));
+ check_uint_valid(dtd_or_schema);
+ XMLGenContext *ctx = create_xmlgen_context(0);
+ ctx->query_string = query_str;
+ StringInfo si = query_to_xml_internal(ctx);
+ xmltype *res = stringinfo_to_xmltype(si);
+ DestroyStringInfo(si);
+ pfree_ext(query_str);
+ free_xmlgen_context(ctx);
+ PG_RETURN_XML_P(res);
+}
+
+/**
+ * generate and return a new context handle from a query string
+ * @param {varchar2} query string
+ * @return {ctxhandle} result - context id
+ */
+Datum new_context_by_query(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid query string")));
+ }
+ char *query_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ int64 result = new_xmlgen_context(query_str);
+ PG_RETURN_NUMERIC(convert_int64_to_numeric((int64)result, 0));
+}
+
+/**
+ * generate and return a new context handle from a query string in the form of a PL/SQL ref cursor
+ * @param {sys_refcursor} query string
+ * @return {ctxhandle} result - context id
+ */
+Datum new_context_by_cursor(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ char *query_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ int64 result = new_xmlgen_context(query_str, false, true);
+ PG_RETURN_NUMERIC(convert_int64_to_numeric((int64)result, 0));
+}
+
+/**
+ * generate and return a new hierarchy context handle from query string
+ * @param {varchar2} query string
+ * @return {ctxhandle} result - context id
+ */
+Datum new_context_from_hierarchy(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid query string")));
+ }
+ char *query_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
+ int64 result = new_xmlgen_context(query_str, true);
+ PG_RETURN_NUMERIC(convert_int64_to_numeric((int64)result, 0));
+}
+
+/**
+ * restart the query and generate the XML from the first row
+ * used to start executing the query again, without having to create a new context
+ * @param {ctxhandle} ctx_id
+ * @return {void}
+ */
+Datum restart_query(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid gms_xmlgen context id")));
+ }
+
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx == NULL) {
+ PG_RETURN_NULL();
+ }
+
+ if (ctx->is_cursor) {
+ if (SPI_connect() != SPI_OK_CONNECT) {
+ SPI_finish();
+ ereport(ERROR, (errcode(ERRCODE_SPI_CONNECTION_FAILURE), errmsg("SPI_connect failed")));
+ }
+
+ Portal portal = SPI_cursor_find((const char *)ctx->query_string);
+ if (portal == NULL) {
+ SPI_finish();
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_CURSOR), errmsg("cursor \"%s\" does not exist", ctx->query_string)));
+ }
+
+ SPI_cursor_move(portal, false,
+ ctx->current_row + (ctx->max_rows < 0 || ctx->processed_rows < ctx->max_rows ? 1 : 0));
+
+ SPI_finish();
+ }
+ ctx->current_row = 0;
+ PG_RETURN_NULL();
+}
+
+/**
+ * set whether or not special characters in the XML data must be converted into their escaped XML equivalent
+ * @param {ctxhandle} ctx_id
+ * @param {boolean} is_convert
+ * @return {void}
+ */
+Datum set_convert_special_chars(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ PG_RETURN_VOID();
+ }
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ bool is_convert = PG_GETARG_BOOL(1);
+ if (ctx == NULL) {
+ PG_RETURN_VOID();
+ }
+ ctx->is_convert_special_chars = is_convert;
+ PG_RETURN_VOID();
+}
+
+/**
+ * set the maximum number of rows to fetch from the SQL query result for every invocation of the GETXML functions call
+ * @param {ctxhandle} ctx_id
+ * @param {number} max_rows
+ * @return {void}
+ */
+Datum set_max_rows(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ PG_RETURN_VOID();
+ }
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ int64 max_rows = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(1));
+ check_uint_valid(max_rows);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx == NULL) {
+ PG_RETURN_VOID();
+ }
+ if (ctx->is_from_hierarchy) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("this operation is invalid in the hierarchy context")));
+ }
+
+ ctx->max_rows = max_rows;
+ PG_RETURN_VOID();
+}
+
+/**
+ * set null handling options, handled through the flag parameter setting
+ * @param {ctxhandle} ctx_id
+ * @param {number} flag - 0 for drop nulls(default); 1 for null attribute; 2 for empty tags
+ * @return {void}
+ */
+Datum set_null_handling(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ int64 flag = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(1));
+ check_uint_valid(flag);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx != NULL) {
+ ctx->null_flag = flag;
+ }
+ PG_RETURN_VOID();
+}
+
+/**
+ * set the name of the root element of the xml
+ * @param {ctxhandle} ctx_id
+ * @param {varchar2} row_set_tag_name
+ * @return {void}
+ */
+Datum set_row_set_tag(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ PG_RETURN_VOID();
+ }
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx == NULL) {
+ PG_RETURN_VOID();
+ }
+ if (ctx->is_from_hierarchy) {
+ ctx->is_hierarchy_set_row_set_tag = true;
+ }
+
+ if (PG_ARGISNULL(1)) {
+ ctx->row_set_tag_name = NULL;
+ PG_RETURN_VOID();
+ }
+
+ char *row_set_tag_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ MemoryContext oldcontext = MemoryContextSwitchTo(ctx->memctx);
+ char *row_set_tag_inner = static_cast(palloc(sizeof(char) * strlen(row_set_tag_name) + 1));
+ errno_t et = strcpy_s(row_set_tag_inner, strlen(row_set_tag_name) + 1, row_set_tag_name);
+ securec_check(et, "\0", "\0");
+ ctx->row_set_tag_name = map_sql_identifier_to_xml_name(row_set_tag_inner, false, false);
+ pfree_ext(row_set_tag_inner);
+ MemoryContextSwitchTo(oldcontext);
+ pfree_ext(row_set_tag_name);
+ PG_RETURN_VOID();
+}
+
+/**
+ * set the name of the element separating all the rows.
+ * @param {ctxhandle} ctx_id
+ * @param {varchar2} row_tag_name
+ * @return {void}
+ */
+Datum set_row_tag(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ PG_RETURN_VOID();
+ }
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx == NULL) {
+ PG_RETURN_VOID();
+ }
+ if (ctx->is_from_hierarchy) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("this operation is invalid in the hierarchy context")));
+ }
+
+ if (PG_ARGISNULL(1)) {
+ ctx->row_tag_name = NULL;
+ PG_RETURN_VOID();
+ }
+
+ char *row_tag_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ MemoryContext oldcontext = MemoryContextSwitchTo(ctx->memctx);
+
+ char *row_tag_inner = static_cast(palloc(sizeof(char) * strlen(row_tag_name) + 1));
+ errno_t et = strcpy_s(row_tag_inner, strlen(row_tag_name) + 1, row_tag_name);
+ securec_check(et, "\0", "\0");
+ ctx->row_tag_name = map_sql_identifier_to_xml_name(row_tag_inner, false, false);
+ pfree_ext(row_tag_inner);
+ MemoryContextSwitchTo(oldcontext);
+ pfree_ext(row_tag_name);
+ PG_RETURN_VOID();
+}
+
+/**
+ * skip a given number of rows before generating the XML output for every call to the getxml functions
+ * @param {ctxhandle} ctx_id
+ * @param {number} skip_rows
+ * @return {void}
+ */
+Datum set_skip_rows(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ PG_RETURN_VOID();
+ }
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ int64 skip_rows = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(1));
+ check_uint_valid(skip_rows);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx == NULL) {
+ PG_RETURN_VOID();
+ }
+ if (ctx->is_from_hierarchy) {
+ ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("this operation is invalid in the hierarchy context")));
+ }
+
+ ctx->skip_rows = skip_rows;
+ PG_RETURN_VOID();
+}
+
+/**
+ * override the default name of the collection elements.
+ * @param {ctxhandle} ctx_id
+ * @return {void}
+ */
+Datum use_item_tags_for_coll(PG_FUNCTION_ARGS)
+{
+ CHECK_XML_SUPPORT();
+
+ if (PG_ARGISNULL(0)) {
+ PG_RETURN_VOID();
+ }
+ int64 ctx_id = (int64)numeric_int16_internal(PG_GETARG_NUMERIC(0));
+ check_uint_valid(ctx_id);
+ XMLGenContext *ctx = get_xmlgen_context(ctx_id);
+ if (ctx == NULL) {
+ PG_RETURN_VOID();
+ }
+
+ ctx->item_tag_name = "_ITEM";
+ PG_RETURN_VOID();
+}
diff --git a/contrib/gms_xmlgen/gms_xmlgen.h b/contrib/gms_xmlgen/gms_xmlgen.h
new file mode 100644
index 000000000..e234cb9ef
--- /dev/null
+++ b/contrib/gms_xmlgen/gms_xmlgen.h
@@ -0,0 +1,77 @@
+#ifndef GMS_XMLGEN_H
+#define GMS_XMLGEN_H
+
+#ifdef USE_LIBXML
+#define CHECK_XML_SUPPORT()
+#else
+#define CHECK_XML_SUPPORT() \
+ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported XML feature"), \
+ errdetail("This functionality requires the server to be built with libxml support.")))
+#endif
+
+// non-negative number between 0 ~ 4294967295
+#define check_uint_valid(ctx_id) \
+ ((ctx_id < 0 || ctx_id > UINT_MAX) \
+ ? ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value is out of range."))) \
+ : (void)0)
+
+#define SPACE_PER_INDENTATION 2
+#define NULL_FLAG_NULL_ATTR 1
+#define NULL_FLAG_EMPTY_TAG 2
+#define MAX_DEPTH 65535
+#define XMLTYPE_STR "xmltype"
+#define XML_HEADER "\n"
+#define XML_XSI_ATTR "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+
+typedef struct XMLGenContext {
+ MemoryContext memctx; // xmlgen memory context
+ int64 ctx_id; // context id for XMLGenContext
+ bool is_query; // is a query or not
+ bool is_cursor; // is a cursor or not
+ char *query_string; // query for xmlgen
+ bool is_from_hierarchy; // is in a hierarchy context
+ bool is_hierarchy_set_row_set_tag; // is set row_set_tag_name in a hierarchy context
+ bool is_convert_special_chars; // is need to encode special chars
+ int64 max_rows; // max row for the xml
+ int64 null_flag; // empty fields display, 0 for null, 1 for nil, 2 for self-close. default 0.
+ char *row_tag_name; // element tag name for root
+ char *row_set_tag_name; // element tag name for each row
+ int64 skip_rows; // skip the first n lines of the query result. default 0.
+ int64 processed_rows; // processed rows when generate xml data.
+ int64 current_row; // gen xml from current_row. default 0.
+ char *item_tag_name; // element tag name suffix for array items
+} XMLGenContext;
+
+typedef struct XMLGenTopContext {
+ MemoryContext memctx;
+ List *xmlgen_context_list;
+ int64 xmlgen_context_id;
+} XMLGenTopContext;
+
+static int64 new_xmlgen_context(char *query_str, bool is_from_hierarchy = false, bool is_cursor = false);
+static char *map_sql_value_to_xml(Datum value, Oid type, bool xml_escape_strings, char *array_tag_suffix = "",
+ int indent_level = 0);
+
+extern "C" Datum ctxhandle_in(PG_FUNCTION_ARGS);
+extern "C" Datum ctxhandle_out(PG_FUNCTION_ARGS);
+extern "C" Datum close_context(PG_FUNCTION_ARGS);
+extern "C" Datum convert_xml(PG_FUNCTION_ARGS);
+extern "C" Datum convert_clob(PG_FUNCTION_ARGS);
+extern "C" Datum get_num_rows_processed(PG_FUNCTION_ARGS);
+extern "C" Datum get_xml_by_ctx_id(PG_FUNCTION_ARGS);
+extern "C" Datum get_xml_by_query(PG_FUNCTION_ARGS);
+extern "C" Datum new_context_by_query(PG_FUNCTION_ARGS);
+extern "C" Datum new_context_by_cursor(PG_FUNCTION_ARGS);
+extern "C" Datum new_context_from_hierarchy(PG_FUNCTION_ARGS);
+extern "C" Datum restart_query(PG_FUNCTION_ARGS);
+extern "C" Datum set_convert_special_chars(PG_FUNCTION_ARGS);
+extern "C" Datum set_max_rows(PG_FUNCTION_ARGS);
+extern "C" Datum set_null_handling(PG_FUNCTION_ARGS);
+extern "C" Datum set_row_set_tag(PG_FUNCTION_ARGS);
+extern "C" Datum set_row_tag(PG_FUNCTION_ARGS);
+extern "C" Datum set_skip_rows(PG_FUNCTION_ARGS);
+extern "C" Datum use_item_tags_for_coll(PG_FUNCTION_ARGS);
+extern "C" void set_extension_index(uint32 index);
+extern "C" void init_session_vars(void);
+
+#endif // GMS_XMLGEN_H
diff --git a/contrib/gms_xmlgen/sql/gms_xmlgen.sql b/contrib/gms_xmlgen/sql/gms_xmlgen.sql
new file mode 100644
index 000000000..6d2c678cb
--- /dev/null
+++ b/contrib/gms_xmlgen/sql/gms_xmlgen.sql
@@ -0,0 +1,1182 @@
+create extension gms_xmlgen;
+create extension gms_output;
+select gms_output.enable(100000);
+create schema gms_xmlgen_test;
+set search_path = gms_xmlgen_test;
+set behavior_compat_options = 'bind_procedure_searchpath';
+-- prepare data
+create table t_types (
+ "integer" integer,
+ "float" float,
+ "numeric" numeric(20, 6),
+ "boolean" boolean,
+ "char" char(20),
+ "varchar" varchar(20),
+ "text" text,
+ "blob" blob,
+ "raw" raw,
+ "date" date,
+ "time" time,
+ "timestamp" timestamp,
+ "json" json,
+ "varchar_array" varchar(20)[]
+);
+insert into t_types
+values(
+ 1,
+ 1.23456,
+ 1.234567,
+ true,
+ '"''<>&char test',
+ 'varchar"''<>&test',
+ 'text test"''<>&',
+ 'ff',
+ hextoraw('ABCD'),
+ '2024-01-02',
+ '18:01:02',
+ '2024-02-03 19:03:04',
+ '{"a" : 1, "b" : 2}',
+ array['abc', '"''<>&', '你好']
+ ),
+ (
+ 2,
+ 2.23456,
+ 2.234567,
+ false,
+ '2"''<>&char test',
+ '2varchar"''<>&test',
+ '2text test"''<>&',
+ 'eeee',
+ hextoraw('ffff'),
+ '2026-03-04',
+ '20:12:13',
+ '2026-05-06 21:13:00',
+ '[9,8,7,6]',
+ array['&^%@', '"''<>&', '<&y''">']
+ ),
+ (
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ );
+
+-- GMS_XMLGEN.NEWCONTEXT && GMS_XMLGEN.CLOSECONTEXT
+select gms_xmlgen.newcontext('select * from t_types');
+select gms_xmlgen.getxml(1);
+select gms_xmlgen.closecontext(1);
+-- procedure case
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- newcontext by cursor
+DECLARE
+CURSOR xc is select * from t_types;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('xc'::refcursor);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- newcontext by cursor expression
+DECLARE
+CURSOR xc is select "integer", CURSOR(select * from t_types) from t_types;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+gms_xmlgen.closecontext(xml_cxt);
+close xc;
+END;
+/
+-- invalid null parameter
+select gms_xmlgen.newcontext(NULL);
+-- ok for invalid query sql
+select gms_xmlgen.newcontext('aabbccdd');
+-- ok for closecontext NULL
+select gms_xmlgen.closecontext(NULL);
+-- ok for closecontext not exist id
+select gms_xmlgen.closecontext(99);
+-- error for closecontext invalid range
+select gms_xmlgen.closecontext(-1);
+select gms_xmlgen.closecontext(4294967296);
+
+-- GMS_XMLGEN.GETXML
+-- getxml by query
+DECLARE
+xml_output clob;
+BEGIN
+xml_output := gms_xmlgen.getxml('select * from t_types');
+gms_output.put_line(xml_output);
+END;
+/
+-- getxml by cursor
+DECLARE
+CURSOR xc is select * from t_types;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+close xc;
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getxml by cursor expression
+DECLARE
+CURSOR xc is select "integer", CURSOR(select * from t_types) from t_types;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+close xc;
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getxml by context id
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getxml by context id with out parameter
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.getxml(xml_cxt, xml_output);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- no result when getxml twice without restartquery
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.getxml(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- invalid null parameter
+select gms_xmlgen.getxml(NULL);
+-- invalid query sql
+select gms_xmlgen.getxml('aabbccdd');
+-- invalid xmlgen context id
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+END;
+/
+-- invalid xmlgen context id with out parameter
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.getxml(xml_cxt, xml_output);
+gms_output.put_line(xml_output);
+END;
+/
+
+-- GMS_XMLGEN.GETXMLTYPE
+-- getxmltype by query
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_type := gms_xmlgen.getxmltype('select * from t_types');
+gms_output.put_line(xml_type::text);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getxmltype by query with parameter 2
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_type := gms_xmlgen.getxmltype('select * from t_types', 1);
+gms_output.put_line(xml_type::text);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getxmltype by context id
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_type := gms_xmlgen.getxmltype(xml_cxt);
+gms_output.put_line(xml_type::text);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getxmltype by context id with parameter 2
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_type := gms_xmlgen.getxmltype(xml_cxt, 1);
+gms_output.put_line(xml_type::text);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getxmltype by cursor
+DECLARE
+CURSOR xc is select * from t_types;
+xml_cxt gms_xmlgen.ctxhandle;
+xml_output xmltype;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+xml_output := gms_xmlgen.getxmltype(xml_cxt);
+gms_output.put_line(xml_output::text);
+close xc;
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getxmltype by cursor expression
+DECLARE
+CURSOR xc is select "integer", CURSOR(select * from t_types) from t_types;
+xml_cxt gms_xmlgen.ctxhandle;
+xml_output xmltype;
+BEGIN
+open xc;
+xml_cxt := gms_xmlgen.newcontext(xc);
+xml_output := gms_xmlgen.getxmltype(xml_cxt);
+gms_output.put_line(xml_output::text);
+close xc;
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- invalid null parameter
+select gms_xmlgen.getxmltype(NULL);
+-- invalid query sql
+select gms_xmlgen.getxmltype('aabbccdd');
+-- invalid context id
+DECLARE
+xml_cxt gms_xmlgen.ctxhandle;
+xml_type xmltype;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+xml_type := gms_xmlgen.getxmltype(xml_cxt);
+END;
+/
+-- invalid parameter 2 range
+select gms_xmlgen.getxmltype('select * from t_types', -1);
+select gms_xmlgen.getxmltype('select * from t_types', 4294967296);
+
+-- GMS_XMLGEN.NEWCONTEXTFROMHIERARCHY
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+-- with set row set tag
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+gms_xmlgen.setrowsettag(xml_cxt_from_hierarchy, 'TopTag');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+-- error with set row tag
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+gms_xmlgen.setrowtag(xml_cxt_from_hierarchy, 'TopTag');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+-- error with setmaxrows
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+gms_xmlgen.setmaxrows(xml_cxt_from_hierarchy, 1);
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+-- error with setskiprows
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('
+SELECT "integer", xmltype(gms_xmlgen.getxml(''select * from t_types''))
+FROM t_types
+START WITH "integer" = 1 OR "integer" = 2
+CONNECT BY nocycle "integer" = PRIOR "integer"');
+gms_xmlgen.setskiprows(xml_cxt_from_hierarchy, 1);
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+-- invalid null parameter
+select gms_xmlgen.newcontextfromhierarchy(NULL);
+-- ok for invalid query sql
+select gms_xmlgen.newcontextfromhierarchy('aabbccdd');
+-- get xml error with invalid query sql
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('aabbccdd');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+-- get xml error with not hierarchy query sql
+DECLARE
+xml_output clob;
+xml_cxt_from_hierarchy gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt_from_hierarchy := gms_xmlgen.newcontextfromhierarchy('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt_from_hierarchy);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt_from_hierarchy);
+END;
+/
+
+-- GMS_XMLGEN.RESTARTQUERY
+-- get xml twice
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.restartquery(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for restartquery closed context id
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.restartquery(xml_cxt);
+END;
+/
+
+-- GMS_XMLGEN.SETCONVERTSPECIALCHARS
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setconvertspecialchars(xml_cxt, false);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.setconvertspecialchars(xml_cxt, true);
+gms_xmlgen.restartquery(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- error for missing parameter 2
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setconvertspecialchars(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setconvertspecialchars(xml_cxt, true);
+END;
+/
+
+-- GMS_XMLGEN.SETMAXROWS
+-- set max rows 0
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, 0);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- set max rows 1
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, 1);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, -1);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, 4294967296);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setmaxrows(xml_cxt, 1);
+END;
+/
+
+-- GMS_XMLGEN.SETNULLHANDLING
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt, 0);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.setnullhandling(xml_cxt, 1);
+gms_xmlgen.restartquery(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.setnullhandling(xml_cxt, 2);
+gms_xmlgen.restartquery(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- other numbers > 2, the same as 0
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt, 3);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+gms_xmlgen.setnullhandling(NULL, 1);
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt, -1);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setnullhandling(xml_cxt, 4294967296);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setnullhandling(xml_cxt, 1);
+END;
+/
+
+-- GMS_XMLGEN.SETROWSETTAG
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt, 'test');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- error for setrowsettag NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt, NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for setrowsettag NULL with one row
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types where rownum = 1');
+gms_xmlgen.setrowsettag(xml_cxt, NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for setrowsettag context id null
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+gms_xmlgen.setrowsettag(NULL, 'test');
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter type error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt, true);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setrowsettag(xml_cxt, 'test');
+END;
+/
+
+-- GMS_XMLGEN.SETROWTAG
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowtag(xml_cxt, 'test');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for setrowtag NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowtag(xml_cxt, NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- error for setrowsettag NULL && setrowtag NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowsettag(xml_cxt, NULL);
+gms_xmlgen.setrowtag(xml_cxt, NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for setrowtag context id null
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+gms_xmlgen.setrowtag(NULL, 'test');
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowtag(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter type error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setrowtag(xml_cxt, true);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setrowtag(xml_cxt, 'test');
+END;
+/
+
+-- GMS_XMLGEN.SETSKIPROWS
+-- set skip row 0
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 0);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- set skip row 1
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 1);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- set skip row 2
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 2);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for setskiprows context id null
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(NULL, 1);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter nums error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, -1);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- parameter range error
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 4294967296);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.setskiprows(xml_cxt, 1);
+END;
+/
+
+-- GMS_XMLGEN.USEITEMTAGSFORCOLL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.useitemtagsforcoll(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for useitemtagsforcoll context id NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.useitemtagsforcoll(NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.useitemtagsforcoll(xml_cxt);
+END;
+/
+
+-- GMS_XMLGEN.USENULLATTRIBUTEINDICATOR
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.usenullattributeindicator(xml_cxt);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for parameter 2 true
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.usenullattributeindicator(xml_cxt, true);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for parameter 2 false, the result is the same as the true's
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.usenullattributeindicator(xml_cxt, false);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for usenullattributeindicator context id NULL
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.usenullattributeindicator(NULL);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.closecontext(xml_cxt);
+gms_xmlgen.usenullattributeindicator(xml_cxt);
+END;
+/
+
+-- GMS_XMLGEN.GETNUMROWSPROCESSED
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getnumrowsprocessed with skip rows
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 1);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getnumrowsprocessed with max rows
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setmaxrows(xml_cxt, 2);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- getnumrowsprocessed with skip rows && max rows
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+gms_xmlgen.setskiprows(xml_cxt, 1);
+gms_xmlgen.setmaxrows(xml_cxt, 1);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_output.put_line(xml_output);
+processed_row:=gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for getnumrowsprocessed context id NULL
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+processed_row := gms_xmlgen.getnumrowsprocessed(NULL);
+gms_output.put_line(processed_row);
+gms_xmlgen.closecontext(xml_cxt);
+END;
+/
+-- ok for closed context
+DECLARE
+processed_row number;
+xml_output clob;
+xml_cxt gms_xmlgen.ctxhandle;
+BEGIN
+xml_cxt := gms_xmlgen.newcontext('select * from t_types');
+xml_output := gms_xmlgen.getxml(xml_cxt);
+gms_xmlgen.closecontext(xml_cxt);
+processed_row := gms_xmlgen.getnumrowsprocessed(xml_cxt);
+gms_output.put_line(processed_row);
+END;
+/
+
+-- GMS_XMLGEN.CONVERT
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2);
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, NULL);
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 0);
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 1);
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 2);
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, -1);
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 4294967295);
+select GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 4294967296);
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2);
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2, NULL);
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2, 0);
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2, 1);
+select GMS_XMLGEN.CONVERT('"'<>&'::varchar2, 2);
+-- would not convert
+select GMS_XMLGEN.CONVERT('"&ApOS;<&gT;&Amp;'::varchar2, 1);
+select pg_typeof(GMS_XMLGEN.CONVERT('"''<>&'::varchar2, 1));
+select GMS_XMLGEN.CONVERT('"''<>&'::clob);
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, NULL);
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 0);
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 1);
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 2);
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, -1);
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 4294967295);
+select GMS_XMLGEN.CONVERT('"''<>&'::clob, 4294967296);
+select GMS_XMLGEN.CONVERT('"'<>&'::clob);
+select GMS_XMLGEN.CONVERT('"'<>&'::clob, NULL);
+select GMS_XMLGEN.CONVERT('"'<>&'::clob, 0);
+select GMS_XMLGEN.CONVERT('"'<>&'::clob, 1);
+select GMS_XMLGEN.CONVERT('"'<>&'::clob, 2);
+-- would not convert
+select GMS_XMLGEN.CONVERT('"&ApOS;<&gT;&Amp;'::clob, 1);
+select pg_typeof(GMS_XMLGEN.CONVERT('"''<>&'::clob, 1));
+-- error for NULL
+select GMS_XMLGEN.CONVERT(NULL, 0);
+select GMS_XMLGEN.CONVERT(NULL);
+
+-- compatibility tool usecases
+DECLARE ctx GMS_xmlgen.ctxHandle;
+BEGIN ctx := GMS_xmlgen.newContext('select * FROM t_types');
+GMS_xmlgen.closeContext(ctx);
+GMS_output.put_line(ctx::text);
+END;
+/
+DECLARE res XMLType;
+BEGIN res := GMS_XMLGEN.GETXMLTYPE('123');
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE res GMS_XMLGEN.ctxHandle;
+BEGIN res := GMS_XMLGEN.NEWCONTEXTFROMHIERARCHY('123');
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE d varchar2(100);
+a varchar2(100);
+BEGIN d := GMS_XMLGEN.CONVERT(a);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE d number;
+a GMS_XMLGEN.ctxHandle;
+BEGIN d := GMS_XMLGEN.GETNUMROWSPROCESSED(a);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE a GMS_XMLGEN.ctxHandle;
+b clob;
+BEGIN b := GMS_XMLGEN.GETXML(a);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN GMS_XMLGEN.RESTARTQUERY(CTX);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.SETCONVERTSPECIALCHARS(CTX, false);
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN GMS_XMLGEN.SETMAXROWS(CTX, 2);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.SETNULLHANDLING(CTX, 1);
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.SETROWSETTAG(CTX, 'srst');
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.SETROWTAG(CTX, 'srst');
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN GMS_XMLGEN.SETSKIPROWS(CTX, 5);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN GMS_XMLGEN.USEITEMTAGSFORCOLL(CTX);
+EXCEPTION
+WHEN OTHERS THEN NULL;
+END;
+/
+DECLARE CTX GMS_XMLGEN.CTXHANDLE;
+BEGIN CTX := GMS_XMLGEN.NEWCONTEXT('select * FROM t_types');
+GMS_XMLGEN.USENULLATTRIBUTEINDICATOR(CTX, false);
+END;
+/
+
+reset search_path;
+drop schema gms_xmlgen_test cascade;
diff --git a/src/common/backend/utils/adt/xml.cpp b/src/common/backend/utils/adt/xml.cpp
index 2ffa3cc88..0f938e7f4 100644
--- a/src/common/backend/utils/adt/xml.cpp
+++ b/src/common/backend/utils/adt/xml.cpp
@@ -364,7 +364,7 @@ static void appendStringInfoText(StringInfo str, const text* t)
}
#endif
-static xmltype* stringinfo_to_xmltype(StringInfo buf)
+xmltype* stringinfo_to_xmltype(StringInfo buf)
{
return (xmltype*)cstring_to_text_with_len(buf->data, buf->len);
}
diff --git a/src/gausskernel/optimizer/commands/dropcmds.cpp b/src/gausskernel/optimizer/commands/dropcmds.cpp
index 0703a39e6..55392914b 100644
--- a/src/gausskernel/optimizer/commands/dropcmds.cpp
+++ b/src/gausskernel/optimizer/commands/dropcmds.cpp
@@ -99,6 +99,7 @@ static void DropExtensionInListIsSupported(List* objname)
"gms_stats",
"gms_output",
"gms_profiler",
+ "gms_xmlgen",
"gms_lob",
"gms_sql"
#endif
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 974efd33c..93e8e2a98 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -80,7 +80,7 @@ extern char* escape_xml(const char* str);
extern char* map_sql_identifier_to_xml_name(char* ident, bool fully_escaped, bool escape_period);
extern char* map_xml_name_to_sql_identifier(char* name);
extern char* map_sql_value_to_xml_value(Datum value, Oid type, bool xml_escape_strings);
-
+extern xmltype* stringinfo_to_xmltype(StringInfo buf);
/* xmltype functions */
extern Datum xmltype_extract(PG_FUNCTION_ARGS);
extern Datum xmltype_extractvalue(PG_FUNCTION_ARGS);