first preps for new function table_to_forallinsert

This commit is contained in:
Ottmar Gobrecht 2020-04-07 20:45:27 +02:00
parent b7a83dec89
commit a534f2febf
7 changed files with 392 additions and 70 deletions

View File

@ -81,7 +81,7 @@ SIGNATURE
```sql
PACKAGE PLEX AUTHID current_user IS
c_plex_name CONSTANT VARCHAR2(30 CHAR) := 'PLEX - PL/SQL Export Utilities';
c_plex_version CONSTANT VARCHAR2(10 CHAR) := '2.1.0';
c_plex_version CONSTANT VARCHAR2(10 CHAR) := '2.1.0.1';
c_plex_url CONSTANT VARCHAR2(40 CHAR) := 'https://github.com/ogobrecht/plex';
c_plex_license CONSTANT VARCHAR2(10 CHAR) := 'MIT';
c_plex_license_url CONSTANT VARCHAR2(60 CHAR) := 'https://github.com/ogobrecht/plex/blob/master/LICENSE.txt';

View File

@ -9,7 +9,7 @@
"scripts": {
"prebuild": "npx ploc --in src/PLEX.pks --out README.md",
"build": "node src/build.js",
"postbuild": "echo exit | sqlplus -S demo/oracle@localhost:1521/xepdb1 @plex_install.sql",
"postbuild": "echo exit | sqlplus -S tests/oracle@localhost/xepdb1 @plex_install.sql",
"watch": "chokidar src/PLEX.pks src/PLEX.pkb src/plex_install.sql --initial -c \"npm run build\""
},
"devDependencies": {

View File

@ -38,7 +38,7 @@ END;
prompt Compile package plex (spec)
CREATE OR REPLACE PACKAGE PLEX AUTHID current_user IS
c_plex_name CONSTANT VARCHAR2(30 CHAR) := 'PLEX - PL/SQL Export Utilities';
c_plex_version CONSTANT VARCHAR2(10 CHAR) := '2.1.0';
c_plex_version CONSTANT VARCHAR2(10 CHAR) := '2.1.0.1';
c_plex_url CONSTANT VARCHAR2(40 CHAR) := 'https://github.com/ogobrecht/plex';
c_plex_license CONSTANT VARCHAR2(10 CHAR) := 'MIT';
c_plex_license_url CONSTANT VARCHAR2(60 CHAR) := 'https://github.com/ogobrecht/plex/blob/master/LICENSE.txt';
@ -607,6 +607,10 @@ PROCEDURE util_clob_query_to_csv (
p_quote_mark IN VARCHAR2 DEFAULT '"',
p_header_prefix IN VARCHAR2 DEFAULT NULL);
PROCEDURE util_clob_table_to_forallinsert (
p_table_name IN VARCHAR2,
p_max_rows IN NUMBER DEFAULT 1000);
PROCEDURE util_clob_create_error_log (p_export_files IN OUT NOCOPY tab_export_files);
PROCEDURE util_clob_create_runtime_log (p_export_files IN OUT NOCOPY tab_export_files);
@ -795,6 +799,10 @@ PROCEDURE util_clob_query_to_csv (
p_quote_mark IN VARCHAR2 DEFAULT '"',
p_header_prefix IN VARCHAR2 DEFAULT NULL);
PROCEDURE util_clob_table_to_forallinsert (
p_table_name IN VARCHAR2,
p_max_rows IN NUMBER DEFAULT 1000);
PROCEDURE util_clob_create_runtime_log (p_export_files IN OUT NOCOPY tab_export_files);
PROCEDURE util_clob_create_error_log (p_export_files IN OUT NOCOPY tab_export_files);
@ -1441,9 +1449,9 @@ IS
replace(v_buffer_varchar2, c_crlf, c_lf),
c_cr,
c_lf);
-- if we have the parameter p_force_quotes set to true or the delimiter character or
-- line feeds in the string then we have to wrap the text in quotes marks and escape
-- the quote marks inside the text by double them
-- if we have the delimiter character or line feeds in the string then we
-- have to wrap the text in quotes marks and escape the quote marks
-- inside the text by double them
IF instr(v_buffer_varchar2, p_delimiter) > 0 OR instr(v_buffer_varchar2, c_lf) > 0 THEN
v_buffer_varchar2 := p_quote_mark
|| replace(v_buffer_varchar2, p_quote_mark, p_quote_mark || p_quote_mark)
@ -1540,6 +1548,158 @@ BEGIN
END IF;
END util_clob_query_to_csv;
PROCEDURE util_clob_table_to_forallinsert (
p_table_name IN VARCHAR2,
p_max_rows IN NUMBER DEFAULT 1000)
IS
v_line_terminator VARCHAR2(2) := c_crlf; -- to be compatible with Excel we need to use crlf here (multiline text uses lf and is wrapped in quotes)
v_cursor PLS_INTEGER;
v_ignore_me PLS_INTEGER;
v_data_count PLS_INTEGER := 0;
v_col_cnt PLS_INTEGER;
v_desc_tab dbms_sql.desc_tab3;
v_buffer_varchar2 VARCHAR2(32767 CHAR);
v_buffer_clob CLOB;
v_buffer_xmltype XMLTYPE;
v_buffer_long LONG;
v_buffer_long_length PLS_INTEGER;
-- numeric type identfiers
c_number CONSTANT PLS_INTEGER := 2; -- FLOAT
c_binary_float CONSTANT PLS_INTEGER := 100;
c_binary_double CONSTANT PLS_INTEGER := 101;
-- string type identfiers
c_char CONSTANT PLS_INTEGER := 96; -- NCHAR
c_varchar2 CONSTANT PLS_INTEGER := 1; -- NVARCHAR2
c_long CONSTANT PLS_INTEGER := 8;
c_clob CONSTANT PLS_INTEGER := 112; -- NCLOB
c_xmltype CONSTANT PLS_INTEGER := 109; -- ANYDATA, ANYDATASET, ANYTYPE, Object type, VARRAY, Nested table
c_rowid CONSTANT PLS_INTEGER := 11;
c_urowid CONSTANT PLS_INTEGER := 208;
-- binary type identfiers
c_raw CONSTANT PLS_INTEGER := 23;
c_long_raw CONSTANT PLS_INTEGER := 24;
c_blob CONSTANT PLS_INTEGER := 113;
c_bfile CONSTANT PLS_INTEGER := 114;
-- date type identfiers
c_date CONSTANT PLS_INTEGER := 12;
c_timestamp CONSTANT PLS_INTEGER := 180;
c_timestamp_with_time_zone CONSTANT PLS_INTEGER := 181;
c_timestamp_with_local_tz CONSTANT PLS_INTEGER := 231;
-- interval type identfiers
c_interval_year_to_month CONSTANT PLS_INTEGER := 182;
c_interval_day_to_second CONSTANT PLS_INTEGER := 183;
-- cursor type identfiers
c_ref CONSTANT PLS_INTEGER := 111;
c_ref_cursor CONSTANT PLS_INTEGER := 102; -- same identfiers for strong and weak ref cursor
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE escape_varchar2_buffer_for_csv IS
BEGIN
NULL;
-- IF v_buffer_varchar2 IS NOT NULL THEN
-- -- normalize line feeds for Excel
-- v_buffer_varchar2 := replace(
-- replace(v_buffer_varchar2, c_crlf, c_lf),
-- c_cr,
-- c_lf);
-- -- if we have the delimiter character or line feeds in the string then we
-- -- have to wrap the text in quotes marks and escape the quote marks
-- -- inside the text by double them
-- IF instr(v_buffer_varchar2, p_delimiter) > 0 OR instr(v_buffer_varchar2, c_lf) > 0 THEN
-- v_buffer_varchar2 := p_quote_mark
-- || replace(v_buffer_varchar2, p_quote_mark, p_quote_mark || p_quote_mark)
-- || p_quote_mark;
-- END IF;
-- END IF;
EXCEPTION
WHEN value_error THEN
v_buffer_varchar2 := 'Value skipped - escaped text larger then ' || c_vc2_max_size || ' characters';
END escape_varchar2_buffer_for_csv;
FUNCTION get_table_pk_list RETURN VARCHAR2 IS
begin
return null;
end;
BEGIN
IF p_table_name IS NOT NULL THEN
v_cursor := dbms_sql.open_cursor;
dbms_sql.parse(
v_cursor,
'select * from ' || p_table_name || ' order by ' || get_table_pk_list,
dbms_sql.native);
-- https://support.esri.com/en/technical-article/000010110
-- http://bluefrog-oracle.blogspot.com/2011/11/describing-ref-cursor-using-dbmssql-api.html
dbms_sql.describe_columns3(v_cursor, v_col_cnt, v_desc_tab);
FOR i IN 1..v_col_cnt LOOP
IF v_desc_tab(i).col_type = c_clob THEN
dbms_sql.define_column(v_cursor, i, v_buffer_clob);
ELSIF v_desc_tab(i).col_type = c_xmltype THEN
dbms_sql.define_column(v_cursor, i, v_buffer_xmltype);
ELSIF v_desc_tab(i).col_type = c_long THEN
dbms_sql.define_column_long(v_cursor, i);
ELSIF v_desc_tab(i).col_type IN (c_raw, c_long_raw, c_blob, c_bfile) THEN
NULL; --> we ignore binary data types
ELSE
dbms_sql.define_column(v_cursor, i, v_buffer_varchar2, c_vc2_max_size);
END IF;
END LOOP;
v_ignore_me := dbms_sql.execute(v_cursor);
FOR i IN 1..v_col_cnt LOOP
v_buffer_varchar2 := v_desc_tab(i).col_name;
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
END LOOP;
util_clob_append(v_line_terminator);
-- create data
LOOP
EXIT WHEN dbms_sql.fetch_rows(v_cursor) = 0 OR v_data_count = p_max_rows;
FOR i IN 1..v_col_cnt LOOP
IF v_desc_tab(i).col_type = c_clob THEN
dbms_sql.column_value(v_cursor, i, v_buffer_clob);
IF length(v_buffer_clob) <= c_vc2_max_size THEN
v_buffer_varchar2 := substr(v_buffer_clob, 1, c_vc2_max_size);
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
ELSE
v_buffer_varchar2 := 'CLOB value skipped - larger then ' || c_vc2_max_size || ' characters';
util_clob_append(v_buffer_varchar2);
END IF;
ELSIF v_desc_tab(i).col_type = c_xmltype THEN
dbms_sql.column_value(v_cursor, i, v_buffer_xmltype);
v_buffer_clob := v_buffer_xmltype.getclobval();
IF length(v_buffer_clob) <= c_vc2_max_size THEN
v_buffer_varchar2 := substr(v_buffer_clob, 1, c_vc2_max_size);
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
ELSE
v_buffer_varchar2 := 'XML value skipped - larger then ' || c_vc2_max_size || ' characters';
util_clob_append(v_buffer_varchar2);
END IF;
ELSIF v_desc_tab(i).col_type = c_long THEN
dbms_sql.column_value_long(v_cursor, i, c_vc2_max_size, 0, v_buffer_varchar2, v_buffer_long_length);
IF v_buffer_long_length <= c_vc2_max_size THEN
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
ELSE
util_clob_append('LONG value skipped - larger then ' || c_vc2_max_size || ' characters');
END IF;
ELSIF v_desc_tab(i).col_type IN (c_raw, c_long_raw, c_blob, c_bfile) THEN
util_clob_append('Binary data type skipped - not supported for CSV');
ELSE
dbms_sql.column_value(v_cursor, i, v_buffer_varchar2);
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
END IF;
END LOOP;
util_clob_append(v_line_terminator);
v_data_count := v_data_count + 1;
END LOOP;
dbms_sql.close_cursor(v_cursor);
END IF;
END util_clob_table_to_forallinsert;
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE util_clob_create_error_log (p_export_files IN OUT NOCOPY tab_export_files) IS

View File

@ -1,5 +1,6 @@
SET DEFINE OFF FEEDBACK OFF
WHENEVER SQLERROR EXIT sql.sqlcode ROLLBACK
set define off feedback off
whenever sqlerror exit sql.sqlcode rollback
prompt
prompt Uninstalling PL/SQL Export Utilities
prompt ====================================

View File

@ -175,6 +175,10 @@ PROCEDURE util_clob_query_to_csv (
p_quote_mark IN VARCHAR2 DEFAULT '"',
p_header_prefix IN VARCHAR2 DEFAULT NULL);
PROCEDURE util_clob_table_to_forallinsert (
p_table_name IN VARCHAR2,
p_max_rows IN NUMBER DEFAULT 1000);
PROCEDURE util_clob_create_runtime_log (p_export_files IN OUT NOCOPY tab_export_files);
PROCEDURE util_clob_create_error_log (p_export_files IN OUT NOCOPY tab_export_files);
@ -821,9 +825,9 @@ IS
replace(v_buffer_varchar2, c_crlf, c_lf),
c_cr,
c_lf);
-- if we have the parameter p_force_quotes set to true or the delimiter character or
-- line feeds in the string then we have to wrap the text in quotes marks and escape
-- the quote marks inside the text by double them
-- if we have the delimiter character or line feeds in the string then we
-- have to wrap the text in quotes marks and escape the quote marks
-- inside the text by double them
IF instr(v_buffer_varchar2, p_delimiter) > 0 OR instr(v_buffer_varchar2, c_lf) > 0 THEN
v_buffer_varchar2 := p_quote_mark
|| replace(v_buffer_varchar2, p_quote_mark, p_quote_mark || p_quote_mark)
@ -920,6 +924,158 @@ BEGIN
END IF;
END util_clob_query_to_csv;
PROCEDURE util_clob_table_to_forallinsert (
p_table_name IN VARCHAR2,
p_max_rows IN NUMBER DEFAULT 1000)
IS
v_line_terminator VARCHAR2(2) := c_crlf; -- to be compatible with Excel we need to use crlf here (multiline text uses lf and is wrapped in quotes)
v_cursor PLS_INTEGER;
v_ignore_me PLS_INTEGER;
v_data_count PLS_INTEGER := 0;
v_col_cnt PLS_INTEGER;
v_desc_tab dbms_sql.desc_tab3;
v_buffer_varchar2 VARCHAR2(32767 CHAR);
v_buffer_clob CLOB;
v_buffer_xmltype XMLTYPE;
v_buffer_long LONG;
v_buffer_long_length PLS_INTEGER;
-- numeric type identfiers
c_number CONSTANT PLS_INTEGER := 2; -- FLOAT
c_binary_float CONSTANT PLS_INTEGER := 100;
c_binary_double CONSTANT PLS_INTEGER := 101;
-- string type identfiers
c_char CONSTANT PLS_INTEGER := 96; -- NCHAR
c_varchar2 CONSTANT PLS_INTEGER := 1; -- NVARCHAR2
c_long CONSTANT PLS_INTEGER := 8;
c_clob CONSTANT PLS_INTEGER := 112; -- NCLOB
c_xmltype CONSTANT PLS_INTEGER := 109; -- ANYDATA, ANYDATASET, ANYTYPE, Object type, VARRAY, Nested table
c_rowid CONSTANT PLS_INTEGER := 11;
c_urowid CONSTANT PLS_INTEGER := 208;
-- binary type identfiers
c_raw CONSTANT PLS_INTEGER := 23;
c_long_raw CONSTANT PLS_INTEGER := 24;
c_blob CONSTANT PLS_INTEGER := 113;
c_bfile CONSTANT PLS_INTEGER := 114;
-- date type identfiers
c_date CONSTANT PLS_INTEGER := 12;
c_timestamp CONSTANT PLS_INTEGER := 180;
c_timestamp_with_time_zone CONSTANT PLS_INTEGER := 181;
c_timestamp_with_local_tz CONSTANT PLS_INTEGER := 231;
-- interval type identfiers
c_interval_year_to_month CONSTANT PLS_INTEGER := 182;
c_interval_day_to_second CONSTANT PLS_INTEGER := 183;
-- cursor type identfiers
c_ref CONSTANT PLS_INTEGER := 111;
c_ref_cursor CONSTANT PLS_INTEGER := 102; -- same identfiers for strong and weak ref cursor
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE escape_varchar2_buffer_for_csv IS
BEGIN
NULL;
-- IF v_buffer_varchar2 IS NOT NULL THEN
-- -- normalize line feeds for Excel
-- v_buffer_varchar2 := replace(
-- replace(v_buffer_varchar2, c_crlf, c_lf),
-- c_cr,
-- c_lf);
-- -- if we have the delimiter character or line feeds in the string then we
-- -- have to wrap the text in quotes marks and escape the quote marks
-- -- inside the text by double them
-- IF instr(v_buffer_varchar2, p_delimiter) > 0 OR instr(v_buffer_varchar2, c_lf) > 0 THEN
-- v_buffer_varchar2 := p_quote_mark
-- || replace(v_buffer_varchar2, p_quote_mark, p_quote_mark || p_quote_mark)
-- || p_quote_mark;
-- END IF;
-- END IF;
EXCEPTION
WHEN value_error THEN
v_buffer_varchar2 := 'Value skipped - escaped text larger then ' || c_vc2_max_size || ' characters';
END escape_varchar2_buffer_for_csv;
FUNCTION get_table_pk_list RETURN VARCHAR2 IS
begin
return null;
end;
BEGIN
IF p_table_name IS NOT NULL THEN
v_cursor := dbms_sql.open_cursor;
dbms_sql.parse(
v_cursor,
'select * from ' || p_table_name || ' order by ' || get_table_pk_list,
dbms_sql.native);
-- https://support.esri.com/en/technical-article/000010110
-- http://bluefrog-oracle.blogspot.com/2011/11/describing-ref-cursor-using-dbmssql-api.html
dbms_sql.describe_columns3(v_cursor, v_col_cnt, v_desc_tab);
FOR i IN 1..v_col_cnt LOOP
IF v_desc_tab(i).col_type = c_clob THEN
dbms_sql.define_column(v_cursor, i, v_buffer_clob);
ELSIF v_desc_tab(i).col_type = c_xmltype THEN
dbms_sql.define_column(v_cursor, i, v_buffer_xmltype);
ELSIF v_desc_tab(i).col_type = c_long THEN
dbms_sql.define_column_long(v_cursor, i);
ELSIF v_desc_tab(i).col_type IN (c_raw, c_long_raw, c_blob, c_bfile) THEN
NULL; --> we ignore binary data types
ELSE
dbms_sql.define_column(v_cursor, i, v_buffer_varchar2, c_vc2_max_size);
END IF;
END LOOP;
v_ignore_me := dbms_sql.execute(v_cursor);
FOR i IN 1..v_col_cnt LOOP
v_buffer_varchar2 := v_desc_tab(i).col_name;
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
END LOOP;
util_clob_append(v_line_terminator);
-- create data
LOOP
EXIT WHEN dbms_sql.fetch_rows(v_cursor) = 0 OR v_data_count = p_max_rows;
FOR i IN 1..v_col_cnt LOOP
IF v_desc_tab(i).col_type = c_clob THEN
dbms_sql.column_value(v_cursor, i, v_buffer_clob);
IF length(v_buffer_clob) <= c_vc2_max_size THEN
v_buffer_varchar2 := substr(v_buffer_clob, 1, c_vc2_max_size);
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
ELSE
v_buffer_varchar2 := 'CLOB value skipped - larger then ' || c_vc2_max_size || ' characters';
util_clob_append(v_buffer_varchar2);
END IF;
ELSIF v_desc_tab(i).col_type = c_xmltype THEN
dbms_sql.column_value(v_cursor, i, v_buffer_xmltype);
v_buffer_clob := v_buffer_xmltype.getclobval();
IF length(v_buffer_clob) <= c_vc2_max_size THEN
v_buffer_varchar2 := substr(v_buffer_clob, 1, c_vc2_max_size);
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
ELSE
v_buffer_varchar2 := 'XML value skipped - larger then ' || c_vc2_max_size || ' characters';
util_clob_append(v_buffer_varchar2);
END IF;
ELSIF v_desc_tab(i).col_type = c_long THEN
dbms_sql.column_value_long(v_cursor, i, c_vc2_max_size, 0, v_buffer_varchar2, v_buffer_long_length);
IF v_buffer_long_length <= c_vc2_max_size THEN
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
ELSE
util_clob_append('LONG value skipped - larger then ' || c_vc2_max_size || ' characters');
END IF;
ELSIF v_desc_tab(i).col_type IN (c_raw, c_long_raw, c_blob, c_bfile) THEN
util_clob_append('Binary data type skipped - not supported for CSV');
ELSE
dbms_sql.column_value(v_cursor, i, v_buffer_varchar2);
escape_varchar2_buffer_for_csv;
util_clob_append(v_buffer_varchar2);
END IF;
END LOOP;
util_clob_append(v_line_terminator);
v_data_count := v_data_count + 1;
END LOOP;
dbms_sql.close_cursor(v_cursor);
END IF;
END util_clob_table_to_forallinsert;
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE util_clob_create_error_log (p_export_files IN OUT NOCOPY tab_export_files) IS

View File

@ -1,6 +1,6 @@
CREATE OR REPLACE PACKAGE PLEX AUTHID current_user IS
c_plex_name CONSTANT VARCHAR2(30 CHAR) := 'PLEX - PL/SQL Export Utilities';
c_plex_version CONSTANT VARCHAR2(10 CHAR) := '2.1.0';
c_plex_version CONSTANT VARCHAR2(10 CHAR) := '2.1.0.1';
c_plex_url CONSTANT VARCHAR2(40 CHAR) := 'https://github.com/ogobrecht/plex';
c_plex_license CONSTANT VARCHAR2(10 CHAR) := 'MIT';
c_plex_license_url CONSTANT VARCHAR2(60 CHAR) := 'https://github.com/ogobrecht/plex/blob/master/LICENSE.txt';
@ -569,6 +569,10 @@ PROCEDURE util_clob_query_to_csv (
p_quote_mark IN VARCHAR2 DEFAULT '"',
p_header_prefix IN VARCHAR2 DEFAULT NULL);
PROCEDURE util_clob_table_to_forallinsert (
p_table_name IN VARCHAR2,
p_max_rows IN NUMBER DEFAULT 1000);
PROCEDURE util_clob_create_error_log (p_export_files IN OUT NOCOPY tab_export_files);
PROCEDURE util_clob_create_runtime_log (p_export_files IN OUT NOCOPY tab_export_files);

View File

@ -1,5 +1,6 @@
SET DEFINE OFF FEEDBACK OFF
WHENEVER SQLERROR EXIT sql.sqlcode ROLLBACK
set define off feedback off
whenever sqlerror exit sql.sqlcode rollback
prompt
prompt Uninstalling PL/SQL Export Utilities
prompt ====================================