支持gms_sql高级包

This commit is contained in:
humengyao
2024-10-23 23:38:06 -07:00
parent 0c6d6e2cb8
commit c2d3d6453e
21 changed files with 3857 additions and 0 deletions

View File

@ -129,6 +129,8 @@
./share/postgresql/extension/gms_stats.control
./share/postgresql/extension/gms_profiler--1.0.sql
./share/postgresql/extension/gms_profiler.control
./share/postgresql/extension/gms_sql--1.0.sql
./share/postgresql/extension/gms_sql.control
./share/postgresql/timezone/GB-Eire
./share/postgresql/timezone/Turkey
./share/postgresql/timezone/Kwajalein
@ -832,6 +834,7 @@
./lib/postgresql/gms_lob.so
./lib/postgresql/gms_stats.so
./lib/postgresql/gms_profiler.so
./lib/postgresql/gms_sql.so
./lib/libpljava.so
./lib/libpq.a
./lib/libpq.so

View File

@ -117,6 +117,8 @@
./share/postgresql/extension/gms_stats.control
./share/postgresql/extension/gms_profiler--1.0.sql
./share/postgresql/extension/gms_profiler.control
./share/postgresql/extension/gms_sql--1.0.sql
./share/postgresql/extension/gms_sql.control
./share/postgresql/timezone/GB-Eire
./share/postgresql/timezone/Turkey
./share/postgresql/timezone/Kwajalein
@ -802,6 +804,7 @@
./lib/postgresql/gms_lob.so
./lib/postgresql/gms_stats.so
./lib/postgresql/gms_profiler.so
./lib/postgresql/gms_sql.so
./lib/libpljava.so
./lib/libpq.a
./lib/libpq.so

View File

@ -129,6 +129,8 @@
./share/postgresql/extension/gms_stats.control
./share/postgresql/extension/gms_profiler--1.0.sql
./share/postgresql/extension/gms_profiler.control
./share/postgresql/extension/gms_sql--1.0.sql
./share/postgresql/extension/gms_sql.control
./share/postgresql/timezone/GB-Eire
./share/postgresql/timezone/Turkey
./share/postgresql/timezone/Kwajalein
@ -832,6 +834,7 @@
./lib/postgresql/assessment.so
./lib/postgresql/gms_output.so
./lib/postgresql/gms_profiler.so
./lib/postgresql/gms_sql.so
./lib/libpljava.so
./lib/libpq.a
./lib/libpq.so

View File

@ -30,6 +30,7 @@ set(CMAKE_MODULE_PATH
${CMAKE_CURRENT_SOURCE_DIR}/gms_stats
${CMAKE_CURRENT_SOURCE_DIR}/gms_profiler
${CMAKE_CURRENT_SOURCE_DIR}/gms_lob
${CMAKE_CURRENT_SOURCE_DIR}/gms_sql
)
add_subdirectory(hstore)
@ -49,6 +50,7 @@ add_subdirectory(pg_xlogdump)
add_subdirectory(file_fdw)
add_subdirectory(log_fdw)
add_subdirectory(gms_stats)
add_subdirectory(gms_sql)
if("${ENABLE_MULTIPLE_NODES}" STREQUAL "OFF")
add_subdirectory(gc_fdw)
endif()

View File

@ -0,0 +1,21 @@
#This is the main CMAKE for build all gms_sql.
# gms_sql
AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} TGT_gms_sql_SRC)
set(TGT_gms_sql_INC
${PROJECT_OPENGS_DIR}/contrib/gms_sql
${PROJECT_OPENGS_DIR}/contrib
)
set(gms_sql_DEF_OPTIONS ${MACRO_OPTIONS})
set(gms_sql_COMPILE_OPTIONS ${OPTIMIZE_OPTIONS} ${OS_OPTIONS} ${PROTECT_OPTIONS} ${WARNING_OPTIONS} ${LIB_SECURE_OPTIONS} ${CHECK_OPTIONS})
set(gms_sql_LINK_OPTIONS ${LIB_LINK_OPTIONS})
add_shared_libtarget(gms_sql TGT_gms_sql_SRC TGT_gms_sql_INC "${gms_sql_DEF_OPTIONS}" "${gms_sql_COMPILE_OPTIONS}" "${gms_sql_LINK_OPTIONS}")
set_target_properties(gms_sql PROPERTIES PREFIX "")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gms_sql.control
DESTINATION share/postgresql/extension/
)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gms_sql--1.0.sql
DESTINATION share/postgresql/extension/
)
install(TARGETS gms_sql DESTINATION lib/postgresql)

28
contrib/gms_sql/Makefile Normal file
View File

@ -0,0 +1,28 @@
# contrib/gms_sql/Makefile
MODULE_big = gms_sql
OBJS = gms_sql.o
EXTENSION = gms_sql
DATA = gms_sql--1.0.sql
exclude_option = -fPIE
override CPPFLAGS := -fstack-protector-strong $(filter-out $(exclude_option),$(CPPFLAGS))
REGRESS = gms_sql
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/gms_sql
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
regress_home = $(top_builddir)/src/test/regress
REGRESS_OPTS = -c 0 -d 1 -r 1 -p 25632 --single_node -w --keep_last_data=false \
--regconf=$(regress_home)/regress.conf \
--temp-config=$(regress_home)/make_fastcheck_postgresql.conf
include $(top_srcdir)/contrib/contrib-global.mk
endif
gms_sql.o: gms_sql.cpp

View File

@ -0,0 +1 @@
The openGauss regression needs this file to run.

View File

@ -0,0 +1,753 @@
CREATE EXTENSION gms_sql;
set gms_sql_max_open_cursor_count = 501;
ERROR: 501 is outside the valid range for parameter "gms_sql_max_open_cursor_count" (10 .. 500)
reset gms_sql_max_open_cursor_count;
show gms_sql_max_open_cursor_count;
gms_sql_max_open_cursor_count
-------------------------------
100
(1 row)
do $$
declare
c int;
strval varchar;
intval int;
nrows int default 30;
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'select ''ahoj'' || i, i from generate_series(1, :nrows) g(i)', gms_sql.v6);
gms_sql.bind_variable(c, 'nrows', nrows);
gms_sql.define_column(c, 1, strval);
gms_sql.define_column(c, 2, intval);
perform gms_sql.execute(c);
while gms_sql.fetch_rows(c) > 0
loop
gms_sql.column_value(c, 1, strval);
gms_sql.column_value(c, 2, intval);
raise notice 'c1: %, c2: %', strval, intval;
end loop;
gms_sql.close_cursor(c);
end;
$$;
NOTICE: c1: ahoj1, c2: 1
NOTICE: c1: ahoj2, c2: 2
NOTICE: c1: ahoj3, c2: 3
NOTICE: c1: ahoj4, c2: 4
NOTICE: c1: ahoj5, c2: 5
NOTICE: c1: ahoj6, c2: 6
NOTICE: c1: ahoj7, c2: 7
NOTICE: c1: ahoj8, c2: 8
NOTICE: c1: ahoj9, c2: 9
NOTICE: c1: ahoj10, c2: 10
NOTICE: c1: ahoj11, c2: 11
NOTICE: c1: ahoj12, c2: 12
NOTICE: c1: ahoj13, c2: 13
NOTICE: c1: ahoj14, c2: 14
NOTICE: c1: ahoj15, c2: 15
NOTICE: c1: ahoj16, c2: 16
NOTICE: c1: ahoj17, c2: 17
NOTICE: c1: ahoj18, c2: 18
NOTICE: c1: ahoj19, c2: 19
NOTICE: c1: ahoj20, c2: 20
NOTICE: c1: ahoj21, c2: 21
NOTICE: c1: ahoj22, c2: 22
NOTICE: c1: ahoj23, c2: 23
NOTICE: c1: ahoj24, c2: 24
NOTICE: c1: ahoj25, c2: 25
NOTICE: c1: ahoj26, c2: 26
NOTICE: c1: ahoj27, c2: 27
NOTICE: c1: ahoj28, c2: 28
NOTICE: c1: ahoj29, c2: 29
NOTICE: c1: ahoj30, c2: 30
do $$
declare
c int;
strval varchar;
intval int;
nrows int default 30;
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'select ''ahoj'' || i, i from generate_series(1, :nrows) g(i)', gms_sql.v7);
gms_sql.bind_variable(c, 'nrows', nrows);
gms_sql.define_column(c, 1, strval);
gms_sql.define_column(c, 2, intval);
perform gms_sql.execute(c);
while gms_sql.fetch_rows(c) > 0
loop
strval := gms_sql.column_value_f(c, 1, strval);
intval := gms_sql.column_value_f(c, 2, intval);
raise notice 'c1: %, c2: %', strval, intval;
end loop;
gms_sql.close_cursor(c);
end;
$$;
NOTICE: c1: ahoj1, c2: 1
NOTICE: c1: ahoj2, c2: 2
NOTICE: c1: ahoj3, c2: 3
NOTICE: c1: ahoj4, c2: 4
NOTICE: c1: ahoj5, c2: 5
NOTICE: c1: ahoj6, c2: 6
NOTICE: c1: ahoj7, c2: 7
NOTICE: c1: ahoj8, c2: 8
NOTICE: c1: ahoj9, c2: 9
NOTICE: c1: ahoj10, c2: 10
NOTICE: c1: ahoj11, c2: 11
NOTICE: c1: ahoj12, c2: 12
NOTICE: c1: ahoj13, c2: 13
NOTICE: c1: ahoj14, c2: 14
NOTICE: c1: ahoj15, c2: 15
NOTICE: c1: ahoj16, c2: 16
NOTICE: c1: ahoj17, c2: 17
NOTICE: c1: ahoj18, c2: 18
NOTICE: c1: ahoj19, c2: 19
NOTICE: c1: ahoj20, c2: 20
NOTICE: c1: ahoj21, c2: 21
NOTICE: c1: ahoj22, c2: 22
NOTICE: c1: ahoj23, c2: 23
NOTICE: c1: ahoj24, c2: 24
NOTICE: c1: ahoj25, c2: 25
NOTICE: c1: ahoj26, c2: 26
NOTICE: c1: ahoj27, c2: 27
NOTICE: c1: ahoj28, c2: 28
NOTICE: c1: ahoj29, c2: 29
NOTICE: c1: ahoj30, c2: 30
drop table if exists foo;
NOTICE: table "foo" does not exist, skipping
create table foo(a int, b varchar, c numeric);
do $$
declare c int;
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'insert into foo values(:a, :b, :c)', gms_sql.native);
for i in 1..100
loop
gms_sql.bind_variable(c, 'a', i);
gms_sql.bind_variable(c, 'b', 'Ahoj ' || i);
gms_sql.bind_variable(c, 'c', i + 0.033);
perform gms_sql.execute(c);
end loop;
gms_sql.close_cursor(c);
end;
$$;
select * from foo;
a | b | c
-----+----------+---------
1 | Ahoj 1 | 1.033
2 | Ahoj 2 | 2.033
3 | Ahoj 3 | 3.033
4 | Ahoj 4 | 4.033
5 | Ahoj 5 | 5.033
6 | Ahoj 6 | 6.033
7 | Ahoj 7 | 7.033
8 | Ahoj 8 | 8.033
9 | Ahoj 9 | 9.033
10 | Ahoj 10 | 10.033
11 | Ahoj 11 | 11.033
12 | Ahoj 12 | 12.033
13 | Ahoj 13 | 13.033
14 | Ahoj 14 | 14.033
15 | Ahoj 15 | 15.033
16 | Ahoj 16 | 16.033
17 | Ahoj 17 | 17.033
18 | Ahoj 18 | 18.033
19 | Ahoj 19 | 19.033
20 | Ahoj 20 | 20.033
21 | Ahoj 21 | 21.033
22 | Ahoj 22 | 22.033
23 | Ahoj 23 | 23.033
24 | Ahoj 24 | 24.033
25 | Ahoj 25 | 25.033
26 | Ahoj 26 | 26.033
27 | Ahoj 27 | 27.033
28 | Ahoj 28 | 28.033
29 | Ahoj 29 | 29.033
30 | Ahoj 30 | 30.033
31 | Ahoj 31 | 31.033
32 | Ahoj 32 | 32.033
33 | Ahoj 33 | 33.033
34 | Ahoj 34 | 34.033
35 | Ahoj 35 | 35.033
36 | Ahoj 36 | 36.033
37 | Ahoj 37 | 37.033
38 | Ahoj 38 | 38.033
39 | Ahoj 39 | 39.033
40 | Ahoj 40 | 40.033
41 | Ahoj 41 | 41.033
42 | Ahoj 42 | 42.033
43 | Ahoj 43 | 43.033
44 | Ahoj 44 | 44.033
45 | Ahoj 45 | 45.033
46 | Ahoj 46 | 46.033
47 | Ahoj 47 | 47.033
48 | Ahoj 48 | 48.033
49 | Ahoj 49 | 49.033
50 | Ahoj 50 | 50.033
51 | Ahoj 51 | 51.033
52 | Ahoj 52 | 52.033
53 | Ahoj 53 | 53.033
54 | Ahoj 54 | 54.033
55 | Ahoj 55 | 55.033
56 | Ahoj 56 | 56.033
57 | Ahoj 57 | 57.033
58 | Ahoj 58 | 58.033
59 | Ahoj 59 | 59.033
60 | Ahoj 60 | 60.033
61 | Ahoj 61 | 61.033
62 | Ahoj 62 | 62.033
63 | Ahoj 63 | 63.033
64 | Ahoj 64 | 64.033
65 | Ahoj 65 | 65.033
66 | Ahoj 66 | 66.033
67 | Ahoj 67 | 67.033
68 | Ahoj 68 | 68.033
69 | Ahoj 69 | 69.033
70 | Ahoj 70 | 70.033
71 | Ahoj 71 | 71.033
72 | Ahoj 72 | 72.033
73 | Ahoj 73 | 73.033
74 | Ahoj 74 | 74.033
75 | Ahoj 75 | 75.033
76 | Ahoj 76 | 76.033
77 | Ahoj 77 | 77.033
78 | Ahoj 78 | 78.033
79 | Ahoj 79 | 79.033
80 | Ahoj 80 | 80.033
81 | Ahoj 81 | 81.033
82 | Ahoj 82 | 82.033
83 | Ahoj 83 | 83.033
84 | Ahoj 84 | 84.033
85 | Ahoj 85 | 85.033
86 | Ahoj 86 | 86.033
87 | Ahoj 87 | 87.033
88 | Ahoj 88 | 88.033
89 | Ahoj 89 | 89.033
90 | Ahoj 90 | 90.033
91 | Ahoj 91 | 91.033
92 | Ahoj 92 | 92.033
93 | Ahoj 93 | 93.033
94 | Ahoj 94 | 94.033
95 | Ahoj 95 | 95.033
96 | Ahoj 96 | 96.033
97 | Ahoj 97 | 97.033
98 | Ahoj 98 | 98.033
99 | Ahoj 99 | 99.033
100 | Ahoj 100 | 100.033
(100 rows)
truncate foo;
do $$
declare c int;
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'insert into foo values(:a, :b, :c)', gms_sql.native);
for i in 1..100
loop
gms_sql.bind_variable_f(c, 'a', i);
gms_sql.bind_variable_f(c, 'b', 'Ahoj ' || i);
gms_sql.bind_variable_f(c, 'c', i + 0.033);
perform gms_sql.execute(c);
end loop;
gms_sql.close_cursor(c);
end;
$$;
select * from foo;
a | b | c
-----+----------+---------
1 | Ahoj 1 | 1.033
2 | Ahoj 2 | 2.033
3 | Ahoj 3 | 3.033
4 | Ahoj 4 | 4.033
5 | Ahoj 5 | 5.033
6 | Ahoj 6 | 6.033
7 | Ahoj 7 | 7.033
8 | Ahoj 8 | 8.033
9 | Ahoj 9 | 9.033
10 | Ahoj 10 | 10.033
11 | Ahoj 11 | 11.033
12 | Ahoj 12 | 12.033
13 | Ahoj 13 | 13.033
14 | Ahoj 14 | 14.033
15 | Ahoj 15 | 15.033
16 | Ahoj 16 | 16.033
17 | Ahoj 17 | 17.033
18 | Ahoj 18 | 18.033
19 | Ahoj 19 | 19.033
20 | Ahoj 20 | 20.033
21 | Ahoj 21 | 21.033
22 | Ahoj 22 | 22.033
23 | Ahoj 23 | 23.033
24 | Ahoj 24 | 24.033
25 | Ahoj 25 | 25.033
26 | Ahoj 26 | 26.033
27 | Ahoj 27 | 27.033
28 | Ahoj 28 | 28.033
29 | Ahoj 29 | 29.033
30 | Ahoj 30 | 30.033
31 | Ahoj 31 | 31.033
32 | Ahoj 32 | 32.033
33 | Ahoj 33 | 33.033
34 | Ahoj 34 | 34.033
35 | Ahoj 35 | 35.033
36 | Ahoj 36 | 36.033
37 | Ahoj 37 | 37.033
38 | Ahoj 38 | 38.033
39 | Ahoj 39 | 39.033
40 | Ahoj 40 | 40.033
41 | Ahoj 41 | 41.033
42 | Ahoj 42 | 42.033
43 | Ahoj 43 | 43.033
44 | Ahoj 44 | 44.033
45 | Ahoj 45 | 45.033
46 | Ahoj 46 | 46.033
47 | Ahoj 47 | 47.033
48 | Ahoj 48 | 48.033
49 | Ahoj 49 | 49.033
50 | Ahoj 50 | 50.033
51 | Ahoj 51 | 51.033
52 | Ahoj 52 | 52.033
53 | Ahoj 53 | 53.033
54 | Ahoj 54 | 54.033
55 | Ahoj 55 | 55.033
56 | Ahoj 56 | 56.033
57 | Ahoj 57 | 57.033
58 | Ahoj 58 | 58.033
59 | Ahoj 59 | 59.033
60 | Ahoj 60 | 60.033
61 | Ahoj 61 | 61.033
62 | Ahoj 62 | 62.033
63 | Ahoj 63 | 63.033
64 | Ahoj 64 | 64.033
65 | Ahoj 65 | 65.033
66 | Ahoj 66 | 66.033
67 | Ahoj 67 | 67.033
68 | Ahoj 68 | 68.033
69 | Ahoj 69 | 69.033
70 | Ahoj 70 | 70.033
71 | Ahoj 71 | 71.033
72 | Ahoj 72 | 72.033
73 | Ahoj 73 | 73.033
74 | Ahoj 74 | 74.033
75 | Ahoj 75 | 75.033
76 | Ahoj 76 | 76.033
77 | Ahoj 77 | 77.033
78 | Ahoj 78 | 78.033
79 | Ahoj 79 | 79.033
80 | Ahoj 80 | 80.033
81 | Ahoj 81 | 81.033
82 | Ahoj 82 | 82.033
83 | Ahoj 83 | 83.033
84 | Ahoj 84 | 84.033
85 | Ahoj 85 | 85.033
86 | Ahoj 86 | 86.033
87 | Ahoj 87 | 87.033
88 | Ahoj 88 | 88.033
89 | Ahoj 89 | 89.033
90 | Ahoj 90 | 90.033
91 | Ahoj 91 | 91.033
92 | Ahoj 92 | 92.033
93 | Ahoj 93 | 93.033
94 | Ahoj 94 | 94.033
95 | Ahoj 95 | 95.033
96 | Ahoj 96 | 96.033
97 | Ahoj 97 | 97.033
98 | Ahoj 98 | 98.033
99 | Ahoj 99 | 99.033
100 | Ahoj 100 | 100.033
(100 rows)
truncate foo;
do $$
declare
c int;
a int[];
b varchar[];
ca numeric[];
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'insert into foo values(:a, :b, :c)', gms_sql.v6);
a := ARRAY[1, 2, 3, 4, 5];
b := ARRAY['Ahoj', 'Nazdar', 'Bazar'];
ca := ARRAY[3.14, 2.22, 3.8, 4];
perform gms_sql.bind_array(c, 'a', a);
perform gms_sql.bind_array(c, 'b', b);
perform gms_sql.bind_array(c, 'c', ca);
raise notice 'inserted rows %d', gms_sql.execute(c);
gms_sql.close_cursor(c);
end;
$$;
NOTICE: inserted rows 3d
select * from foo;
a | b | c
---+--------+------
1 | Ahoj | 3.14
2 | Nazdar | 2.22
3 | Bazar | 3.8
(3 rows)
truncate foo;
do $$
declare
c int;
a int[];
b varchar[];
ca numeric[];
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'insert into foo values(:a, :b, :c)', gms_sql.v7);
a := ARRAY[1, 2, 3, 4, 5];
b := ARRAY['Ahoj', 'Nazdar', 'Bazar'];
ca := ARRAY[3.14, 2.22, 3.8, 4];
perform gms_sql.bind_array(c, 'a', a, 2, 3);
perform gms_sql.bind_array(c, 'b', b, 3, 4);
perform gms_sql.bind_array(c, 'c', ca);
raise notice 'inserted rows %d', gms_sql.execute(c);
gms_sql.close_cursor(c);
end;
$$;
NOTICE: inserted rows 1d
select * from foo;
a | b | c
---+-------+-----
3 | Bazar | 3.8
(1 row)
truncate foo;
do $$
declare
c int;
a int[];
b varchar[];
ca numeric[];
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'select i, ''Ahoj'' || i, i + 0.003 from generate_series(1, 35) g(i)', 0);
gms_sql.define_array(c, 1, a, 10, 1);
gms_sql.define_array(c, 2, b, 10, 1);
gms_sql.define_array(c, 3, ca, 10, 1);
perform gms_sql.execute(c);
while gms_sql.fetch_rows(c) > 0
loop
gms_sql.column_value(c, 1, a);
gms_sql.column_value(c, 2, b);
gms_sql.column_value(c, 3, ca);
raise notice 'a = %', a;
raise notice 'b = %', b;
raise notice 'c = %', ca;
end loop;
gms_sql.close_cursor(c);
end;
$$;
NOTICE: a = {1,2,3,4,5,6,7,8,9,10}
NOTICE: b = {Ahoj1,Ahoj2,Ahoj3,Ahoj4,Ahoj5,Ahoj6,Ahoj7,Ahoj8,Ahoj9,Ahoj10}
NOTICE: c = {1.003,2.003,3.003,4.003,5.003,6.003,7.003,8.003,9.003,10.003}
NOTICE: a = {11,12,13,14,15,16,17,18,19,20}
NOTICE: b = {Ahoj11,Ahoj12,Ahoj13,Ahoj14,Ahoj15,Ahoj16,Ahoj17,Ahoj18,Ahoj19,Ahoj20}
NOTICE: c = {11.003,12.003,13.003,14.003,15.003,16.003,17.003,18.003,19.003,20.003}
NOTICE: a = {21,22,23,24,25,26,27,28,29,30}
NOTICE: b = {Ahoj21,Ahoj22,Ahoj23,Ahoj24,Ahoj25,Ahoj26,Ahoj27,Ahoj28,Ahoj29,Ahoj30}
NOTICE: c = {21.003,22.003,23.003,24.003,25.003,26.003,27.003,28.003,29.003,30.003}
NOTICE: a = {31,32,33,34,35}
NOTICE: b = {Ahoj31,Ahoj32,Ahoj33,Ahoj34,Ahoj35}
NOTICE: c = {31.003,32.003,33.003,34.003,35.003}
drop table foo;
do $$
declare
l_curid int;
l_cnt int;
l_desctab gms_sql.desc_tab;
l_sqltext varchar(2000);
begin
l_sqltext='select * from pg_object;';
l_curid := gms_sql.open_cursor();
gms_sql.parse(l_curid, l_sqltext, 0);
gms_sql.describe_columns(l_curid, l_cnt, l_desctab);
for i in 1 .. l_desctab.count loop
raise notice '%,% ', l_desctab(i).col_name,l_desctab(i).col_type;
end loop;
gms_sql.close_cursor(l_curid);
end;
$$;
NOTICE: object_oid,109
NOTICE: object_type,96
NOTICE: creator,109
NOTICE: ctime,181
NOTICE: mtime,181
NOTICE: createcsn,2
NOTICE: changecsn,2
NOTICE: valid,109
create table t1(id int, name varchar(20));
insert into t1 select generate_series(1,3), 'abcddd';
create table t2(a int, b date);
insert into t2 values(1, '2022-12-11 10:00:01.123');
insert into t2 values(3, '2022-12-12 12:00:11.13');
do $$
declare
c1 refcursor;
c2 refcursor;
begin
open c1 for select * from t1;
gms_sql.return_result(c1);
open c2 for select * from t2;
gms_sql.return_result(c2);
end;
$$;
ResultSet #1
id | name
----+--------
1 | abcddd
2 | abcddd
3 | abcddd
(3 rows)
ResultSet #2
a | b
---+--------------------------
1 | Sun Dec 11 10:00:01 2022
3 | Mon Dec 12 12:00:11 2022
(2 rows)
create procedure test_result() as
declare
c1 refcursor;
c2 refcursor;
begin
open c1 for select * from t1;
gms_sql.return_result(c1);
open c2 for select * from t2;
gms_sql.return_result(c2);
end;
/
call test_result();
ResultSet #1
id | name
----+--------
1 | abcddd
2 | abcddd
3 | abcddd
(3 rows)
ResultSet #2
a | b
---+--------------------------
1 | Sun Dec 11 10:00:01 2022
3 | Mon Dec 12 12:00:11 2022
(2 rows)
test_result
-------------
(1 row)
drop procedure test_result;
create procedure aam() as
declare
id1 int;
id2 int;
begin
id1 :=gms_sql.open_cursor();
gms_sql.parse(id1,'select * from t1', 1);
perform gms_sql.execute(id1);
gms_sql.return_result(id1);
gms_sql.close_cursor(id1);
id2 :=gms_sql.open_cursor();
gms_sql.parse(id2,'select * from t2', 2);
perform gms_sql.execute(id2);
gms_sql.return_result(id2);
gms_sql.close_cursor(id2);
end;
/
call aam();
ResultSet #1
id | name
----+--------
1 | abcddd
2 | abcddd
3 | abcddd
(3 rows)
ResultSet #2
a | b
---+--------------------------
1 | Sun Dec 11 10:00:01 2022
3 | Mon Dec 12 12:00:11 2022
(2 rows)
aam
-----
(1 row)
drop procedure aam;
create table col_name_too_long(aaaaabbbbbcccccdddddeeeeefffffggg int, col2 text);
do $$
declare
l_curid int;
l_cnt int;
l_desctab gms_sql.desc_tab;
l_desctab2 gms_sql.desc_tab2;
l_sqltext varchar(2000);
begin
l_sqltext='select * from t1;';
l_curid := gms_sql.open_cursor();
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns(l_curid, l_cnt, l_desctab);
for i in 1 .. l_desctab.count loop
raise notice '%', l_desctab(i).col_name;
end loop;
-- output col_name
l_sqltext='select * from col_name_too_long;';
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns2(l_curid, l_cnt, l_desctab2);
for i in 1 .. l_desctab2.count loop
raise notice '%', l_desctab2(i).col_name;
end loop;
-- error
l_sqltext='select * from col_name_too_long;';
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns(l_curid, l_cnt, l_desctab);
for i in 1 .. l_desctab.count loop
raise notice '%', l_desctab(i).col_name;
end loop;
end;
$$;
NOTICE: id
NOTICE: name
NOTICE: aaaaabbbbbcccccdddddeeeeefffffggg
NOTICE: col2
ERROR: desc_rec.col_name(33) is more than 32
CONTEXT: SQL statement "CALL gms_sql.describe_columns(l_curid,l_cnt,l_desctab)"
PL/pgSQL function inline_code_block line 26 at SQL statement
select gms_sql.is_open(0);
is_open
---------
t
(1 row)
select gms_sql.close_cursor(0);
close_cursor
--------------
(1 row)
do $$
declare
l_curid int;
l_cnt int;
l_desctab3 gms_sql.desc_tab3;
l_desctab4 gms_sql.desc_tab4;
l_sqltext varchar(2000);
begin
l_sqltext='select * from col_name_too_long;';
l_curid := gms_sql.open_cursor();
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns3(l_curid, l_cnt, l_desctab3);
for i in 1 .. l_desctab3.count loop
raise notice '%,%,%', l_desctab3(i).col_type,l_desctab3(i).col_type_name,l_desctab3(i).col_name;
end loop;
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns3(l_curid, l_cnt, l_desctab4);
for i in 1 .. l_desctab4.count loop
raise notice '%,%,%,%', l_desctab3(i).col_type,l_desctab4(i).col_type_name,l_desctab4(i).col_type_name_len,l_desctab4(i).col_name_len;
end loop;
gms_sql.close_cursor(l_curid);
end;
$$;
NOTICE: 2,<NULL>,aaaaabbbbbcccccdddddeeeeefffffggg
NOTICE: 109,text,col2
NOTICE: 2,<NULL>,<NULL>,33
NOTICE: 109,text,4,4
drop table t1,t2, col_name_too_long;
select gms_sql.open_cursor();
open_cursor
-------------
0
(1 row)
select gms_sql.is_open(0);
is_open
---------
t
(1 row)
select gms_sql.open_cursor();
open_cursor
-------------
1
(1 row)
select gms_sql.is_open(1);
is_open
---------
t
(1 row)
select gms_sql.open_cursor();
open_cursor
-------------
2
(1 row)
select gms_sql.is_open(2);
is_open
---------
t
(1 row)
select gms_sql.open_cursor();
open_cursor
-------------
3
(1 row)
select gms_sql.is_open(3);
is_open
---------
t
(1 row)
select gms_sql.close_cursor(0);
close_cursor
--------------
(1 row)
select gms_sql.close_cursor(1);
close_cursor
--------------
(1 row)
select gms_sql.close_cursor(2);
close_cursor
--------------
(1 row)
select gms_sql.close_cursor(3);
close_cursor
--------------
(1 row)
select gms_sql.is_open(3);
is_open
---------
f
(1 row)
select gms_sql.close_cursor(10000);
ERROR: cursor 10000 value of cursor id is out of range
CONTEXT: referenced column: close_cursor
select gms_sql.close_cursor(-1);
ERROR: cursor -1 value of cursor id is out of range
CONTEXT: referenced column: close_cursor

View File

@ -0,0 +1,117 @@
/* contrib/gms_sql/gms_sql--1.0.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION gms_sql" to load this file. \quit
-- gms_sql package begin
-- gms_sql schema
CREATE SCHEMA gms_sql;
GRANT USAGE ON SCHEMA gms_sql TO PUBLIC;
CREATE FUNCTION gms_sql.is_open(c int) RETURNS bool AS 'MODULE_PATHNAME', 'gms_sql_is_open' LANGUAGE c STABLE NOT FENCED;
CREATE FUNCTION gms_sql.open_cursor() RETURNS int AS 'MODULE_PATHNAME', 'gms_sql_open_cursor' LANGUAGE c STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.close_cursor(c int) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_close_cursor' STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.debug_cursor(c int) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_debug_cursor' STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.parse(c int, stmt varchar2, ver int) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_parse' STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.bind_variable(c int, name varchar2, value "any") LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_bind_variable' STABLE NOT FENCED;
CREATE FUNCTION gms_sql.bind_variable_f(c int, name varchar2, value "any") RETURNS void AS 'MODULE_PATHNAME', 'gms_sql_bind_variable_f' LANGUAGE c STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.bind_array(c int, name varchar2, value anyarray) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_bind_array_3' package STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.bind_array(c int, name varchar2, value anyarray, index1 int, index2 int) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_bind_array_5' package STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.define_column(c int, col int, value "any", column_size int DEFAULT -1) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_define_column' STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.define_array(c int, col int, value "anyarray", cnt int, lower_bnd int) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_define_array' STABLE NOT FENCED;
CREATE FUNCTION gms_sql.execute(c int) RETURNS bigint AS 'MODULE_PATHNAME', 'gms_sql_execute' LANGUAGE c STABLE NOT FENCED;
CREATE FUNCTION gms_sql.fetch_rows(c int) RETURNS int AS 'MODULE_PATHNAME', 'gms_sql_fetch_rows' LANGUAGE c STABLE NOT FENCED;
CREATE FUNCTION gms_sql.execute_and_fetch(c int, exact bool DEFAULT false) RETURNS int AS 'MODULE_PATHNAME', 'gms_sql_execute_and_fetch' LANGUAGE c STABLE NOT FENCED;
CREATE FUNCTION gms_sql.last_row_count() RETURNS int AS 'MODULE_PATHNAME', 'gms_sql_last_row_count' LANGUAGE c STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.column_value(c int, pos int, INOUT value anyelement) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_column_value' STABLE NOT FENCED;
CREATE FUNCTION gms_sql.column_value_f(c int, pos int, value anyelement) RETURNS anyelement AS 'MODULE_PATHNAME', 'gms_sql_column_value_f' LANGUAGE c STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.return_result(c refcursor, to_client bool DEFAULT false) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_return_result' PACKAGE STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.return_result(c int, to_client bool DEFAULT false) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_return_result_i' PACKAGE STABLE NOT FENCED;
CREATE FUNCTION gms_sql.v6() RETURNS int AS $$
BEGIN
return 0;
END;
$$ language plpgsql;
CREATE FUNCTION gms_sql.native() RETURNS int AS $$
BEGIN
return 1;
END;
$$ language plpgsql;
CREATE FUNCTION gms_sql.v7() RETURNS int AS $$
BEGIN
return 2;
END;
$$ language plpgsql;
CREATE TYPE gms_sql.desc_rec AS (
col_type int,
col_max_len int,
col_name varchar2(32),
col_name_len int,
col_schema_name text,
col_schema_name_len int,
col_precision int,
col_scale int,
col_charsetid int,
col_charsetform int,
col_null_ok boolean);
CREATE TYPE gms_sql.desc_rec2 AS (
col_type int,
col_max_len int,
col_name text,
col_name_len int,
col_schema_name text,
col_schema_name_len int,
col_precision int,
col_scale int,
col_charsetid int,
col_charsetform int,
col_null_ok boolean);
CREATE TYPE gms_sql.desc_rec3 AS (
col_type int,
col_max_len int,
col_name text,
col_name_len int,
col_schema_name text,
col_schema_name_len int,
col_precision int,
col_scale int,
col_charsetid int,
col_charsetform int,
col_null_ok boolean,
col_type_name text,
col_type_name_len int);
CREATE TYPE gms_sql.desc_rec4 AS (
col_type int,
col_max_len int,
col_name text,
col_name_len int,
col_schema_name text,
col_schema_name_len int,
col_precision int,
col_scale int,
col_charsetid int,
col_charsetform int,
col_null_ok boolean,
col_type_name text,
col_type_name_len int);
CREATE TYPE gms_sql.desc_tab IS TABLE OF gms_sql.desc_rec;
CREATE TYPE gms_sql.desc_tab2 IS TABLE OF gms_sql.desc_rec2;
CREATE TYPE gms_sql.desc_tab3 IS TABLE OF gms_sql.desc_rec3;
CREATE TYPE gms_sql.desc_tab4 IS TABLE OF gms_sql.desc_rec4;
CREATE TYPE gms_sql.number_table IS TABLE OF number;
CREATE TYPE gms_sql.varchar2_table IS TABLE OF varchar2;
CREATE TYPE gms_sql.date_table IS TABLE OF date;
CREATE TYPE gms_sql.blob_table IS TABLE OF blob;
CREATE TYPE gms_sql.clob_table IS TABLE OF clob;
CREATE TYPE gms_sql.binary_double_table IS TABLE OF number;
CREATE FUNCTION gms_sql.describe_columns_f(c int, OUT col_cnt int, OUT desc_t gms_sql.desc_rec3[]) AS 'MODULE_PATHNAME', 'gms_sql_describe_columns_f' LANGUAGE c STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.describe_columns(c int, INOUT col_cnt int, INOUT desc_t gms_sql.desc_rec[]) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_describe_columns_f' STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.describe_columns2(c int, INOUT col_cnt int, INOUT desc_t gms_sql.desc_rec2[]) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_describe_columns_f' STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.describe_columns3(c int, INOUT col_cnt int, INOUT desc_t gms_sql.desc_rec3[]) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_describe_columns_f' PACKAGE STABLE NOT FENCED;
CREATE PROCEDURE gms_sql.describe_columns3(c int, INOUT col_cnt int, INOUT desc_t gms_sql.desc_rec4[]) LANGUAGE c AS 'MODULE_PATHNAME', 'gms_sql_describe_columns_f' PACKAGE STABLE NOT FENCED;
-- gms_sql package end

View File

@ -0,0 +1,5 @@
# gms_sql extension
comment = 'collection of sql data for PL/SQL applications'
default_version = '1.0'
module_pathname = '$libdir/gms_sql'
relocatable = true

2295
contrib/gms_sql/gms_sql.cpp Normal file

File diff suppressed because it is too large Load Diff

163
contrib/gms_sql/gms_sql.h Normal file
View File

@ -0,0 +1,163 @@
/*---------------------------------------------------------------------------------------*
* gms_sql.h
*
* Definition about gms_sql package.
*
* IDENTIFICATION
* contrib/gms_sql/gms_sql.h
*
* ---------------------------------------------------------------------------------------
*/
#ifndef GMS_SQL_H
#define GMS_SQL_H
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "parser/parse_coerce.h"
#include "utils/memutils.h"
/*
* It is used for transformation result data to form
* generated by column_value procedure or column
* value function.
*/
typedef struct
{
bool isvalid; /* true, when this cast can be used */
bool without_cast; /* true, when cast is not necessary */
Oid targettypid; /* used for domains */
Oid array_targettypid; /* used for array domains */
int32 targettypmod; /* used for strings */
bool typbyval; /* used for copy result to outer memory context */
int16 typlen; /* used for copy result to outer memory context */
bool is_array;
Oid funcoid;
Oid funcoid_typmod;
CoercionPathType path;
CoercionPathType path_typmod;
FmgrInfo finfo;
FmgrInfo finfo_typmod;
FmgrInfo finfo_out;
FmgrInfo finfo_in;
Oid typIOParam;
} CastCacheData;
/*
* gms_sql cursor definition
*/
typedef struct
{
int16 cid;
char *parsed_query;
char *original_query;
unsigned int nvariables;
int max_colpos;
List *variables;
List *columns;
char cursorname[32];
Portal portal; /* one shot (execute) plan */
SPIPlanPtr plan;
MemoryContext cursor_cxt;
MemoryContext cursor_xact_cxt;
MemoryContext tuples_cxt;
MemoryContext result_cxt; /* short life memory context */
HeapTuple tuples[1000];
TupleDesc coltupdesc;
TupleDesc tupdesc;
CastCacheData *casts;
uint64 processed;
uint64 nread;
uint64 start_read;
bool assigned;
bool executed;
Bitmapset *array_columns; /* set of array columns */
uint64 batch_rows; /* how much rows should be fetched to fill target arrays */
} CursorData;
typedef struct GmssqlContext
{
MemoryContext gms_sql_cxt = NULL;
CursorData *gms_sql_cursors = NULL;
}GmssqlContext;
/*
* bind variable data
*/
typedef struct
{
char *refname;
int position;
Datum value;
Oid typoid;
bool typbyval;
int16 typlen;
bool isnull;
unsigned int varno; /* number of assigned placeholder of parsed query */
bool is_array; /* true, when a value is assigned via bind_array */
Oid typelemid; /* Oid of element of a array */
bool typelembyval;
int16 typelemlen;
int index1;
int index2;
} VariableData;
/*
* Query result column definition
*/
typedef struct
{
int position;
Oid typoid;
bool typbyval;
int16 typlen;
int32 typmod;
bool typisstr;
Oid typarrayoid; /* oid of requested array output value */
uint64 rowcount; /* maximal rows of requested array */
int index1; /* output array should be rewrited from this index */
} ColumnData;
typedef enum
{
TOKEN_SPACES,
TOKEN_COMMENT,
TOKEN_NUMBER,
TOKEN_BIND_VAR,
TOKEN_STR,
TOKEN_EXT_STR,
TOKEN_DOLAR_STR,
TOKEN_IDENTIF,
TOKEN_QIDENTIF,
TOKEN_DOUBLE_COLON,
TOKEN_OTHER,
TOKEN_NONE
} ofTokenType;
/* from gms_sql.cpp */
extern "C" Datum gms_sql_is_open(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_open_cursor(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_close_cursor(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_parse(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_bind_variable(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_bind_variable_f(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_bind_array_3(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_bind_array_5(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_define_column(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_define_array(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_execute(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_fetch_rows(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_execute_and_fetch(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_column_value(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_column_value_f(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_last_row_count(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_describe_columns(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_describe_columns_f(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_debug_cursor(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_return_result(PG_FUNCTION_ARGS);
extern "C" Datum gms_sql_return_result_i(PG_FUNCTION_ARGS);
#endif

View File

@ -0,0 +1,315 @@
CREATE EXTENSION gms_sql;
set gms_sql_max_open_cursor_count = 501;
reset gms_sql_max_open_cursor_count;
show gms_sql_max_open_cursor_count;
do $$
declare
c int;
strval varchar;
intval int;
nrows int default 30;
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'select ''ahoj'' || i, i from generate_series(1, :nrows) g(i)', gms_sql.v6);
gms_sql.bind_variable(c, 'nrows', nrows);
gms_sql.define_column(c, 1, strval);
gms_sql.define_column(c, 2, intval);
perform gms_sql.execute(c);
while gms_sql.fetch_rows(c) > 0
loop
gms_sql.column_value(c, 1, strval);
gms_sql.column_value(c, 2, intval);
raise notice 'c1: %, c2: %', strval, intval;
end loop;
gms_sql.close_cursor(c);
end;
$$;
do $$
declare
c int;
strval varchar;
intval int;
nrows int default 30;
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'select ''ahoj'' || i, i from generate_series(1, :nrows) g(i)', gms_sql.v7);
gms_sql.bind_variable(c, 'nrows', nrows);
gms_sql.define_column(c, 1, strval);
gms_sql.define_column(c, 2, intval);
perform gms_sql.execute(c);
while gms_sql.fetch_rows(c) > 0
loop
strval := gms_sql.column_value_f(c, 1, strval);
intval := gms_sql.column_value_f(c, 2, intval);
raise notice 'c1: %, c2: %', strval, intval;
end loop;
gms_sql.close_cursor(c);
end;
$$;
drop table if exists foo;
create table foo(a int, b varchar, c numeric);
do $$
declare c int;
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'insert into foo values(:a, :b, :c)', gms_sql.native);
for i in 1..100
loop
gms_sql.bind_variable(c, 'a', i);
gms_sql.bind_variable(c, 'b', 'Ahoj ' || i);
gms_sql.bind_variable(c, 'c', i + 0.033);
perform gms_sql.execute(c);
end loop;
gms_sql.close_cursor(c);
end;
$$;
select * from foo;
truncate foo;
do $$
declare c int;
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'insert into foo values(:a, :b, :c)', gms_sql.native);
for i in 1..100
loop
gms_sql.bind_variable_f(c, 'a', i);
gms_sql.bind_variable_f(c, 'b', 'Ahoj ' || i);
gms_sql.bind_variable_f(c, 'c', i + 0.033);
perform gms_sql.execute(c);
end loop;
gms_sql.close_cursor(c);
end;
$$;
select * from foo;
truncate foo;
do $$
declare
c int;
a int[];
b varchar[];
ca numeric[];
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'insert into foo values(:a, :b, :c)', gms_sql.v6);
a := ARRAY[1, 2, 3, 4, 5];
b := ARRAY['Ahoj', 'Nazdar', 'Bazar'];
ca := ARRAY[3.14, 2.22, 3.8, 4];
perform gms_sql.bind_array(c, 'a', a);
perform gms_sql.bind_array(c, 'b', b);
perform gms_sql.bind_array(c, 'c', ca);
raise notice 'inserted rows %d', gms_sql.execute(c);
gms_sql.close_cursor(c);
end;
$$;
select * from foo;
truncate foo;
do $$
declare
c int;
a int[];
b varchar[];
ca numeric[];
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'insert into foo values(:a, :b, :c)', gms_sql.v7);
a := ARRAY[1, 2, 3, 4, 5];
b := ARRAY['Ahoj', 'Nazdar', 'Bazar'];
ca := ARRAY[3.14, 2.22, 3.8, 4];
perform gms_sql.bind_array(c, 'a', a, 2, 3);
perform gms_sql.bind_array(c, 'b', b, 3, 4);
perform gms_sql.bind_array(c, 'c', ca);
raise notice 'inserted rows %d', gms_sql.execute(c);
gms_sql.close_cursor(c);
end;
$$;
select * from foo;
truncate foo;
do $$
declare
c int;
a int[];
b varchar[];
ca numeric[];
begin
c := gms_sql.open_cursor();
gms_sql.parse(c, 'select i, ''Ahoj'' || i, i + 0.003 from generate_series(1, 35) g(i)', 0);
gms_sql.define_array(c, 1, a, 10, 1);
gms_sql.define_array(c, 2, b, 10, 1);
gms_sql.define_array(c, 3, ca, 10, 1);
perform gms_sql.execute(c);
while gms_sql.fetch_rows(c) > 0
loop
gms_sql.column_value(c, 1, a);
gms_sql.column_value(c, 2, b);
gms_sql.column_value(c, 3, ca);
raise notice 'a = %', a;
raise notice 'b = %', b;
raise notice 'c = %', ca;
end loop;
gms_sql.close_cursor(c);
end;
$$;
drop table foo;
do $$
declare
l_curid int;
l_cnt int;
l_desctab gms_sql.desc_tab;
l_sqltext varchar(2000);
begin
l_sqltext='select * from pg_object;';
l_curid := gms_sql.open_cursor();
gms_sql.parse(l_curid, l_sqltext, 0);
gms_sql.describe_columns(l_curid, l_cnt, l_desctab);
for i in 1 .. l_desctab.count loop
raise notice '%,% ', l_desctab(i).col_name,l_desctab(i).col_type;
end loop;
gms_sql.close_cursor(l_curid);
end;
$$;
create table t1(id int, name varchar(20));
insert into t1 select generate_series(1,3), 'abcddd';
create table t2(a int, b date);
insert into t2 values(1, '2022-12-11 10:00:01.123');
insert into t2 values(3, '2022-12-12 12:00:11.13');
do $$
declare
c1 refcursor;
c2 refcursor;
begin
open c1 for select * from t1;
gms_sql.return_result(c1);
open c2 for select * from t2;
gms_sql.return_result(c2);
end;
$$;
create procedure test_result() as
declare
c1 refcursor;
c2 refcursor;
begin
open c1 for select * from t1;
gms_sql.return_result(c1);
open c2 for select * from t2;
gms_sql.return_result(c2);
end;
/
call test_result();
drop procedure test_result;
create procedure aam() as
declare
id1 int;
id2 int;
begin
id1 :=gms_sql.open_cursor();
gms_sql.parse(id1,'select * from t1', 1);
perform gms_sql.execute(id1);
gms_sql.return_result(id1);
gms_sql.close_cursor(id1);
id2 :=gms_sql.open_cursor();
gms_sql.parse(id2,'select * from t2', 2);
perform gms_sql.execute(id2);
gms_sql.return_result(id2);
gms_sql.close_cursor(id2);
end;
/
call aam();
drop procedure aam;
create table col_name_too_long(aaaaabbbbbcccccdddddeeeeefffffggg int, col2 text);
do $$
declare
l_curid int;
l_cnt int;
l_desctab gms_sql.desc_tab;
l_desctab2 gms_sql.desc_tab2;
l_sqltext varchar(2000);
begin
l_sqltext='select * from t1;';
l_curid := gms_sql.open_cursor();
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns(l_curid, l_cnt, l_desctab);
for i in 1 .. l_desctab.count loop
raise notice '%', l_desctab(i).col_name;
end loop;
-- output col_name
l_sqltext='select * from col_name_too_long;';
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns2(l_curid, l_cnt, l_desctab2);
for i in 1 .. l_desctab2.count loop
raise notice '%', l_desctab2(i).col_name;
end loop;
-- error
l_sqltext='select * from col_name_too_long;';
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns(l_curid, l_cnt, l_desctab);
for i in 1 .. l_desctab.count loop
raise notice '%', l_desctab(i).col_name;
end loop;
end;
$$;
select gms_sql.is_open(0);
select gms_sql.close_cursor(0);
do $$
declare
l_curid int;
l_cnt int;
l_desctab3 gms_sql.desc_tab3;
l_desctab4 gms_sql.desc_tab4;
l_sqltext varchar(2000);
begin
l_sqltext='select * from col_name_too_long;';
l_curid := gms_sql.open_cursor();
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns3(l_curid, l_cnt, l_desctab3);
for i in 1 .. l_desctab3.count loop
raise notice '%,%,%', l_desctab3(i).col_type,l_desctab3(i).col_type_name,l_desctab3(i).col_name;
end loop;
gms_sql.parse(l_curid, l_sqltext, 1);
gms_sql.describe_columns3(l_curid, l_cnt, l_desctab4);
for i in 1 .. l_desctab4.count loop
raise notice '%,%,%,%', l_desctab3(i).col_type,l_desctab4(i).col_type_name,l_desctab4(i).col_type_name_len,l_desctab4(i).col_name_len;
end loop;
gms_sql.close_cursor(l_curid);
end;
$$;
drop table t1,t2, col_name_too_long;
select gms_sql.open_cursor();
select gms_sql.is_open(0);
select gms_sql.open_cursor();
select gms_sql.is_open(1);
select gms_sql.open_cursor();
select gms_sql.is_open(2);
select gms_sql.open_cursor();
select gms_sql.is_open(3);
select gms_sql.close_cursor(0);
select gms_sql.close_cursor(1);
select gms_sql.close_cursor(2);
select gms_sql.close_cursor(3);
select gms_sql.is_open(3);
select gms_sql.close_cursor(10000);
select gms_sql.close_cursor(-1);

View File

@ -457,6 +457,7 @@ primary_slotname|string|0,0|NULL|NULL|
psort_work_mem|int|64,2147483647|kB|Several running sessions may be storing in the action column to sort the table at the same time. Therefore, the total memory usage may be several times than psort_work_mem.|
query_band|string|0,0|NULL|NULL|
query_dop|int|1,64|NULL|NULL|
gms_sql_max_open_cursor_count|int|10,500|NULL|NULL|
query_mem|int|0,2147483647|kB|Sets the memory to be reserved for a statement.|
query_max_mem|int|0,2147483647|kB|Sets the max memory to be reserved for a statement.|
quote_all_identifiers|bool|0,0|NULL|NULL|

View File

@ -17239,6 +17239,34 @@ CreateProcedureStmt:
$$ = (Node *)n;
}
| CREATE opt_or_replace definer_user PROCEDURE func_name_opt_arg proc_args
LANGUAGE ColId_or_Sconst AS func_as opt_createproc_opt_list
{
CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
int count = get_outarg_num($6);
n->isOraStyle = false;
n->isPrivate = false;
n->replace = $2;
n->definer = $3;
if (n->replace && NULL != n->definer) {
parser_yyerror("not support DEFINER function");
}
n->funcname = $5;
n->parameters = $6;
n->returnType = NULL;
if (0 == count)
{
n->returnType = makeTypeName("void");
n->returnType->typmods = NULL;
n->returnType->arrayBounds = NULL;
}
n->options = $11;
n->options = lappend(n->options, makeDefElem("language", (Node *)makeString($8)));
n->options = lappend(n->options, makeDefElem("as", (Node *)$10));
n->withClause = NIL;
n->isProcedure = true;
$$ = (Node *)n;
}
;
CreatePackageStmt:

View File

@ -331,6 +331,7 @@ const char* sync_guc_variable_namelist[] = {"work_mem",
"default_text_search_config",
"dynamic_library_path",
"gin_fuzzy_search_limit",
"gms_sql_max_open_cursor_count",
"local_preload_libraries",
"deadlock_timeout",
"array_nulls",
@ -2698,6 +2699,20 @@ static void InitConfigureNamesInt()
NULL,
NULL,
NULL},
{{"gms_sql_max_open_cursor_count",
PGC_USERSET,
NODE_ALL,
CLIENT_CONN_OTHER,
gettext_noop("Sets the maximum is open cursor num."),
NULL,
0},
&u_sess->attr.attr_common.maxOpenCursorCount,
100,
10,
500,
NULL,
NULL,
NULL},
/* Can't be set in postgresql.conf */
{{"server_version_num",
PGC_INTERNAL,

View File

@ -64,6 +64,7 @@ THR_LOCAL MemoryContext AlignMemoryContext = NULL;
static void MemoryContextStatsInternal(MemoryContext context, int level);
static void FreeMemoryContextList(List* context_list);
static void MemoryContextCallResetCallbacks(MemoryContext context);
#ifdef PGXC
void* allocTopCxt(size_t s);
@ -264,6 +265,7 @@ void std_MemoryContextReset(MemoryContext context)
/* Nothing to do if no pallocs since startup or last reset */
if (!context->isReset) {
RemoveMemoryContextInfo(context);
MemoryContextCallResetCallbacks(context);
(*context->methods->reset)(context);
context->isReset = true;
}
@ -335,6 +337,13 @@ static void MemoryContextDeleteInternal(MemoryContext context, bool parent_locke
MemoryContextCheck(context, context->session_id > 0);
#endif
/*
* It's not entirely clear whether 'tis better to do this before or after
* delinking the context; but an error in a callback will likely result in
* leaking the whole context (if it's not a root context) if we do it
* after, so let's do it before.
*/
MemoryContextCallResetCallbacks(context);
MemoryContext parent = context->parent;
@ -449,6 +458,52 @@ void std_MemoryContextDeleteChildren(MemoryContext context, List* context_list)
}
}
/*
* MemoryContextRegisterResetCallback
* Register a function to be called before next context reset/delete.
* Such callbacks will be called in reverse order of registration.
*
* The caller is responsible for allocating a MemoryContextCallback struct
* to hold the info about this callback request, and for filling in the
* "func" and "arg" fields in the struct to show what function to call with
* what argument. Typically the callback struct should be allocated within
* the specified context, since that means it will automatically be freed
* when no longer needed.
*
* There is no API for deregistering a callback once registered. If you
* want it to not do anything anymore, adjust the state pointed to by its
* "arg" to indicate that.
*/
void MemoryContextRegisterResetCallback(MemoryContext context,
MemoryContextCallback *cb)
{
AssertArg(MemoryContextIsValid(context));
/* Push onto head so this will be called before older registrants. */
cb->next = context->resetCbs;
context->resetCbs = cb;
/* Mark the context as non-reset (it probably is already). */
context->isReset = false;
}
/*
* MemoryContextCallResetCallbacks
* Internal function to call all registered callbacks for context.
*/
static void MemoryContextCallResetCallbacks(MemoryContext context)
{
MemoryContextCallback *cb;
/*
* We pop each callback from the list before calling. That way, if an
* error occurs inside the callback, we won't try to call it a second time
* in the likely event that we reset or delete the context later.
*/
while ((cb = context->resetCbs) != NULL) {
context->resetCbs = cb->next;
(*cb->func)(cb->arg);
}
}
/*
* std_MemoryContextResetAndDeleteChildren
* Release all space allocated within a context and delete all

View File

@ -1496,6 +1496,29 @@ void *SPI_palloc(Size size)
return pointer;
}
Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen)
{
MemoryContext old_ctx = NULL;
Datum result;
if (u_sess->SPI_cxt._curid + 1 == u_sess->SPI_cxt._connected) { /* connected */
if (u_sess->SPI_cxt._current != &(u_sess->SPI_cxt._stack[u_sess->SPI_cxt._curid + 1])) {
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("SPI stack corrupted when allocate, connected level: %d", u_sess->SPI_cxt._connected)));
}
old_ctx = MemoryContextSwitchTo(u_sess->SPI_cxt._current->savedcxt);
}
result = datumCopy(value, typByVal, typLen);
if (old_ctx) {
(void)MemoryContextSwitchTo(old_ctx);
}
return result;
}
void *SPI_repalloc(void *pointer, Size size)
{
/* No longer need to worry which context chunk was in... */

View File

@ -144,6 +144,8 @@ extern void SPI_pfree(void* pointer);
extern void SPI_freetuple(HeapTuple pointer);
extern void SPI_freetuptable(SPITupleTable* tuptable);
extern Datum SPI_datumTransfer(Datum value, bool typByVal, int typLen);
extern Portal SPI_cursor_open(const char* name, SPIPlanPtr plan, Datum* Values, const char* Nulls, bool read_only);
extern Portal SPI_cursor_open_with_args(const char* name, const char* src, int nargs, Oid* argtypes, Datum* Values,
const char* Nulls, bool read_only, int cursorOptions, parse_query_func parser = GetRawParser());

View File

@ -102,6 +102,7 @@ typedef struct knl_session_attr_common {
int tcp_keepalives_count;
int tcp_user_timeout;
int GinFuzzySearchLimit;
int maxOpenCursorCount;
int server_version_num;
int log_temp_files;
int transaction_sync_naptime;

View File

@ -28,6 +28,8 @@
#ifndef PALLOC_H
#define PALLOC_H
#ifndef FRONTEND_PARSER
#include <iostream>
#include <memory>
#include "postgres.h"
#include "c.h"
#include "nodes/nodes.h"
@ -99,6 +101,21 @@ typedef struct McxtOperationMethods {
void (*mcxt_check)(MemoryContext context, bool own_by_session);
#endif
} McxtOperationMethods;
/*
* A memory context can have callback functions registered on it. Any such
* function will be called once just before the context is next reset or
* deleted. The MemoryContextCallback struct describing such a callback
* typically would be allocated within the context itself, thereby avoiding
* any need to manage it explicitly (the reset/delete action will free it).
*/
typedef void (*MemoryContextCallbackFunction) (std::shared_ptr<void> arg);
typedef struct MemoryContextCallback {
MemoryContextCallbackFunction func; /* function to call */
std::shared_ptr<void> arg; /* argument to pass it */
struct MemoryContextCallback *next; /* next in list of callbacks */
} MemoryContextCallback;
typedef struct MemoryContextData {
NodeTag type; /* identifies exact kind of context */
@ -114,6 +131,7 @@ typedef struct MemoryContextData {
MemoryContext prevchild; /* previous child of same parent */
MemoryContext nextchild; /* next child of same parent */
char* name; /* context name (just for debugging) */
MemoryContextCallback *resetCbs; /* list of reset/delete callbacks */
pthread_rwlock_t lock; /* lock to protect members if the context is shared */
int level; /* context level */
uint64 session_id; /* session id of context owner */
@ -140,6 +158,7 @@ extern THR_LOCAL PGDLLIMPORT MemoryContext TopMemoryContext;
const uint64 BlkMagicNum = 0xDADADADADADADADA;
const uint32 PremagicNum = 0xBABABABA;
#endif
/*
* Flags for MemoryContextAllocExtended.
*/
@ -308,6 +327,10 @@ static inline MemoryContext MemoryContextSwitchTo(MemoryContext context)
extern MemoryContext MemoryContextSwitchTo(MemoryContext context);
#endif /* USE_INLINE && !FRONTEND */
/* Registration of memory context reset/delete callbacks */
extern void MemoryContextRegisterResetCallback(MemoryContext context,
MemoryContextCallback *cb);
/*
* These are like standard strdup() except the copied string is
* allocated in a context, not with malloc().