diff --git a/GNUmakefile.in b/GNUmakefile.in index fd5bbbd06..a998f2d8d 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -115,6 +115,7 @@ install: echo "SCWS is not installed, skipping chparser build."; \ fi \ fi + @if test -d contrib/gms_lob; then $(MAKE) -C contrib/gms_lob $@; fi +@echo "openGauss installation complete." endif endif diff --git a/build/script/aarch64_opengauss_list b/build/script/aarch64_opengauss_list index 69fb09dd0..346ee0554 100644 --- a/build/script/aarch64_opengauss_list +++ b/build/script/aarch64_opengauss_list @@ -123,6 +123,8 @@ ./share/postgresql/extension/dblink.control ./share/postgresql/extension/gms_output--1.0.sql ./share/postgresql/extension/gms_output.control +./share/postgresql/extension/gms_lob--1.0.sql +./share/postgresql/extension/gms_lob.control ./share/postgresql/extension/gms_stats--1.0.sql ./share/postgresql/extension/gms_stats.control ./share/postgresql/extension/gms_profiler--1.0.sql @@ -827,6 +829,7 @@ ./lib/postgresql/pgoutput.so ./lib/postgresql/assessment.so ./lib/postgresql/gms_output.so +./lib/postgresql/gms_lob.so ./lib/postgresql/gms_stats.so ./lib/postgresql/gms_profiler.so ./lib/libpljava.so diff --git a/build/script/opengauss_release_list_ubuntu_single b/build/script/opengauss_release_list_ubuntu_single index 00208c25c..b436eef1a 100644 --- a/build/script/opengauss_release_list_ubuntu_single +++ b/build/script/opengauss_release_list_ubuntu_single @@ -111,6 +111,8 @@ ./share/postgresql/extension/dblink.control ./share/postgresql/extension/gms_output--1.0.sql ./share/postgresql/extension/gms_output.control +./share/postgresql/extension/gms_lob--1.0.sql +./share/postgresql/extension/gms_lob.control ./share/postgresql/extension/gms_stats--1.0.sql ./share/postgresql/extension/gms_stats.control ./share/postgresql/extension/gms_profiler--1.0.sql @@ -797,6 +799,7 @@ ./lib/postgresql/postgres_fdw.so ./lib/postgresql/dblink.so ./lib/postgresql/gms_output.so +./lib/postgresql/gms_lob.so ./lib/postgresql/gms_stats.so ./lib/postgresql/gms_profiler.so ./lib/libpljava.so diff --git a/build/script/x86_64_opengauss_list b/build/script/x86_64_opengauss_list index b4982684f..50f8be714 100644 --- a/build/script/x86_64_opengauss_list +++ b/build/script/x86_64_opengauss_list @@ -123,6 +123,8 @@ ./share/postgresql/extension/dblink.control ./share/postgresql/extension/gms_output--1.0.sql ./share/postgresql/extension/gms_output.control +./share/postgresql/extension/gms_lob--1.0.sql +./share/postgresql/extension/gms_lob.control ./share/postgresql/extension/gms_stats--1.0.sql ./share/postgresql/extension/gms_stats.control ./share/postgresql/extension/gms_profiler--1.0.sql @@ -824,6 +826,7 @@ ./lib/postgresql/java/pljava.jar ./lib/postgresql/postgres_fdw.so ./lib/postgresql/dblink.so +./lib/postgresql/gms_lob.so ./lib/postgresql/gms_stats.so ./lib/postgresql/pgoutput.so ./lib/postgresql/assessment.so diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index ace80fcfe..3843da999 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -29,6 +29,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/chparser ${CMAKE_CURRENT_SOURCE_DIR}/gms_stats ${CMAKE_CURRENT_SOURCE_DIR}/gms_profiler + ${CMAKE_CURRENT_SOURCE_DIR}/gms_lob ) add_subdirectory(hstore) @@ -62,3 +63,5 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/chparser) add_subdirectory(chparser) endif() add_subdirectory(gms_profiler) + +add_subdirectory(gms_lob) diff --git a/contrib/gms_lob/CMakeLists.txt b/contrib/gms_lob/CMakeLists.txt new file mode 100644 index 000000000..ceab1fec4 --- /dev/null +++ b/contrib/gms_lob/CMakeLists.txt @@ -0,0 +1,21 @@ +#This is the main CMAKE for build all gms_stats. +# gms_stats +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} TGT_gms_lob_SRC) +set(TGT_gms_lob_INC + ${PROJECT_OPENGS_DIR}/contrib/gms_lob + ${PROJECT_OPENGS_DIR}/contrib +) + +set(gms_lob_DEF_OPTIONS ${MACRO_OPTIONS}) +set(gms_lob_COMPILE_OPTIONS ${OPTIMIZE_OPTIONS} ${OS_OPTIONS} ${PROTECT_OPTIONS} ${WARNING_OPTIONS} ${LIB_SECURE_OPTIONS} ${CHECK_OPTIONS}) +set(gms_lob_LINK_OPTIONS ${LIB_LINK_OPTIONS}) +add_shared_libtarget(gms_lob TGT_gms_lob_SRC TGT_gms_lob_INC "${gms_lob_DEF_OPTIONS}" "${gms_lob_COMPILE_OPTIONS}" "${gms_lob_LINK_OPTIONS}") +set_target_properties(gms_lob PROPERTIES PREFIX "") + +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gms_lob.control + DESTINATION share/postgresql/extension/ +) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gms_lob--1.0.sql + DESTINATION share/postgresql/extension/ +) +install(TARGETS gms_lob DESTINATION lib/postgresql) diff --git a/contrib/gms_lob/Makefile b/contrib/gms_lob/Makefile new file mode 100644 index 000000000..d0aa453c3 --- /dev/null +++ b/contrib/gms_lob/Makefile @@ -0,0 +1,25 @@ +# contrib/gms_lob/Makefile +MODULE_big = gms_lob +OBJS = gms_lob.o + +EXTENSION = gms_lob +DATA = gms_lob--1.0.sql + +REGRESS = gms_lob + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/gms_lob +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_lob.o: gms_lob.cpp diff --git a/contrib/gms_lob/data/dummy.txt b/contrib/gms_lob/data/dummy.txt new file mode 100644 index 000000000..8e09a4f6c --- /dev/null +++ b/contrib/gms_lob/data/dummy.txt @@ -0,0 +1 @@ +The openGauss regression needs this file to run. diff --git a/contrib/gms_lob/expected/gms_lob.out b/contrib/gms_lob/expected/gms_lob.out new file mode 100644 index 000000000..c35921e54 --- /dev/null +++ b/contrib/gms_lob/expected/gms_lob.out @@ -0,0 +1,1123 @@ +drop database if exists testlob; +NOTICE: database "testlob" does not exist, skipping +create database testlob; +\c testlob +create extension gms_lob; +create extension gms_output; +select gms_output.enable(4000); + enable +-------- + +(1 row) + +create or replace function cast_to_raw(strdata varchar2) returns raw +as 'select encode(cast($1 as bytea), ''hex'')::raw;'LANGUAGE SQL; +--测试blob类型长度 +CREATE TABLE testblob(id INT, b BLOB); +--cast_to_raw +INSERT INTO testblob VALUES(1, cast_to_raw('Blob')); +INSERT INTO testblob VALUES(2, cast_to_raw('中文测试')); +INSERT INTO testblob VALUES(3, cast_to_raw('')); +INSERT INTO testblob VALUES(4, cast_to_raw('test +test')); +SELECT id, gms_lob.getlength(b) FROM testblob; + id | getlength +----+----------- + 1 | 4 + 2 | 12 + 3 | + 4 | 9 +(4 rows) + +DROP TABLE testblob; +--测试clob类型的长度 +CREATE TABLE testclob(id INT, b CLOB); +INSERT INTO testclob VALUES(1, ('Blob')); +INSERT INTO testclob VALUES(2, ('中文测试')); +INSERT INTO testclob VALUES(3, ('')); +INSERT INTO testclob VALUES(4, ('test +test')); +SELECT *,gms_lob.getlength(b) FROM testclob; + id | b | getlength +----+----------+----------- + 1 | Blob | 4 + 2 | 中文测试 | 4 + 3 | | + 4 | test +| 9 + | test | +(4 rows) + +DROP TABLE testclob; +--在函数中调用 +CREATE OR REPLACE FUNCTION fun_blob() RETURNS INTEGER LANGUAGE plpgsql AS $$ +DECLARE + lob_object BLOB := cast_to_raw('中文1'); + offset INTEGER; +BEGIN + RETURN gms_lob.getlength(lob_object); +END; +$$; +SELECT fun_blob(); + fun_blob +---------- + 7 +(1 row) + +CREATE OR REPLACE FUNCTION fun_clob() RETURNS INTEGER LANGUAGE plpgsql AS $$ +DECLARE + lob_object CLOB := ('中文1'); + offset INTEGER; +BEGIN + RETURN gms_lob.getlength(lob_object); +END; +$$; +SELECT fun_clob(); + fun_clob +---------- + 3 +(1 row) + +CREATE OR REPLACE FUNCTION fun_null() RETURNS INTEGER LANGUAGE plpgsql AS $$ +DECLARE + lob_object CLOB; + offset INTEGER; +BEGIN + RETURN gms_lob.getlength(lob_object); +END; +$$; +SELECT fun_null(); + fun_null +---------- + +(1 row) + +DROP FUNCTION fun_blob; +DROP FUNCTION fun_clob; +DROP FUNCTION fun_null; +--测试输入为空的场景 +SELECT gms_lob.getlength(); + getlength +----------- + +(1 row) + +create table tbl_testlob(id int, c_lob clob, b_lob blob); +insert into tbl_testlob values(1, 'clob', cast_to_raw('blob')); +insert into tbl_testlob values(2, '中文clobobject测试', cast_to_raw('中文blobobject测试')); +create or replace function func_clob() returns void +AS $$ +DECLARE + v_clob1 clob; + v_clob2 clob; + v_clob3 clob; + len1 int; + len3 int; +BEGIN + select c_lob into v_clob1 from tbl_testlob where id = 1; + gms_lob.open(v_clob1, gms_lob.LOB_READWRITE); + gms_lob.append(v_clob1, ' test'); + len1 := gms_lob.getlength(v_clob1); + gms_output.put_line('clob2:' || v_clob2); + gms_lob.read(v_clob1, len1, 1, v_clob2); + gms_output.put_line('clob1:' || v_clob1); + gms_output.put_line('clob2:' || v_clob2); + + select c_lob into v_clob3 from tbl_testlob where id = 2; + len3 := gms_lob.getlength(v_clob3); + + gms_output.put_line('clob3:' || v_clob3); + --不调用open函数。默认权限为读写 + gms_lob.write(v_clob3, len1, len3, v_clob1); + gms_output.put_line('clob3:' || v_clob3); + + gms_lob.close(v_clob1); + gms_lob.freetemporary(v_clob2); +END; +$$LANGUAGE plpgsql; +create or replace function func_blob() returns void +AS $$ +DECLARE + v_blob1 blob; + v_blob2 blob; + v_blob3 blob; + len1 int; + len3 int; +BEGIN + select b_lob into v_blob1 from tbl_testlob where id = 1; + gms_lob.open(v_blob1, gms_lob.LOB_READWRITE); + + len1 := gms_lob.getlength(v_blob1); + gms_output.put_line('blob1:' || v_blob1::text); + gms_output.put_line('blob2:' || v_blob2::text); + gms_lob.read(v_blob1, len1, 1, v_blob2); + gms_output.put_line('blob1:' || v_blob1::text); + gms_output.put_line('blob2:' || v_blob2::text); + + select b_lob into v_blob3 from tbl_testlob where id = 2; + len3 := gms_lob.getlength(v_blob3); + --不调用open函数。默认权限为读写 + gms_output.put_line('blob3:' || v_blob3::text); + gms_lob.write(v_blob3, len1, len3, v_blob1); + gms_output.put_line('blob3:' || v_blob3::text); + + gms_lob.close(v_blob1); + gms_lob.freetemporary(v_blob2); +END; +$$LANGUAGE plpgsql; +select func_clob(); +clob2: +clob1:clob test +clob2:clob test +clob3:中文clobobject测试 +clob3:中文clobobject测clob test + func_clob +----------- + +(1 row) + +select func_blob(); +blob1:626C6F62 +blob2: +blob1:626C6F62 +blob2:626C6F62 +blob3:E4B8ADE69687626C6F626F626A656374E6B58BE8AF95 +blob3:E4B8ADE69687626C6F626F626A656374E6B58BE8AF626C6F62 + func_blob +----------- + +(1 row) + +----------open函数----------- +--(1)打开无效的lob +DECLARE + v_clob clob; +BEGIN + gms_lob.open(v_clob, gms_lob.LOB_READWRITE); + gms_lob.close(v_clob); +END; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.open(v_clob,gms_lob.LOB_READWRITE)" +PL/pgSQL function inline_code_block line 3 at SQL statement +--(2)open_mode为数值 +DECLARE + v_clob clob; +BEGIN + gms_lob.createtemporary(v_clob, false, 10); + gms_lob.open(v_clob, 1); + gms_lob.close(v_clob); + gms_lob.freetemporary(v_clob); +END; +/ +--(3)open_mode为其他值 +DECLARE + v_clob clob; +BEGIN + gms_lob.createtemporary(v_clob, false); + gms_lob.open(v_clob, 100); + gms_lob.close(v_clob); + gms_lob.freetemporary(v_clob); +END; +/ +ERROR: invalid open_mode +CONTEXT: SQL statement "CALL gms_lob.open(v_clob,100)" +PL/pgSQL function inline_code_block line 4 at SQL statement +--重复打开 +DECLARE + v_clob clob; +BEGIN + gms_lob.createtemporary(v_clob, false); + gms_lob.open(v_clob, gms_lob.LOB_READONLY); + gms_lob.open(v_clob, gms_lob.LOB_READWRITE); + gms_lob.close(v_clob); + gms_lob.freetemporary(v_clob); +END; +/ +NOTICE: Lob(v_clob) already opened in the same transaction +CONTEXT: SQL statement "CALL gms_lob.open(v_clob,gms_lob.LOB_READWRITE)" +PL/pgSQL function inline_code_block line 5 at SQL statement +-- 大写名称 +declare +"MYLOB" CLOB; +begin +gms_lob.createtemporary("MYLOB",true); +gms_lob.open("MYLOB",gms_lob.lob_readwrite); +"MYLOB":='foo'; +raise notice '%',"MYLOB"; +end; +/ +NOTICE: foo +----------isopen函数----------- +DECLARE + v_clob clob; +BEGIN + gms_lob.createtemporary(v_clob, false); + gms_lob.open(v_clob, gms_lob.LOB_READWRITE); + gms_output.put_line('isopen: ' || gms_lob.isopen(v_clob)); + gms_lob.close(v_clob); + gms_output.put_line('isopen: ' || gms_lob.isopen(v_clob)); + gms_lob.freetemporary(v_clob); +END; +/ +isopen: 1 +isopen: 0 +-----------freetemporary函数----------- +DECLARE + v_clob CLOB; + v_char VARCHAR2(100); +BEGIN + v_char := 'Chinese中国人'; + gms_lob.createtemporary(v_clob,TRUE,12); + gms_lob.append(v_clob,v_char); + gms_output.put_line(v_clob||' 字符长度:'||gms_lob.getlength(v_clob)); + gms_lob.freetemporary(v_clob); + gms_output.put_line(' 释放后再输出:'||v_clob); +END; +/ +Chinese中国人 字符长度:10 + 释放后再输出: +drop table tbl_testlob; +drop function func_clob; +drop function func_blob; +declare +lob1 clob := '123'; +lob2 clob := '456'; +lob3 clob := '789'; +begin +gms_lob.open (lob_loc => lob1,open_mode => 1); +gms_lob.open (lob_loc => lob2,open_mode => gms_lob.lob_readwrite); +gms_lob.open (lob_loc => lob3,open_mode => gms_lob.lob_readonly); +raise notice '%,%,%',lob1,lob2,lob3; +end; +/ +NOTICE: 123,456,789 +-- 支持GMS_LOB.WRITE/READ/APPEND函数 +-----------read函数----------- +--gms_lob.gms_lob_read_blob +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ +616263 +-- float +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=3.2; +off_set float :=2.1; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ +616263 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=3.8; +off_set float :=2.9; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ +62634546 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=3.8; +off_set float :=2.9; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ +43444546 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=8; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ +616263 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=20; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, b2); +gms_output.put_line(r1::text); +end; +/ +--VALUEERROR +--lob为空 +declare +b1 blob; +amount INTEGER :=3; +off_set INTEGER :=2; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.read(b1,amount,off_set,r1)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--amount为空 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER; +off_set INTEGER :=9; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ +ERROR: numeric or value error +CONTEXT: SQL statement "CALL gms_lob.read(b1,amount,off_set,r1)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--offset为空 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=3; +off_set INTEGER; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ +ERROR: numeric or value error +CONTEXT: SQL statement "CALL gms_lob.read(b1,amount,off_set,r1)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--lob无效 +declare +amount INTEGER :=3; +off_set INTEGER :=2; +r1 raw; +begin +gms_lob.read(cast_to_raw('111111'), amount, off_set, r1); +end; +/ +--amount大于buffer大小 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=15; +off_set INTEGER :=2; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ +--offset超出范围 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=3; +off_set INTEGER :=30; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ +ERROR: no data found +CONTEXT: SQL statement "CALL gms_lob.read(b1,amount,off_set,r1)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--lob只读 +declare +b1 blob :=cast_to_raw('1111'); +amount INTEGER :=2; +off_set INTEGER :=2; +r1 raw; +begin +gms_lob.open(b1, gms_lob.LOB_READONLY); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ +3131 +--gms_lob.gms_lob_read_clob +declare +c1 clob :='abcdefgh'; +amount INTEGER :=3; +off_set INTEGER :=1; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +clob read: abc +declare +c1 clob :='abcdefgh'; +amount INTEGER :=3; +off_set INTEGER :=9; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +ERROR: no data found +CONTEXT: SQL statement "CALL gms_lob.read(c1,amount,off_set,var_buf)" +PL/pgSQL function inline_code_block line 6 at SQL statement +declare +c1 clob :='abcdefgh'; +amount INTEGER :=10; +off_set INTEGER :=2; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +clob read: bcdefgh +--VALUEERROR +--lob为空 +declare +c1 clob; +amount INTEGER :=3; +off_set INTEGER :=2; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.read(c1,amount,off_set,var_buf)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--amount为空 +declare +c1 clob :='abcdefgh'; +amount INTEGER; +off_set INTEGER :=1; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +ERROR: numeric or value error +CONTEXT: SQL statement "CALL gms_lob.read(c1,amount,off_set,var_buf)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--offset为空 +declare +c1 clob :='abcdefgh'; +amount INTEGER :=3; +off_set INTEGER; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +ERROR: numeric or value error +CONTEXT: SQL statement "CALL gms_lob.read(c1,amount,off_set,var_buf)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--lob无效 +declare +c1 clob; +amount INTEGER :=3; +off_set INTEGER :=2; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.read(c1,amount,off_set,var_buf)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--amount大于buffer大小 +declare +c1 clob :='abcdefgh'; +amount INTEGER :=6; +off_set INTEGER :=2; +var_buf varchar2(3); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +ERROR: value too long for type character varying(3) +CONTEXT: PL/pgSQL function inline_code_block line 6 at SQL statement +--offset超出范围 +declare +c1 clob :='abcdefgh'; +amount INTEGER :=2; +off_set INTEGER :=0; +var_buf varchar2(3); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +ERROR: offset is invalid or out of range +CONTEXT: SQL statement "CALL gms_lob.read(c1,amount,off_set,var_buf)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--lob只读 +declare +c1 clob :='1111'; +amount INTEGER :=2; +off_set INTEGER :=2; +var_buf varchar2(10); +begin +gms_lob.open(c1, gms_lob.LOB_READONLY); +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ +clob read: 11 +-----------write函数----------- +--gms_lob.gms_lob_write_blob +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ +4161626345464748 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=8; +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ +41424344454647616263 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=20; +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ +41424344454647482020202020202020202020616263 +--buffer等于destlob +declare +b1 blob :=cast_to_raw('1234'); +amount INTEGER :=2; +off_set INTEGER :=2; +begin +gms_lob.write(b1, amount, off_set, b1); +gms_output.put_line(b1::text); +end; +/ +31313234 +--VALUEERROR +--lob为空 +declare +b1 blob; +amount INTEGER :=3; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.write(b1,amount,off_set,b2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--amount为空 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER; +off_set INTEGER :=9; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ +ERROR: Any of the input parameters are NULL +CONTEXT: SQL statement "CALL gms_lob.write(b1,amount,off_set,b2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--amount,offset向下取整 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=3.9; -- 3 +off_set float :=5.6; -- 5 +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ +4142434461626348 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=2.2; -- 2 +off_set float :=5.1; -- 5 +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ +4142434461624748 +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=4; +off_set INTEGER :=8; +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ +ERROR: amount is invalid or out of range +CONTEXT: SQL statement "CALL gms_lob.write(b1,amount,off_set,b2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--offset为空 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=3; +off_set INTEGER; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ +ERROR: Any of the input parameters are NULL +CONTEXT: SQL statement "CALL gms_lob.write(b1,amount,off_set,b2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--lob无效 +declare +amount INTEGER :=3; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(cast_to_raw('111111'), amount, off_set, b2); +end; +/ +ERROR: output parameter not a bind variable +CONTEXT: SQL statement "CALL gms_lob.write(cast_to_raw('111111'),amount,off_set,b2)" +PL/pgSQL function inline_code_block line 5 at SQL statement +--amount大于buffer大小 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=5; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ +ERROR: amount is invalid or out of range +CONTEXT: SQL statement "CALL gms_lob.write(b1,amount,off_set,b2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--offset超出范围 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=3; +off_set INTEGER :=0; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ +ERROR: Invalid argument value for dest_offset. +CONTEXT: SQL statement "CALL gms_lob.write(b1,amount,off_set,b2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--lob只读 +declare +b1 blob :=cast_to_raw('1111'); +amount INTEGER :=2; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.open(b1, gms_lob.LOB_READONLY); +gms_lob.write(b1, amount, off_set, b2); +end; +/ +ERROR: cannot update a LOB opened in read-only mode. +CONTEXT: SQL statement "CALL gms_lob.write(b1,amount,off_set,b2)" +PL/pgSQL function inline_code_block line 7 at SQL statement +--gms_lob.gms_lob_write_clob +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER :=1; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ +22211111 +-- amount, off_set 不向下取整 +declare +c1 clob :='11111111'; +amount INTEGER :=3.9; +off_set INTEGER :=1.8; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ +ERROR: amount is invalid or out of range +CONTEXT: SQL statement "CALL gms_lob.write(c1,amount,off_set,c2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER :=1.8; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ +12221111 +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER :=9; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ +11111111222 +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER :=10; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ +11111111 222 +--buffer等于destlob +declare +c1 clob :='1234'; +amount INTEGER :=2; +off_set INTEGER :=2; +begin +gms_lob.write(c1, amount, off_set, c1); +gms_output.put_line(c1::text); +end; +/ +1124 +--VALUEERROR +--lob为空 +declare +c1 clob; +amount INTEGER :=3; +off_set INTEGER :=2; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.write(c1,amount,off_set,c2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--amount为空 +declare +c1 clob :='11111111'; +amount INTEGER; +off_set INTEGER :=10; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ +ERROR: Any of the input parameters are NULL +CONTEXT: SQL statement "CALL gms_lob.write(c1,amount,off_set,c2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--offset为空 +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ +ERROR: Any of the input parameters are NULL +CONTEXT: SQL statement "CALL gms_lob.write(c1,amount,off_set,c2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--lob无效 +declare +amount INTEGER :=3; +off_set INTEGER :=2; +c2 clob :='222'; +begin +gms_lob.write('11111111', amount, off_set, c2); +end; +/ +ERROR: output parameter not a bind variable +CONTEXT: SQL statement "CALL gms_lob.write('11111111',amount,off_set,c2)" +PL/pgSQL function inline_code_block line 5 at SQL statement +--amount大于buffer大小 +declare +c1 clob :='11111111'; +amount INTEGER :=5; +off_set INTEGER :=2; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ +ERROR: amount is invalid or out of range +CONTEXT: SQL statement "CALL gms_lob.write(c1,amount,off_set,c2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--offset超出范围 +declare +c1 clob :='11111111'; +amount INTEGER :=2; +off_set INTEGER :=0; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ +ERROR: Invalid argument value for dest_offset. +CONTEXT: SQL statement "CALL gms_lob.write(c1,amount,off_set,c2)" +PL/pgSQL function inline_code_block line 6 at SQL statement +--lob只读 +declare +c1 clob :='1111'; +amount INTEGER :=2; +off_set INTEGER :=2; +c2 clob :='222'; +begin +gms_lob.open(c1, gms_lob.LOB_READONLY); +gms_lob.write(c1, amount, off_set, c2); +end; +/ +ERROR: cannot update a LOB opened in read-only mode. +CONTEXT: SQL statement "CALL gms_lob.write(c1,amount,off_set,c2)" +PL/pgSQL function inline_code_block line 7 at SQL statement +-----------append函数----------- +--gms_lob.gms_lob_append_blob +declare +b1 blob :=cast_to_raw('11111111'); +b2 blob :=cast_to_raw('222'); +begin +gms_lob.append(b1, b2); +gms_output.put_line(b1::text); +end; +/ +3131313131313131323232 +--VALUEERROR +--destlob为空 +declare +b1 blob; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.append(b1, b2); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.append(b1,b2)" +PL/pgSQL function inline_code_block line 4 at SQL statement +--srclob为空 +declare +b1 blob :=cast_to_raw('11111111'); +b2 blob; +begin +gms_lob.append(b1, b2); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.append(b1,b2)" +PL/pgSQL function inline_code_block line 4 at SQL statement +--lob无效 +declare +b2 blob :=cast_to_raw('222'); +begin +gms_lob.append(cast_to_raw('1111'), b2); +end; +/ +ERROR: output parameter not a bind variable +CONTEXT: SQL statement "CALL gms_lob.append(cast_to_raw('1111'),b2)" +PL/pgSQL function inline_code_block line 3 at SQL statement +--lob只读 +declare +b1 blob :=cast_to_raw('1111'); +b2 blob :=cast_to_raw('222'); +begin +gms_lob.open(b1, gms_lob.LOB_READONLY); +gms_lob.append(b1, b2); +end; +/ +ERROR: cannot update a LOB opened in read-only mode. +CONTEXT: SQL statement "CALL gms_lob.append(b1,b2)" +PL/pgSQL function inline_code_block line 5 at SQL statement +--gms_lob.gms_lob_append_clob +declare +c1 clob :='11111111'; +c2 clob :='222'; +begin +gms_lob.append(c1, c2); +gms_output.put_line(c1::text); +end; +/ +11111111222 +--VALUEERROR +--destlob为空 +declare +c1 clob; +c2 clob :='222'; +begin +gms_lob.append(c1, c2); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.append(c1,c2)" +PL/pgSQL function inline_code_block line 4 at SQL statement +--srclob为空 +declare +c1 clob :='11111111'; +c2 clob; +begin +gms_lob.append(c1, c2); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.append(c1,c2)" +PL/pgSQL function inline_code_block line 4 at SQL statement +--lob无效 +declare +c2 clob :='222'; +begin +gms_lob.append('1111', c2); +end; +/ +ERROR: output parameter not a bind variable +CONTEXT: SQL statement "CALL gms_lob.append('1111',c2)" +PL/pgSQL function inline_code_block line 3 at SQL statement +--lob只读 +declare +c1 clob :='1111'; +c2 clob :='222'; +begin +gms_lob.open(c1, gms_lob.LOB_READONLY); +gms_lob.append(c1, c2); +end; +/ +ERROR: cannot update a LOB opened in read-only mode. +CONTEXT: SQL statement "CALL gms_lob.append(c1,c2)" +PL/pgSQL function inline_code_block line 5 at SQL statement +declare +b1 blob :=cast_to_raw('ABC123'); +amount INTEGER :=3; +off_set INTEGER :=100; +b2 blob :=cast_to_raw('abc'); +c1 int; +BEGIN +gms_lob.write(b1,amount,off_set,b2); +gms_output.put_line(b1::text); +c1:=gms_lob.getlength(b1); +gms_output.put_line(c1); +end; +/ +414243313233202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020616263 +102 +declare +c1 clob :='ABC123'; +amount INTEGER :=3; +off_set INTEGER :=100; +c2 clob :='abc'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ +ABC123 abc +declare +b1 blob :=null; +b2 blob :=cast_to_raw('abc'); +BEGIN +gms_lob.append(b1,b2); +gms_output.put_line(b1::text); +end; +/ +ERROR: invalid LOB object specified +CONTEXT: SQL statement "CALL gms_lob.append(b1,b2)" +PL/pgSQL function inline_code_block line 4 at SQL statement +drop table if exists lob_mvcc; +NOTICE: table "lob_mvcc" does not exist, skipping +create table lob_mvcc(id int, c_lob clob, b_lob blob); +insert into lob_mvcc values(1, 'clob', cast_to_raw('blob')); +insert into lob_mvcc values(2, '中文clobobject测试', cast_to_raw('中文blobobject测试')); +select * from lob_mvcc order by id; + id | c_lob | b_lob +----+--------------------+---------------------------------------------- + 1 | clob | 626C6F62 + 2 | 中文clobobject测试 | E4B8ADE69687626C6F626F626A656374E6B58BE8AF95 +(2 rows) + +declare +c1 clob; +c2 clob :='222test'; +begin +select c_lob into c1 from lob_mvcc where id=1 for update; +gms_lob.append(c1, c2); +end; +/ +select * from lob_mvcc order by id; + id | c_lob | b_lob +----+--------------------+---------------------------------------------- + 1 | clob | 626C6F62 + 2 | 中文clobobject测试 | E4B8ADE69687626C6F626F626A656374E6B58BE8AF95 +(2 rows) + +drop table lob_mvcc; +create or replace procedure proc_1034970 +as +b1 clob :='测试'; +b2 clob :='测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试'; +begin +gms_lob.open(b1,gms_lob.lob_readwrite); +for i in 1..100 loop +gms_lob.append(b1,b2); +end loop; +end; +/ +call proc_1034970(); + proc_1034970 +-------------- + +(1 row) + +drop procedure proc_1034970; +\c contrib_regression +drop database if exists testlob; diff --git a/contrib/gms_lob/gms_lob--1.0.sql b/contrib/gms_lob/gms_lob--1.0.sql new file mode 100644 index 000000000..6cb703e85 --- /dev/null +++ b/contrib/gms_lob/gms_lob--1.0.sql @@ -0,0 +1,139 @@ +/* contrib/gms_lob/gms_lob--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION gms_lob" to load this file. \quit +create schema gms_lob; +GRANT USAGE ON SCHEMA gms_lob TO PUBLIC; + +-- GMS_LOB Constants - Basic +CREATE OR REPLACE FUNCTION gms_lob."CALL"() returns int +as $$ + begin + return 12; + end; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION gms_lob.FILE_READONLY() returns int +as $$ + begin + return 0; + end; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION gms_lob.LOB_READONLY() returns BINARY_INTEGER +as $$ + begin + return 0; + end; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION gms_lob.LOB_READWRITE() returns BINARY_INTEGER +as $$ + begin + return 1; + end; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION gms_lob.LOBMAXSIZE() returns numeric +as $$ + begin + return 18446744073709551615; + end; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION gms_lob.SESSION() returns INTEGER +as $$ + begin + return 10; + end; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION gms_lob.createtemporary(INOUT lob_loc BLOB, cache boolean, dur INTEGER DEFAULT 10, lobname text DEFAULT ':') +RETURNS BLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_createtemporary' +LANGUAGE C IMMUTABLE NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.createtemporary(INOUT lob_loc CLOB, cache boolean, dur INTEGER DEFAULT 10, lobname text DEFAULT ':') +RETURNS CLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_createtemporary' +LANGUAGE C IMMUTABLE NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.freetemporary(INOUT lob_loc BLOB, lobname text DEFAULT ':') +RETURNS BLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_freetemporary' +LANGUAGE C STRICT NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.freetemporary(INOUT lob_loc CLOB, lobname text DEFAULT ':') +RETURNS CLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_freetemporary' +LANGUAGE C STRICT NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.read(lob_loc BLOB, INOUT amount INTEGER, "offset" bigint, INOUT buffer raw, lobname text DEFAULT ':') +RETURNS record +AS 'MODULE_PATHNAME', 'gms_lob_og_read_blob' +LANGUAGE C NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.read(lob_loc CLOB, INOUT amount INTEGER, "offset" bigint, INOUT buffer varchar, lobname text DEFAULT ':') +RETURNS record +AS 'MODULE_PATHNAME', 'gms_lob_og_read_clob' +LANGUAGE C NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.write(INOUT lob_loc BLOB, amount numeric, "offset" numeric, buffer raw, lobname text DEFAULT ':') +RETURNS BLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_write_blob' +LANGUAGE C IMMUTABLE NOT FENCED; +CREATE OR REPLACE FUNCTION gms_lob.write(INOUT lob_loc CLOB, amount numeric, "offset" numeric, buffer varchar, lobname text DEFAULT ':') +RETURNS CLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_write_clob' +LANGUAGE C IMMUTABLE NOT FENCED; + + +CREATE OR REPLACE FUNCTION gms_lob.isopen(lob_loc BLOB, lobname text DEFAULT ':') +RETURNS INTEGER +AS 'MODULE_PATHNAME', 'gms_lob_og_isopen' +LANGUAGE C STRICT NOT FENCED; +CREATE OR REPLACE FUNCTION gms_lob.isopen(lob_loc CLOB, lobname text DEFAULT ':') +RETURNS INTEGER +AS 'MODULE_PATHNAME', 'gms_lob_og_isopen' +LANGUAGE C STRICT NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.open(INOUT lob_loc BLOB, open_mode INTEGER, lobname text DEFAULT ':') +RETURNS BLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_open' +LANGUAGE C NOT FENCED; +CREATE OR REPLACE FUNCTION gms_lob.open(INOUT lob_loc CLOB, open_mode INTEGER, lobname text DEFAULT ':') +RETURNS CLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_open' +LANGUAGE C NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.append(INOUT dest_lob BLOB, src_lob BLOB, lobname text DEFAULT ':') +RETURNS BLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_append_blob' +LANGUAGE C IMMUTABLE NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.append(INOUT dest_lob CLOB, src_lob CLOB, lobname text DEFAULT ':') +RETURNS CLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_append_clob' +LANGUAGE C IMMUTABLE NOT FENCED; +CREATE OR REPLACE FUNCTION gms_lob.close(INOUT lob_loc BLOB, lobname text DEFAULT ':') +RETURNS BLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_close' +LANGUAGE C STRICT NOT FENCED; +CREATE OR REPLACE FUNCTION gms_lob.close(INOUT lob_loc CLOB, lobname text DEFAULT ':') +RETURNS CLOB +AS 'MODULE_PATHNAME', 'gms_lob_og_close' +LANGUAGE C STRICT NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.getlength(lob_loc BLOB, lobname text DEFAULT ':') +RETURNS INTEGER +AS 'MODULE_PATHNAME', 'gms_lob_og_bloblength' +LANGUAGE C IMMUTABLE STRICT NOT FENCED; +CREATE OR REPLACE FUNCTION gms_lob.getlength(lob_loc CLOB, lobname text DEFAULT ':') +RETURNS INTEGER +AS 'MODULE_PATHNAME', 'gms_lob_og_cloblength' +LANGUAGE C IMMUTABLE STRICT NOT FENCED; + +CREATE OR REPLACE FUNCTION gms_lob.getlength(lobname text DEFAULT ':') +RETURNS void +AS 'MODULE_PATHNAME', 'gms_lob_og_null' +LANGUAGE C IMMUTABLE STRICT NOT FENCED; \ No newline at end of file diff --git a/contrib/gms_lob/gms_lob.control b/contrib/gms_lob/gms_lob.control new file mode 100644 index 000000000..79415c990 --- /dev/null +++ b/contrib/gms_lob/gms_lob.control @@ -0,0 +1,5 @@ +# gms_lob extension +comment = 'collection of stats data for PL/SQL applications' +default_version = '1.0' +module_pathname = '$libdir/gms_lob' +relocatable = true diff --git a/contrib/gms_lob/gms_lob.cpp b/contrib/gms_lob/gms_lob.cpp new file mode 100644 index 000000000..6736c0d38 --- /dev/null +++ b/contrib/gms_lob/gms_lob.cpp @@ -0,0 +1,899 @@ +/*------------------------------------------------------------------------------ + * gms_lob.cpp + * + * gms_lob内置包的实现 + * + * Copyright (c) 2002-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 2021, openGauss Contributors + * + * IDENTIFICATION + * contrib/gms_stats/gms_lob.cpp + * + *------------------------------------------------------------------------------ + */ +#include "postgres.h" + +#include "knl/knl_session.h" +#include "utils/memutils.h" +#include "catalog/pg_proc.h" +#include "c.h" +#include "miscadmin.h" +#include "access/xact.h" +#include "access/hash.h" +#include "utils/rel.h" +#include "utils/builtins.h" +#include "utils/syscache.h" +#include "access/genam.h" +#include "utils/lsyscache.h" +#include "executor/spi.h" +#include "lib/stringinfo.h" +#include "executor/executor.h" +#include "catalog/storage_gtt.h" +#include "utils/numeric.h" +#include "access/tuptoaster.h" + +#include "funcapi.h" +#include "fmgr.h" +#include "catalog/pg_directory.h" +#include "mb/pg_wchar.h" +#include "libpq/pqformat.h" +#include "storage/ipc.h" +#include "utils/acl.h" +#include "utils/bytea.h" +#include "libpq/be-fsstubs.h" +#include "libpq/libpq-fs.h" +#include "commands/extension.h" +#include "gms_lob.h" +#include "utils/palloc.h" + +PG_MODULE_MAGIC; + +#define NUMLOB 64 +static uint32 gmslob_index; + +PG_FUNCTION_INFO_V1(gms_lob_og_createtemporary); +PG_FUNCTION_INFO_V1(gms_lob_og_freetemporary); +PG_FUNCTION_INFO_V1(gms_lob_og_read_blob); +PG_FUNCTION_INFO_V1(gms_lob_og_read_clob); +PG_FUNCTION_INFO_V1(gms_lob_og_write_blob); +PG_FUNCTION_INFO_V1(gms_lob_og_write_clob); +PG_FUNCTION_INFO_V1(gms_lob_og_isopen); +PG_FUNCTION_INFO_V1(gms_lob_og_open); +PG_FUNCTION_INFO_V1(gms_lob_og_append_blob); +PG_FUNCTION_INFO_V1(gms_lob_og_append_clob); +PG_FUNCTION_INFO_V1(gms_lob_og_close); +PG_FUNCTION_INFO_V1(gms_lob_og_cloblength); +PG_FUNCTION_INFO_V1(gms_lob_og_bloblength); +PG_FUNCTION_INFO_V1(gms_lob_og_null); + +void init_session_vars(void) { + RepallocSessionVarsArrayIfNecessary(); + GmsLobContext* psc = + (GmsLobContext*)MemoryContextAllocZero(u_sess->self_mem_cxt, sizeof(GmsLobContext)); + u_sess->attr.attr_common.extension_session_vars_array[gmslob_index] = psc; + psc->gmsLobNameHash = NULL; +} + +GmsLobContext* get_session_context() { + if (u_sess->attr.attr_common.extension_session_vars_array[gmslob_index] == NULL) { + init_session_vars(); + } + return (GmsLobContext*)u_sess->attr.attr_common.extension_session_vars_array[gmslob_index]; +} + +static int32 getVarSize(varlena* var) +{ + if (VARATT_IS_HUGE_TOAST_POINTER(var)) { + struct varatt_lob_external large_toast_pointer; + + VARATT_EXTERNAL_GET_HUGE_POINTER(large_toast_pointer, var); + return large_toast_pointer.va_rawsize; + } else { + return VARSIZE_ANY_EXHDR(var); + } +} + +/* + * charlen_to_bytelen() + * Compute the number of bytes occupied by n characters starting at *p + * + * It is caller's responsibility that there actually are n characters; + * the string need not be null-terminated. + */ +static int charlen_to_bytelen(const char* p, int n) +{ + if (pg_database_encoding_max_length() == 1) { + /* Optimization for single-byte encodings */ + return n; + } else { + const char* s = NULL; + + for (s = p; n > 0; n--) + s += pg_mblen(s); + + return s - p; + } +} + +/* numeric 向下取整转成int4 */ +int32 numericFloorToInt4(Numeric num) +{ + Datum datumnum = DirectFunctionCall1(numeric_floor, NumericGetDatum(num)); + return DatumGetInt32(DirectFunctionCall1(numeric_int4, datumnum)); +} +/***************************************************************************** + * LOB哈希表相关 + *****************************************************************************/ +struct GmsLobHashKey { + char* keyvalue; + int length; +}; + +typedef struct LobHashEnt { + GmsLobHashKey key; /* hash表key值 */ + int open_mode; /* 打开方式 */ +} LobHashEnt; +/* + * lob数据的hash值生成函数 + */ +uint32 lob_hash(const void* key, Size kwysize) +{ + const GmsLobHashKey* hash_key = (const GmsLobHashKey*)key; + return DatumGetUInt32(hash_any((const unsigned char*)hash_key->keyvalue, hash_key->length)); +} +/* + * lob数据的hash比较函数 + */ +int lob_match(const void* key1, const void* key2, Size kwysize) +{ + const GmsLobHashKey* k1 = (const GmsLobHashKey*)key1; + const GmsLobHashKey* k2 = (const GmsLobHashKey*)key2; + + if (k1->length > k2->length) { + return 1; + } else if (k1->length < k2->length) { + return -1; + } + + return strncmp(k1->keyvalue, k2->keyvalue, k1->length); +} +/* + * 创建hash表 + */ +static HTAB* createlobHash() +{ + HASHCTL hash_ctl; + + hash_ctl.keysize = sizeof(GmsLobHashKey); + hash_ctl.entrysize = sizeof(LobHashEnt); + hash_ctl.hash = lob_hash; + hash_ctl.match = lob_match; + + return hash_create("Lob hash", NUMLOB, &hash_ctl, HASH_ELEM | HASH_FUNCTION | HASH_COMPARE); +} + +/* + * 程序退出时的清理函数 + * 作为回调函数添加到proc_exit()调用的函数列表中 + */ +static void gms_lob_og_lob_exit(int code, Datum arg) +{ + HASH_SEQ_STATUS scan; + LobHashEnt *hentry = NULL; + + if (get_session_context()->gmsLobNameHash == NULL) { + return; + } + + hash_seq_init(&scan, get_session_context()->gmsLobNameHash); + while ((hentry = (LobHashEnt *)hash_seq_search(&scan))) { + if (hentry->key.keyvalue) { + pfree(hentry->key.keyvalue); + } + } + /* 清理hash表 */ + hash_destroy(get_session_context()->gmsLobNameHash); + get_session_context()->gmsLobNameHash = NULL; +} + +char* generateLobKey(char* argname) +{ + char* key = NULL; + int keylen = 0; + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + + TransactionId currentTransactionId = GetCurrentTransactionId(); + // TransactionId采用16进制方式打印,逗号末尾结束符各占一个字节 + keylen = strlen(argname) + sizeof(TransactionId) * 2 + 1 + 1; + key = (char*)palloc0(keylen); + errno_t errorno = snprintf_s(key, keylen, keylen - 1, "%16x,%s", currentTransactionId, argname); + securec_check_ss(errorno, "\0", "\0"); + return key; +} + +int searchLob(char* argname, bool* found) +{ + LobHashEnt* hentry = NULL; + GmsLobHashKey hash_key; + if (get_session_context()->gmsLobNameHash) { + hash_key.keyvalue = generateLobKey(argname); + hash_key.length = strlen(hash_key.keyvalue) + 1; + hentry = (LobHashEnt*)hash_search(get_session_context()->gmsLobNameHash, &hash_key, HASH_FIND, NULL); + if (hentry) { + *found = true; + return hentry->open_mode; + } + + if (hash_key.keyvalue) { + pfree(hash_key.keyvalue); + } + } else { + get_session_context()->gmsLobNameHash = createlobHash(); + on_proc_exit(&gms_lob_og_lob_exit, PointerGetDatum(NULL)); + } + *found = false; + return -1; +} + +static void closeLob(char* argname) +{ + bool found; + LobHashEnt* hentry = NULL; + GmsLobHashKey hash_key; + + searchLob(argname, &found); + if (found) { + hash_key.keyvalue = generateLobKey(argname); + hash_key.length = strlen(hash_key.keyvalue) + 1; + hentry = (LobHashEnt*)hash_search(get_session_context()->gmsLobNameHash, &hash_key, HASH_REMOVE, NULL); + if (!hentry) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("undefined lob."))); + } + + if (hash_key.keyvalue) { + pfree(hash_key.keyvalue); + } + + if (hentry->key.keyvalue) { + pfree(hentry->key.keyvalue); + } + } +} + +static void openLob(char* argname, int openmode) +{ + bool found; + LobHashEnt* hentry = NULL; + GmsLobHashKey hash_key; + + hash_key.keyvalue = generateLobKey(argname); + hash_key.length = strlen(hash_key.keyvalue) + 1; + hentry = (LobHashEnt*)hash_search(get_session_context()->gmsLobNameHash, &hash_key, HASH_ENTER, &found); + if (found) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("duplicate lob(%s) openned", argname))); + } + + hentry->key.keyvalue = (char*)MemoryContextAlloc(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), hash_key.length); + int rc = memcpy_s(hentry->key.keyvalue, hash_key.length, hash_key.keyvalue, hash_key.length); + securec_check(rc, "\0", "\0"); + hentry->key.length = hash_key.length; + hentry->open_mode = openmode; + + if (hash_key.keyvalue) { + pfree(hash_key.keyvalue); + } +} + +/* + * GMS_LOB.CREATETEMPORARY ( + * lob_loc IN OUT BLOB/CLOB, --blob/clob对象 + * cache IN BOOLEAN, --是否将LOB读取到缓冲区(不生效) + * dur IN PLS_INTEGER := GMS_LOB.SESSION); --指定何时清除临时LOB(10/ SESSION:会话结束时;12/ CALL:调用结束时)(不生效 +) + */ +Datum gms_lob_og_createtemporary(PG_FUNCTION_ARGS) +{ + char* argname = text_to_cstring(PG_GETARG_TEXT_P(3)); + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + + varlena* res = NULL; + if (PG_ARGISNULL(0)) { + res = (varlena*)palloc(VARHDRSZ); + SET_VARSIZE(res, VARHDRSZ); + PG_RETURN_POINTER(res); + } + + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); +} + +/* + * GMS_LOB.FREETEMPORARY ( + * lob_loc IN OUT BLOB/CLOB); --blob/clob对象 + */ +Datum gms_lob_og_freetemporary(PG_FUNCTION_ARGS) +{ + char* argname = text_to_cstring(PG_GETARG_TEXT_P(1)); + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + /* lob如果没关闭,将其关闭 */ + closeLob(argname); + + /* 返回一个空值的varlena */ + varlena* res = (varlena*)palloc0(VARHDRSZ); + SET_VARSIZE(res, VARHDRSZ); + + PG_RETURN_DATUM(PointerGetDatum(res)); +} + +/* + * lob_loc IN BLOB, + * amount IN OUT NOCOPY BINARY_INTEGER, + * offset IN INTEGER, + * buffer INOUT RAW + */ +Datum gms_lob_og_read_blob(PG_FUNCTION_ARGS) +{ + if (PG_ARGISNULL(0)) + ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE),errmsg("invalid LOB object specified"))); + + if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("numeric or value error"))); + } + + bytea *src_lob = PG_GETARG_BYTEA_P(0); + int32 amount = PG_GETARG_INT32(1); + int32 offset = PG_GETARG_INT32(2); + + if (amount < 1 || amount > AMOUNT_MAX_SIZE) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("amount is invalid or out of range"))); + } + if (offset < 1 || offset > LOBMAXSIZE) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("offset is invalid or out of range"))); + } + + int32 srclen = VARSIZE(src_lob) - VARHDRSZ; + + if (offset > srclen) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("no data found"))); + } + + int32 srcamount = amount > (srclen - offset + 1) ? (srclen - offset + 1) : amount; //src实际复制的字节数 + bytea* res = NULL; + int rc = 0; + res = (bytea*)palloc0(srcamount + VARHDRSZ); + SET_VARSIZE(res, srcamount + VARHDRSZ); + rc = memcpy_s(VARDATA(res), srcamount, VARDATA(src_lob) + offset - 1, srcamount); + securec_check(rc, "\0", "\0"); + + TupleDesc tupdesc; + Datum result; + HeapTuple tuple; + Datum values[2]; + bool nulls[2] = { false, false }; + /* 构造返回结果集 */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { + elog(ERROR, "return type must be a row type"); + } + + /*拼接结果*/ + values[0] = Int32GetDatum(srcamount); + values[1] = PointerGetDatum(res); + tuple = heap_form_tuple(tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} + +/* + * lob_loc IN CLOB, + * amount IN OUT NOCOPY BINARY_INTEGER, + * offset IN INTEGER, + * buffer INOUT VARCHAR2 + */ +Datum gms_lob_og_read_clob(PG_FUNCTION_ARGS) +{ + if (PG_ARGISNULL(0)) + ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE),errmsg("invalid LOB object specified"))); + + if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("numeric or value error"))); + } + + text* src_lob = PG_GETARG_TEXT_P(0); + int32 amount = PG_GETARG_INT32(1); + int32 offset = PG_GETARG_INT32(2); + + if (amount < 1 || amount > AMOUNT_MAX_SIZE) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("amount is invalid or out of range"))); + } + + if (offset < 1 || offset > LOBMAXSIZE) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("offset is invalid or out of range"))); + } + + int32 srclen = DirectFunctionCall1(textlen, PointerGetDatum(src_lob)); + + if (offset > srclen) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("no data found"))); + } + + int32 srcamount = amount > (srclen - offset + 1) ? (srclen - offset + 1) : amount; //src实际复制的字符数 + int32 srcbytestart = charlen_to_bytelen(VARDATA(src_lob), offset - 1); //src复制起点 + int32 copybyte = charlen_to_bytelen(VARDATA(src_lob) + srcbytestart, srcamount); //src实际复制的字节数 + text* res = NULL; + int rc = 0; + + res = (text*)palloc0(copybyte + VARHDRSZ); + SET_VARSIZE(res, copybyte + VARHDRSZ); + rc = memcpy_s(VARDATA(res), copybyte, VARDATA(src_lob) + srcbytestart, copybyte); + securec_check(rc, "\0", "\0"); + + TupleDesc tupdesc; + Datum result; + HeapTuple tuple; + Datum values[2]; + bool nulls[2] = { false, false }; + /* 构造返回结果集 */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { + elog(ERROR, "return type must be a row type"); + } + + /*拼接结果*/ + values[0] = Int32GetDatum(srcamount); + values[1] = PointerGetDatum(res); + + tuple = heap_form_tuple(tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + PG_RETURN_DATUM(result); +} + +/* +* GMS_LOB.WRITE ( +* lob_loc IN OUT NOCOPY BLOB, --目标blob对象 +* amount IN INTEGER, --指定的传入字节数 +* offset IN INTEGER, --开始读取blob的字节数的偏移量(原点在1) +* buffer IN RAW); --用于写的输入缓冲区 +*/ +Datum gms_lob_og_write_blob(PG_FUNCTION_ARGS) +{ + char* argname = text_to_cstring(PG_GETARG_TEXT_P(4)); + /* check input args */ + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + if (PG_ARGISNULL(0)) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid LOB object specified"))); + } + if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Any of the input parameters are NULL"))); + } + + bytea* destlob = PG_GETARG_BYTEA_P(0); + int32 amount = numericFloorToInt4(PG_GETARG_NUMERIC(1)); + int32 offset = numericFloorToInt4(PG_GETARG_NUMERIC(2)); + bytea* buffer = PG_GETARG_BYTEA_P(3); + int32 bufsize = getVarSize(buffer); + + if (amount < 1 || amount > 32767 || amount > bufsize) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("amount is invalid or out of range"))); + } + if (offset < 1 || offset > LOBMAXSIZE) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Invalid argument value for dest_offset."))); + } + + /* check lob's write and read permissions */ + bool found; + int32 openmode = searchLob(argname, &found); + if (found && openmode == 0) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("cannot update a LOB opened in read-only mode."))); + } + + int32 destlen = getVarSize(destlob); + int destbytestart = (offset - 1) > destlen ? destlen : (offset - 1); + int32 spacecnt = (offset - 1) > destlen ? (offset - destlen - 1) : 0; + int32 appendbyte = amount; + int32 destbyteend = (offset - 1 + appendbyte) > destlen ? destlen : (offset - 1 + appendbyte); + int64 reslen = destbytestart + spacecnt + appendbyte + (destlen - destbyteend); + bytea* res = NULL; + char* resdata = NULL; + int rc = 0; + + if (reslen != destlen || buffer == destlob) { + /* dest is lack of space, or displaced copy when + * buffer is the same as destlob, to avoid errors + * in memcpy_s checking, reapply + */ + if (reslen > MAX_TOAST_CHUNK_SIZE - VARHDRSZ) { +#ifdef ENABLE_MULTIPLE_NODES + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support clob/blob type more than 1GB for distributed system"))); +#endif + } + res = (bytea*)palloc(reslen + VARHDRSZ); + SET_VARSIZE(res, reslen + VARHDRSZ); + } else { + res = destlob; + } + resdata = VARDATA(res); + if (destbytestart > 0 && res != destlob) { + rc = memcpy_s(resdata, destbytestart, VARDATA(destlob), destbytestart); + securec_check(rc, "\0", "\0"); + } + resdata += destbytestart; + if (spacecnt > 0) { + rc = memset_s(resdata, spacecnt, ' ', spacecnt); + securec_check(rc, "\0", "\0"); + } + resdata += spacecnt; + if (appendbyte > 0) { + rc = memcpy_s(resdata, appendbyte, VARDATA(buffer), appendbyte); + securec_check(rc, "\0", "\0"); + } + resdata += appendbyte; + if (destbyteend < destlen && res != destlob) { + rc = memcpy_s(resdata, destlen - destbyteend, VARDATA(destlob) + destbyteend, destlen - destbyteend); + securec_check(rc, "\0", "\0"); + } + PG_RETURN_BYTEA_P(res); +} + +/* +* GMS_LOB.WRITE ( +* lob_loc IN OUT NOCOPY CLOB CHARACTER SET ANY_CS, --目标clob对象 +* amount IN INTEGER, --指定传入的字符数 +* offset IN INTEGER, --开始读取blob的字符数的偏移量(原点在1) +* buffer IN VARCHAR2 CHARACTER SET lob_loc%CHARSET); --用于写入的缓冲区 +*/ +Datum gms_lob_og_write_clob(PG_FUNCTION_ARGS) +{ + char* argname = text_to_cstring(PG_GETARG_TEXT_P(4)); + /* check input args */ + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + if (PG_ARGISNULL(0)) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid LOB object specified"))); + } + if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Any of the input parameters are NULL"))); + } + + text* destlob = PG_GETARG_TEXT_P(0); + int32 amount = numericFloorToInt4(PG_GETARG_NUMERIC(1)); + int32 offset = numericFloorToInt4(PG_GETARG_NUMERIC(2)); + text* buffer = PG_GETARG_TEXT_P(3); + int32 bufsize = DirectFunctionCall1(textlen, (Datum)buffer); + + if (amount < 1 || amount > 32767 || amount > bufsize) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("amount is invalid or out of range"))); + } + if (offset < 1 || offset > LOBMAXSIZE) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("Invalid argument value for dest_offset."))); + } + + /* check lob's write and read permissions */ + bool found; + int32 openmode = searchLob(argname, &found); + if (found && openmode == 0) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("cannot update a LOB opened in read-only mode."))); + } + + int32 destlen = DirectFunctionCall1(textlen, (Datum)destlob); + int32 srcamount = amount; + int32 destbytestart = 0; + int32 spacecnt = 0; + int32 appendbyte = charlen_to_bytelen(VARDATA(buffer), srcamount); + int32 destamount = 0; + int32 destbyteend = 0; + int32 destbytecnt = VARSIZE(destlob) - VARHDRSZ; + int64 reslen = 0; + text* res = NULL; + char* resdata = NULL; + int rc = 0; + + if (destlen < offset - 1) { + destamount = destlen; + spacecnt = offset - 1 - destamount; + destbytestart = destbytecnt; + destbyteend = destbytecnt; + } else { + destamount = srcamount > (destlen - offset + 1) ? (destlen - offset + 1) : srcamount; + destbytestart = charlen_to_bytelen(VARDATA(destlob), offset - 1); + destbyteend = charlen_to_bytelen(VARDATA(destlob) + destbytestart, destamount) + destbytestart; + } + reslen = destbytestart + spacecnt + appendbyte + (destbytecnt - destbyteend); + + if (reslen != destbytecnt || buffer == destlob) { + /* dest is lack of space, or displaced copy when + * buffer is the same as destlob, to avoid errors + * in memcpy_s checking, reapply + */ + if (reslen > MAX_TOAST_CHUNK_SIZE - VARHDRSZ) { +#ifdef ENABLE_MULTIPLE_NODES + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support clob/blob type more than 1GB for distributed system"))); +#endif + } + res = (text*)palloc(reslen + VARHDRSZ); + SET_VARSIZE(res, reslen + VARHDRSZ); + } else { + res = destlob; + } + resdata = VARDATA(res); + if (destbytestart > 0 && res != destlob) { + rc = memcpy_s(resdata, destbytestart, VARDATA(destlob), destbytestart); + securec_check(rc, "\0", "\0"); + } + resdata += destbytestart; + if (spacecnt > 0) { + rc = memset_s(resdata, spacecnt, ' ', spacecnt); + securec_check(rc, "\0", "\0"); + } + resdata += spacecnt; + if (appendbyte > 0) { + rc = memcpy_s(resdata, appendbyte, VARDATA(buffer), appendbyte); + securec_check(rc, "\0", "\0"); + } + resdata += appendbyte; + if (destbyteend < destbytecnt && res != destlob) { + rc = memcpy_s(resdata, destbytecnt - destbyteend, VARDATA(destlob) + destbyteend, destbytecnt - destbyteend); + securec_check(rc, "\0", "\0"); + } + PG_RETURN_TEXT_P(res); +} + +/* + * GMS_LOB.ISOPEN ( + * lob_loc IN OUT BLOB/CLOB); --blob/clob对象 + */ +Datum gms_lob_og_isopen(PG_FUNCTION_ARGS) +{ + bool found; + char* argname = text_to_cstring(PG_GETARG_TEXT_P(1)); + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + searchLob(argname, &found); + if (found) { + PG_RETURN_UINT8(1); + } else { + PG_RETURN_UINT8(0); + } +} +/* + * GMS_LOB.OPEN ( + * lob_loc IN OUT BLOB/CLOB, + * open_mode IN BINARY_INTEGER); + */ +Datum gms_lob_og_open(PG_FUNCTION_ARGS) +{ + if PG_ARGISNULL(0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid LOB object specified"))); + } + bool found; + int openmode = PG_GETARG_INT32(1); + if (openmode != 0 && openmode != 1) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid open_mode"))); + } + char* argname = text_to_cstring(PG_GETARG_TEXT_P(2)); + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + + searchLob(argname, &found); + if (found) { + ereport(NOTICE, (errmsg("Lob(%s) already opened in the same transaction", argname))); + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); + } + openLob(argname, openmode); + + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); +} + +/* +GMS_LOB.APPEND ( + dest_lob IN OUT NOCOPY BLOB, --目标blob对象 + src_lob IN BLOB); --源blob对象 +*/ +Datum gms_lob_og_append_blob(PG_FUNCTION_ARGS) +{ + char* argname = text_to_cstring(PG_GETARG_TEXT_P(2)); + /* check input args */ + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid LOB object specified"))); + } + + /* check lob's write and read permissions */ + bool found; + int32 openmode = searchLob(argname, &found); + if (found && openmode == 0) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("cannot update a LOB opened in read-only mode."))); + } + + Datum destlob = PG_GETARG_DATUM(0); + Datum srclob = PG_GETARG_DATUM(1); + Datum result = DirectFunctionCall2(byteacat, destlob, srclob); + + PG_RETURN_BYTEA_P(DatumGetPointer(result)); +} + +/* +GMS_LOB.APPEND ( + dest_lob IN OUT NOCOPY CLOB CHARACTER SET ANY_CS, --目标clob对象 + src_lob IN CLOB CHARACTER SET dest_lob%CHARSET); --源clob对象 +*/ +Datum gms_lob_og_append_clob(PG_FUNCTION_ARGS) +{ + char* argname = text_to_cstring(PG_GETARG_TEXT_P(2)); + /* check input args */ + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid LOB object specified"))); + } + + /* check lob's write and read permissions */ + bool found; + int32 openmode = searchLob(argname, &found); + if (found && openmode == 0) { + closeLob(argname); + ereport(ERROR, + (errcode(ERRCODE_INVALID_OPERATION), + errmsg("cannot update a LOB opened in read-only mode."))); + } + + Datum destlob = PG_GETARG_DATUM(0); + Datum srclob = PG_GETARG_DATUM(1); + Datum result = DirectFunctionCall2(textcat, destlob, srclob); + + PG_RETURN_TEXT_P(DatumGetPointer(result)); +} + +/* + * GMS_LOB.CLOSE ( + * lob_loc IN OUT BLOB/CLOB); --blob/clob对象 + */ +Datum gms_lob_og_close(PG_FUNCTION_ARGS) +{ + char* argname = text_to_cstring(PG_GETARG_TEXT_P(1)); + if (strcmp(argname, ":") == 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("output parameter not a bind variable"))); + } + bool found; + searchLob(argname, &found); + if (!found) { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("cannot perform operation on an unopened file or LOB"))); + } + closeLob(argname); + + PG_RETURN_DATUM(PG_GETARG_DATUM(0)); +} + +/* + * GMS_LOB.getlength ( + * lob_loc IN OUT CLOB); --clob对象 + */ +Datum gms_lob_og_cloblength(PG_FUNCTION_ARGS) +{ + Datum data = PG_GETARG_DATUM(0); + int datalen = 0; + + datalen = DirectFunctionCall1(textlen, data); + PG_RETURN_INT32(datalen); +} +/* + * GMS_LOB.getlength ( + * lob_loc IN OUT BLOB); --blob对象 + */ +Datum gms_lob_og_bloblength(PG_FUNCTION_ARGS) +{ + bytea* data = PG_GETARG_BYTEA_P(0); + int datalen = 0; + + datalen = VARSIZE(data) - VARHDRSZ; + PG_RETURN_INT32(datalen); +} + +/* + * GMS_LOB.getlength (); --NULL对象 + */ +Datum gms_lob_og_null(PG_FUNCTION_ARGS) +{ + PG_RETURN_VOID(); +} \ No newline at end of file diff --git a/contrib/gms_lob/gms_lob.h b/contrib/gms_lob/gms_lob.h new file mode 100644 index 000000000..280da7d40 --- /dev/null +++ b/contrib/gms_lob/gms_lob.h @@ -0,0 +1,37 @@ +/*---------------------------------------------------------------------------------------* + * gms_lob.h + * + * Definition about gms_lob package. + * + * IDENTIFICATION + * contrib/gms_stats/gms_lob.h + * + * --------------------------------------------------------------------------------------- + */ +#ifndef GMS_LOB_H +#define GMS_LOB_H +/* blob/clob最大存储长度1G*/ +#define LOBMAXSIZE (int64)0x3fffffff +#define AMOUNT_MAX_SIZE 32767 +typedef struct GmsLobContext { + struct HTAB* gmsLobNameHash; +} GmsLobContext; + +extern "C" Datum gms_lob_og_createtemporary(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_freetemporary(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_read_blob(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_read_clob(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_write_blob(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_write_clob(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_isopen(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_open(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_append_blob(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_append_clob(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_close(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_cloblength(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_bloblength(PG_FUNCTION_ARGS); +extern "C" Datum gms_lob_og_null(PG_FUNCTION_ARGS); +extern "C" void set_extension_index(uint32 index); +extern "C" void init_session_vars(void); +extern "C" GmsLobContext* get_session_context(); +#endif diff --git a/contrib/gms_lob/sql/gms_lob.sql b/contrib/gms_lob/sql/gms_lob.sql new file mode 100644 index 000000000..55a57deb4 --- /dev/null +++ b/contrib/gms_lob/sql/gms_lob.sql @@ -0,0 +1,990 @@ +drop database if exists testlob; +create database testlob; +\c testlob +create extension gms_lob; +create extension gms_output; +select gms_output.enable(4000); +create or replace function cast_to_raw(strdata varchar2) returns raw +as 'select encode(cast($1 as bytea), ''hex'')::raw;'LANGUAGE SQL; + +--测试blob类型长度 +CREATE TABLE testblob(id INT, b BLOB); +--cast_to_raw +INSERT INTO testblob VALUES(1, cast_to_raw('Blob')); +INSERT INTO testblob VALUES(2, cast_to_raw('中文测试')); +INSERT INTO testblob VALUES(3, cast_to_raw('')); +INSERT INTO testblob VALUES(4, cast_to_raw('test +test')); + +SELECT id, gms_lob.getlength(b) FROM testblob; + +DROP TABLE testblob; + +--测试clob类型的长度 +CREATE TABLE testclob(id INT, b CLOB); +INSERT INTO testclob VALUES(1, ('Blob')); +INSERT INTO testclob VALUES(2, ('中文测试')); +INSERT INTO testclob VALUES(3, ('')); +INSERT INTO testclob VALUES(4, ('test +test')); + +SELECT *,gms_lob.getlength(b) FROM testclob; + +DROP TABLE testclob; + +--在函数中调用 +CREATE OR REPLACE FUNCTION fun_blob() RETURNS INTEGER LANGUAGE plpgsql AS $$ +DECLARE + lob_object BLOB := cast_to_raw('中文1'); + offset INTEGER; +BEGIN + RETURN gms_lob.getlength(lob_object); +END; +$$; + +SELECT fun_blob(); + +CREATE OR REPLACE FUNCTION fun_clob() RETURNS INTEGER LANGUAGE plpgsql AS $$ +DECLARE + lob_object CLOB := ('中文1'); + offset INTEGER; +BEGIN + RETURN gms_lob.getlength(lob_object); +END; +$$; + +SELECT fun_clob(); + +CREATE OR REPLACE FUNCTION fun_null() RETURNS INTEGER LANGUAGE plpgsql AS $$ +DECLARE + lob_object CLOB; + offset INTEGER; +BEGIN + RETURN gms_lob.getlength(lob_object); +END; +$$; + +SELECT fun_null(); + +DROP FUNCTION fun_blob; +DROP FUNCTION fun_clob; +DROP FUNCTION fun_null; + +--测试输入为空的场景 +SELECT gms_lob.getlength(); + +create table tbl_testlob(id int, c_lob clob, b_lob blob); +insert into tbl_testlob values(1, 'clob', cast_to_raw('blob')); +insert into tbl_testlob values(2, '中文clobobject测试', cast_to_raw('中文blobobject测试')); + +create or replace function func_clob() returns void +AS $$ +DECLARE + v_clob1 clob; + v_clob2 clob; + v_clob3 clob; + len1 int; + len3 int; +BEGIN + select c_lob into v_clob1 from tbl_testlob where id = 1; + gms_lob.open(v_clob1, gms_lob.LOB_READWRITE); + gms_lob.append(v_clob1, ' test'); + len1 := gms_lob.getlength(v_clob1); + gms_output.put_line('clob2:' || v_clob2); + gms_lob.read(v_clob1, len1, 1, v_clob2); + gms_output.put_line('clob1:' || v_clob1); + gms_output.put_line('clob2:' || v_clob2); + + select c_lob into v_clob3 from tbl_testlob where id = 2; + len3 := gms_lob.getlength(v_clob3); + + gms_output.put_line('clob3:' || v_clob3); + --不调用open函数。默认权限为读写 + gms_lob.write(v_clob3, len1, len3, v_clob1); + gms_output.put_line('clob3:' || v_clob3); + + gms_lob.close(v_clob1); + gms_lob.freetemporary(v_clob2); +END; +$$LANGUAGE plpgsql; + +create or replace function func_blob() returns void +AS $$ +DECLARE + v_blob1 blob; + v_blob2 blob; + v_blob3 blob; + len1 int; + len3 int; +BEGIN + select b_lob into v_blob1 from tbl_testlob where id = 1; + gms_lob.open(v_blob1, gms_lob.LOB_READWRITE); + + len1 := gms_lob.getlength(v_blob1); + gms_output.put_line('blob1:' || v_blob1::text); + gms_output.put_line('blob2:' || v_blob2::text); + gms_lob.read(v_blob1, len1, 1, v_blob2); + gms_output.put_line('blob1:' || v_blob1::text); + gms_output.put_line('blob2:' || v_blob2::text); + + select b_lob into v_blob3 from tbl_testlob where id = 2; + len3 := gms_lob.getlength(v_blob3); + --不调用open函数。默认权限为读写 + gms_output.put_line('blob3:' || v_blob3::text); + gms_lob.write(v_blob3, len1, len3, v_blob1); + gms_output.put_line('blob3:' || v_blob3::text); + + gms_lob.close(v_blob1); + gms_lob.freetemporary(v_blob2); +END; +$$LANGUAGE plpgsql; + +select func_clob(); +select func_blob(); + +----------open函数----------- +--(1)打开无效的lob +DECLARE + v_clob clob; +BEGIN + gms_lob.open(v_clob, gms_lob.LOB_READWRITE); + gms_lob.close(v_clob); +END; +/ +--(2)open_mode为数值 +DECLARE + v_clob clob; +BEGIN + gms_lob.createtemporary(v_clob, false, 10); + gms_lob.open(v_clob, 1); + gms_lob.close(v_clob); + gms_lob.freetemporary(v_clob); +END; +/ +--(3)open_mode为其他值 +DECLARE + v_clob clob; +BEGIN + gms_lob.createtemporary(v_clob, false); + gms_lob.open(v_clob, 100); + gms_lob.close(v_clob); + gms_lob.freetemporary(v_clob); +END; +/ + +--重复打开 +DECLARE + v_clob clob; +BEGIN + gms_lob.createtemporary(v_clob, false); + gms_lob.open(v_clob, gms_lob.LOB_READONLY); + gms_lob.open(v_clob, gms_lob.LOB_READWRITE); + gms_lob.close(v_clob); + gms_lob.freetemporary(v_clob); +END; +/ + +-- 大写名称 +declare +"MYLOB" CLOB; +begin +gms_lob.createtemporary("MYLOB",true); +gms_lob.open("MYLOB",gms_lob.lob_readwrite); +"MYLOB":='foo'; +raise notice '%',"MYLOB"; +end; +/ + +----------isopen函数----------- +DECLARE + v_clob clob; +BEGIN + gms_lob.createtemporary(v_clob, false); + gms_lob.open(v_clob, gms_lob.LOB_READWRITE); + gms_output.put_line('isopen: ' || gms_lob.isopen(v_clob)); + gms_lob.close(v_clob); + gms_output.put_line('isopen: ' || gms_lob.isopen(v_clob)); + gms_lob.freetemporary(v_clob); +END; +/ + +-----------freetemporary函数----------- +DECLARE + v_clob CLOB; + v_char VARCHAR2(100); +BEGIN + v_char := 'Chinese中国人'; + gms_lob.createtemporary(v_clob,TRUE,12); + gms_lob.append(v_clob,v_char); + gms_output.put_line(v_clob||' 字符长度:'||gms_lob.getlength(v_clob)); + gms_lob.freetemporary(v_clob); + gms_output.put_line(' 释放后再输出:'||v_clob); +END; +/ + +drop table tbl_testlob; +drop function func_clob; +drop function func_blob; + +declare +lob1 clob := '123'; +lob2 clob := '456'; +lob3 clob := '789'; +begin +gms_lob.open (lob_loc => lob1,open_mode => 1); +gms_lob.open (lob_loc => lob2,open_mode => gms_lob.lob_readwrite); +gms_lob.open (lob_loc => lob3,open_mode => gms_lob.lob_readonly); +raise notice '%,%,%',lob1,lob2,lob3; +end; +/ + +-- 支持GMS_LOB.WRITE/READ/APPEND函数 + +-----------read函数----------- +--gms_lob.gms_lob_read_blob +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ + +-- float +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=3.2; +off_set float :=2.1; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=3.8; +off_set float :=2.9; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=3.8; +off_set float :=2.9; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=8; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=20; +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_lob.read(b1, amount, off_set, b2); +gms_output.put_line(r1::text); +end; +/ + +--VALUEERROR +--lob为空 +declare +b1 blob; +amount INTEGER :=3; +off_set INTEGER :=2; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ + +--amount为空 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER; +off_set INTEGER :=9; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ + +--offset为空 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=3; +off_set INTEGER; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ + +--lob无效 +declare +amount INTEGER :=3; +off_set INTEGER :=2; +r1 raw; +begin +gms_lob.read(cast_to_raw('111111'), amount, off_set, r1); +end; +/ + +--amount大于buffer大小 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=15; +off_set INTEGER :=2; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ + +--offset超出范围 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=3; +off_set INTEGER :=30; +r1 raw; +begin +gms_lob.read(b1, amount, off_set, r1); +end; +/ + +--lob只读 +declare +b1 blob :=cast_to_raw('1111'); +amount INTEGER :=2; +off_set INTEGER :=2; +r1 raw; +begin +gms_lob.open(b1, gms_lob.LOB_READONLY); +gms_lob.read(b1, amount, off_set, r1); +gms_output.put_line(r1::text); +end; +/ + +--gms_lob.gms_lob_read_clob +declare +c1 clob :='abcdefgh'; +amount INTEGER :=3; +off_set INTEGER :=1; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +declare +c1 clob :='abcdefgh'; +amount INTEGER :=3; +off_set INTEGER :=9; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +declare +c1 clob :='abcdefgh'; +amount INTEGER :=10; +off_set INTEGER :=2; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +--VALUEERROR +--lob为空 +declare +c1 clob; +amount INTEGER :=3; +off_set INTEGER :=2; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +--amount为空 +declare +c1 clob :='abcdefgh'; +amount INTEGER; +off_set INTEGER :=1; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +--offset为空 +declare +c1 clob :='abcdefgh'; +amount INTEGER :=3; +off_set INTEGER; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +--lob无效 +declare +c1 clob; +amount INTEGER :=3; +off_set INTEGER :=2; +var_buf varchar2(10); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +--amount大于buffer大小 +declare +c1 clob :='abcdefgh'; +amount INTEGER :=6; +off_set INTEGER :=2; +var_buf varchar2(3); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +--offset超出范围 +declare +c1 clob :='abcdefgh'; +amount INTEGER :=2; +off_set INTEGER :=0; +var_buf varchar2(3); +begin +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +--lob只读 +declare +c1 clob :='1111'; +amount INTEGER :=2; +off_set INTEGER :=2; +var_buf varchar2(10); +begin +gms_lob.open(c1, gms_lob.LOB_READONLY); +gms_lob.read(c1, amount, off_set, var_buf); +gms_output.put_line('clob read: ' || var_buf::text); +end; +/ + +-----------write函数----------- +--gms_lob.gms_lob_write_blob +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=8; +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=3; +off_set INTEGER :=20; +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ + +--buffer等于destlob +declare +b1 blob :=cast_to_raw('1234'); +amount INTEGER :=2; +off_set INTEGER :=2; +begin +gms_lob.write(b1, amount, off_set, b1); +gms_output.put_line(b1::text); +end; +/ + +--VALUEERROR +--lob为空 +declare +b1 blob; +amount INTEGER :=3; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ + +--amount为空 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER; +off_set INTEGER :=9; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ +--amount,offset向下取整 + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=3.9; -- 3 +off_set float :=5.6; -- 5 +b2 blob :=cast_to_raw('abc'); +r1 raw; +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount float :=2.2; -- 2 +off_set float :=5.1; -- 5 +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ + +declare +b1 blob :=cast_to_raw('ABCDEFGH'); +amount INTEGER :=4; +off_set INTEGER :=8; +b2 blob :=cast_to_raw('abc'); +begin +gms_lob.write(b1, amount, off_set, b2); +gms_output.put_line(b1::text); +end; +/ + +--offset为空 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=3; +off_set INTEGER; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ + +--lob无效 +declare +amount INTEGER :=3; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(cast_to_raw('111111'), amount, off_set, b2); +end; +/ + +--amount大于buffer大小 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=5; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ + +--offset超出范围 +declare +b1 blob :=cast_to_raw('11111111'); +amount INTEGER :=3; +off_set INTEGER :=0; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.write(b1, amount, off_set, b2); +end; +/ + +--lob只读 +declare +b1 blob :=cast_to_raw('1111'); +amount INTEGER :=2; +off_set INTEGER :=2; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.open(b1, gms_lob.LOB_READONLY); +gms_lob.write(b1, amount, off_set, b2); +end; +/ + +--gms_lob.gms_lob_write_clob +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER :=1; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ + +-- amount, off_set 不向下取整 +declare +c1 clob :='11111111'; +amount INTEGER :=3.9; +off_set INTEGER :=1.8; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ + +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER :=1.8; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ + +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER :=9; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ + +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER :=10; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ + +--buffer等于destlob +declare +c1 clob :='1234'; +amount INTEGER :=2; +off_set INTEGER :=2; +begin +gms_lob.write(c1, amount, off_set, c1); +gms_output.put_line(c1::text); +end; +/ + +--VALUEERROR +--lob为空 +declare +c1 clob; +amount INTEGER :=3; +off_set INTEGER :=2; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ + +--amount为空 +declare +c1 clob :='11111111'; +amount INTEGER; +off_set INTEGER :=10; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ + +--offset为空 +declare +c1 clob :='11111111'; +amount INTEGER :=3; +off_set INTEGER; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ + +--lob无效 +declare +amount INTEGER :=3; +off_set INTEGER :=2; +c2 clob :='222'; +begin +gms_lob.write('11111111', amount, off_set, c2); +end; +/ + +--amount大于buffer大小 +declare +c1 clob :='11111111'; +amount INTEGER :=5; +off_set INTEGER :=2; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ + +--offset超出范围 +declare +c1 clob :='11111111'; +amount INTEGER :=2; +off_set INTEGER :=0; +c2 clob :='222'; +begin +gms_lob.write(c1, amount, off_set, c2); +end; +/ + +--lob只读 +declare +c1 clob :='1111'; +amount INTEGER :=2; +off_set INTEGER :=2; +c2 clob :='222'; +begin +gms_lob.open(c1, gms_lob.LOB_READONLY); +gms_lob.write(c1, amount, off_set, c2); +end; +/ + +-----------append函数----------- +--gms_lob.gms_lob_append_blob +declare +b1 blob :=cast_to_raw('11111111'); +b2 blob :=cast_to_raw('222'); +begin +gms_lob.append(b1, b2); +gms_output.put_line(b1::text); +end; +/ + +--VALUEERROR +--destlob为空 +declare +b1 blob; +b2 blob :=cast_to_raw('222'); +begin +gms_lob.append(b1, b2); +end; +/ + +--srclob为空 +declare +b1 blob :=cast_to_raw('11111111'); +b2 blob; +begin +gms_lob.append(b1, b2); +end; +/ + +--lob无效 +declare +b2 blob :=cast_to_raw('222'); +begin +gms_lob.append(cast_to_raw('1111'), b2); +end; +/ + +--lob只读 +declare +b1 blob :=cast_to_raw('1111'); +b2 blob :=cast_to_raw('222'); +begin +gms_lob.open(b1, gms_lob.LOB_READONLY); +gms_lob.append(b1, b2); +end; +/ + +--gms_lob.gms_lob_append_clob +declare +c1 clob :='11111111'; +c2 clob :='222'; +begin +gms_lob.append(c1, c2); +gms_output.put_line(c1::text); +end; +/ + +--VALUEERROR +--destlob为空 +declare +c1 clob; +c2 clob :='222'; +begin +gms_lob.append(c1, c2); +end; +/ + +--srclob为空 +declare +c1 clob :='11111111'; +c2 clob; +begin +gms_lob.append(c1, c2); +end; +/ + +--lob无效 +declare +c2 clob :='222'; +begin +gms_lob.append('1111', c2); +end; +/ + +--lob只读 +declare +c1 clob :='1111'; +c2 clob :='222'; +begin +gms_lob.open(c1, gms_lob.LOB_READONLY); +gms_lob.append(c1, c2); +end; +/ + +declare +b1 blob :=cast_to_raw('ABC123'); +amount INTEGER :=3; +off_set INTEGER :=100; +b2 blob :=cast_to_raw('abc'); +c1 int; +BEGIN +gms_lob.write(b1,amount,off_set,b2); +gms_output.put_line(b1::text); +c1:=gms_lob.getlength(b1); +gms_output.put_line(c1); +end; +/ + +declare +c1 clob :='ABC123'; +amount INTEGER :=3; +off_set INTEGER :=100; +c2 clob :='abc'; +begin +gms_lob.write(c1, amount, off_set, c2); +gms_output.put_line(c1::text); +end; +/ + +declare +b1 blob :=null; +b2 blob :=cast_to_raw('abc'); +BEGIN +gms_lob.append(b1,b2); +gms_output.put_line(b1::text); +end; +/ + +drop table if exists lob_mvcc; +create table lob_mvcc(id int, c_lob clob, b_lob blob); +insert into lob_mvcc values(1, 'clob', cast_to_raw('blob')); +insert into lob_mvcc values(2, '中文clobobject测试', cast_to_raw('中文blobobject测试')); + +select * from lob_mvcc order by id; + +declare +c1 clob; +c2 clob :='222test'; +begin +select c_lob into c1 from lob_mvcc where id=1 for update; +gms_lob.append(c1, c2); +end; +/ + +select * from lob_mvcc order by id; +drop table lob_mvcc; + +create or replace procedure proc_1034970 +as +b1 clob :='测试'; +b2 clob :='测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试'; +begin +gms_lob.open(b1,gms_lob.lob_readwrite); +for i in 1..100 loop +gms_lob.append(b1,b2); +end loop; +end; +/ + +call proc_1034970(); +drop procedure proc_1034970; +\c contrib_regression +drop database if exists testlob; + diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 18a583be3..dd61bda6a 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -16327,6 +16327,39 @@ CallFuncStmt: CALL func_name '(' ')' } | CALL func_name '(' callfunc_args ')' { + ListCell* l = list_head($2); + Node* name = (Node*)lfirst(l); + if (IsA(name, String) && (strcmp(strVal(name), "gms_lob") == 0)) { + ListCell* lc = list_head($4); + Node* n1 = (Node*)lfirst(lc); + if (IsA(n1, ColumnRef)) { + char* lobname = strVal(linitial(((ColumnRef*)n1)->fields)); + Node* funcname = (Node*)lfirst(list_tail($2)); + if (strcmp(strVal(funcname), "createtemporary") == 0) { + if (list_length($4) == 2) { + $4 = lappend($4, makeIntConst(10, -1)); + } + } + $4 = lappend($4, makeStringConst(lobname, -1)); + } else if (IsA(n1, NamedArgExpr)) { + Node* n2 = ((Node*)((NamedArgExpr*)n1)->arg); + if (IsA(n2, ColumnRef)) { + char* lobname = strVal(linitial(((ColumnRef*)n2)->fields)); + Node* funcname = (Node*)lfirst(list_tail($2)); + if (strcmp(strVal(funcname), "createtemporary") == 0) { + if (list_length($4) == 2) { + $4 = lappend($4, makeIntConst(10, -1)); + } + } + NamedArgExpr *na = makeNode(NamedArgExpr); + na->name = pstrdup("lobname"); + na->arg = (Expr *)makeStringConst(lobname, -1); + na->argnumber = -1; /* until determined */ + na->location = @1; + $4 = lappend($4, (Node *) na); + } + } + } #ifndef ENABLE_MULTIPLE_NODES $$ = makeCallFuncStmt($2, $4, enable_out_param_override()); #else @@ -28745,6 +28778,7 @@ func_expr: func_application within_group_clause filter_clause over_clause func_application: func_name '(' func_arg_list opt_sort_clause ')' { FuncCall *n = makeNode(FuncCall); + ListCell* l = list_head($1); n->funcname = $1; n->args = $3; n->agg_order = $4; @@ -28754,6 +28788,38 @@ func_application: func_name '(' func_arg_list opt_sort_clause ')' n->over = NULL; n->location = @1; n->call_func = false; + Node* name = (Node*)lfirst(l); + if (IsA(name, String) && (strcmp(strVal(name), "gms_lob") == 0)) { + ListCell* lc = list_head($3); + Node* n1 = (Node*)lfirst(lc); + if (IsA(n1, ColumnRef)) { + char* lobname = lobname = strVal(linitial(((ColumnRef*)n1)->fields)); + Node* funcname = (Node*)lfirst(list_tail($1)); + if (strcmp(strVal(funcname), "createtemporary") == 0) { + if (list_length($4) == 2) { + n->args = lappend(n->args, makeIntConst(10, -1)); + }; + } + n->args = lappend(n->args, makeStringConst(lobname, -1)); + } else if (IsA(n1, NamedArgExpr)) { + Node* n2 = ((Node*)((NamedArgExpr*)n1)->arg); + if (IsA(n2, ColumnRef)) { + char* lobname = strVal(linitial(((ColumnRef*)$1)->fields)); + Node* funcname = (Node*)lfirst(list_tail($1)); + if (strcmp(strVal(funcname), "createtemporary") == 0) { + if (list_length(n->args) == 2) { + n->args = lappend(n->args, makeIntConst(10, -1)); + } + } + NamedArgExpr *na = makeNode(NamedArgExpr); + na->name = pstrdup("lobname"); + na->arg = (Expr *)makeStringConst(lobname, -1); + na->argnumber = -1; /* until determined */ + na->location = @1; + n->args = lappend(n->args, (Node *) na); + } + } + } $$ = (Node *)n; } | func_application_special { $$ = $1; } @@ -33129,7 +33195,8 @@ makeCallFuncStmt(List* funcname,List* parameters, bool is_call) if (clist->next) { has_overload_func = true; - if (IsPackageFunction(funcname) == false && IsPackageSchemaOid(SchemaNameGetSchemaOid(schemaname, true)) == false) + if (IsPackageFunction(funcname) == false && IsPackageSchemaOid(SchemaNameGetSchemaOid(schemaname, true)) == false && + (schemaname == NULL || strncmp(schemaname, "gms_lob", strlen("gms_lob")))) { const char* message = "function isn't exclusive "; InsertErrorMessage(message, u_sess->plsql_cxt.plpgsql_yylloc); @@ -33238,7 +33305,6 @@ makeCallFuncStmt(List* funcname,List* parameters, bool is_call) } } } - i++; } diff --git a/src/common/pl/plpgsql/src/gram.y b/src/common/pl/plpgsql/src/gram.y index 38d762bde..e954f9043 100755 --- a/src/common/pl/plpgsql/src/gram.y +++ b/src/common/pl/plpgsql/src/gram.y @@ -7482,7 +7482,8 @@ make_callfunc_stmt(const char *sqlstart, int location, bool is_assign, bool eate } else { DeconstructQualifiedName(funcname, &schemaname, &funcStrName, &pkgname); } - if (IsPackageFunction(funcname) == false && IsPackageSchemaOid(SchemaNameGetSchemaOid(schemaname, true)) == false) + if (IsPackageFunction(funcname) == false && IsPackageSchemaOid(SchemaNameGetSchemaOid(schemaname, true)) == false && + (schemaname == NULL || strncmp(schemaname, "gms_lob", strlen("gms_lob")))) { const char* message = "function is not exclusive"; InsertErrorMessage(message, plpgsql_yylloc);