Compare commits

...

7 Commits

Author SHA1 Message Date
Ottmar Gobrecht
349d6c4545 add file download procedure, cleanup 2022-10-04 22:08:46 +02:00
Ottmar Gobrecht
2476fdea95 update dependencies 2022-10-04 22:03:18 +02:00
Ottmar Gobrecht
802781e7fc rename license 2022-10-04 22:02:09 +02:00
Ottmar Gobrecht
6e8e3f0603 Create .npmrc 2022-10-04 22:01:52 +02:00
Ottmar Gobrecht
5958ff16cd Merge branch 'master' into development 2021-08-14 20:38:34 +02:00
Ottmar Gobrecht
46bab05f5d create data load script for import format "om_tapigen" 2021-07-26 09:52:09 +02:00
Ottmar Gobrecht
16ae48af6d new format OM_TAPIGEN for data export 2021-07-25 19:23:40 +02:00
13 changed files with 1977 additions and 478 deletions

1
.npmrc Normal file
View File

@ -0,0 +1 @@
registry=https://registry.npmjs.org/

View File

@ -1,5 +1,9 @@
# Changelog
## 2.5.0 (2022-xx-xx)
- Add file download procedure
## 2.4.2 (2021-01-05)
- Function BackApp:

20
LICENSE.md Normal file
View File

@ -0,0 +1,20 @@
# MIT License
Copyright (c) 2018 Ottmar Gobrecht
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2018 Ottmar Gobrecht
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

698
README.md
View File

@ -1,7 +1,7 @@
<!-- DO NOT EDIT THIS FILE DIRECTLY - it is generated from source file src/PLEX.pks -->
<!-- markdownlint-disable MD003 MD012 MD033 -->
PL/SQL Export Utilities
PL/SQL Export Utilities
=======================
- [Package PLEX](#plex)
@ -10,6 +10,8 @@ PL/SQL Export Utilities
- [Function queries_to_csv](#queries_to_csv)
- [Function to_zip](#to_zip)
- [Function to_base64](#to_base64)
- [Procedure download](#download)
- [Procedure download](#download)
- [Function view_error_log](#view_error_log)
- [Function view_runtime_log](#view_runtime_log)
@ -17,40 +19,40 @@ PL/SQL Export Utilities
<h2><a id="plex"></a>Package PLEX</h2>
<!----------------------------------->
PLEX was created to be able to quickstart version control for existing Oracle DB projects and has currently two main functions called **BackApp** and **Queries_to_CSV**. Queries_to_CSV is used by BackApp as a helper function, but its functionality is also useful standalone.
Also see this resources for more information:
- [Blog post on how to getting started](https://ogobrecht.github.io/posts/2018-08-26-plex-plsql-export-utilities)
- [PLEX project page on GitHub](https://github.com/ogobrecht/plex)
- [Changelog](https://github.com/ogobrecht/plex/blob/master/CHANGELOG.md)
- [Give feedback](https://github.com/ogobrecht/plex/issues/new)
DEPENDENCIES
The package itself is independend, but functionality varies on the following conditions:
- For APEX app export: APEX >= 5.1.4 installed
- For ORDS modules export: ORDS >= 18.3 installed (I think package ords_export is included since this version, but I don't know it)
- ATTENTION: There seems to be a [bug in ORDS 19.2](https://community.oracle.com/thread/4292776) which prevents you to export ORDS modules via the package ords_export. Please see plex_error_log.md, if you miss your ORDS modules after an export - this is no problem of PLEX.
INSTALLATION
- Download the [latest version](https://github.com/ogobrecht/plex/releases/latest)
- Unzip it, open a shell and go into the root directory
- Start SQL*Plus (or another tool which can run SQL scripts)
- To install PLEX run the provided install script `plex_install.sql` (script provides compiler flags)
PLEX was created to be able to quickstart version control for existing Oracle DB projects and has currently two main functions called **BackApp** and **Queries_to_CSV**. Queries_to_CSV is used by BackApp as a helper function, but its functionality is also useful standalone.
Also see this resources for more information:
- [Blog post on how to getting started](https://ogobrecht.github.io/posts/2018-08-26-plex-plsql-export-utilities)
- [PLEX project page on GitHub](https://github.com/ogobrecht/plex)
- [Changelog](https://github.com/ogobrecht/plex/blob/master/CHANGELOG.md)
- [Give feedback](https://github.com/ogobrecht/plex/issues/new)
DEPENDENCIES
The package itself is independend, but functionality varies on the following conditions:
- For APEX app export: APEX >= 5.1.4 installed
- For ORDS modules export: ORDS >= 18.3 installed (I think package ords_export is included since this version, but I don't know it)
- ATTENTION: There seems to be a [bug in ORDS 19.2](https://community.oracle.com/thread/4292776) which prevents you to export ORDS modules via the package ords_export. Please see plex_error_log.md, if you miss your ORDS modules after an export - this is no problem of PLEX.
INSTALLATION
- Download the [latest version](https://github.com/ogobrecht/plex/releases/latest)
- Unzip it, open a shell and go into the root directory
- Start SQL*Plus (or another tool which can run SQL scripts)
- To install PLEX run the provided install script `plex_install.sql` (script provides compiler flags)
- To uninstall PLEX run the provided script `plex_uninstall.sql` or drop the package manually
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.4.2';
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';
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.4.2';
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';
c_plex_author CONSTANT VARCHAR2(20 CHAR) := 'Ottmar Gobrecht';
```
@ -58,154 +60,154 @@ c_plex_author CONSTANT VARCHAR2(20 CHAR) := 'Ottmar Gobrecht';
<h2><a id="backapp"></a>Function backapp</h2>
<!------------------------------------------>
Get a file collection of an APEX application (or the current user/schema only) including:
- The app export SQL files splitted ready to use for version control and deployment
- Optional the DDL scripts for all objects and grants
- Optional the data in CSV files (this option was implemented to track catalog tables, can be used as logical backup, has the typical CSV limitations...)
- Everything in a (hopefully) nice directory structure
EXAMPLE BASIC USAGE
```sql
DECLARE
l_file_collection plex.tab_export_files;
BEGIN
l_file_collection := plex.backapp(
p_app_id => 100, -- parameter only available when APEX is installed
p_include_ords_modules => true, -- parameter only available when ORDS is installed
p_include_object_ddl => false,
p_include_data => false,
p_include_templates => false);
-- do something with the file collection
FOR i IN 1..l_file_collection.count LOOP
dbms_output.put_line(i || ' | '
|| lpad(round(length(l_file_collection(i).contents) / 1024), 3) || ' kB' || ' | '
|| l_file_collection(i).name);
END LOOP;
END;
/
```
EXAMPLE ZIP FILE PL/SQL
```sql
DECLARE
l_zip_file BLOB;
BEGIN
l_zip_file := plex.to_zip(plex.backapp(
p_app_id => 100, -- parameter only available when APEX is installed
p_include_ords_modules => true, -- parameter only available when ORDS is installed
p_include_object_ddl => true,
p_include_data => false,
p_include_templates => true));
-- do something with the zip file
-- Your code here...
END;
/
```
EXAMPLE ZIP FILE SQL
```sql
-- Inline function because of boolean parameters (needs Oracle 12c or higher).
-- Alternative create a helper function and call that in a SQL context.
WITH
FUNCTION backapp RETURN BLOB IS
BEGIN
RETURN plex.to_zip(plex.backapp(
p_app_id => 100, -- parameter only available when APEX is installed
p_include_ords_modules => true, -- parameter only available when ORDS is installed
p_include_object_ddl => true,
p_include_data => false,
p_include_templates => true));
END backapp;
SELECT backapp FROM dual;
```
EXAMPLE ZIP FILE SQL*Plus
```sql
-- SQL*Plus can only handle CLOBs, no BLOBs - so we are forced to create a CLOB
-- for spooling the content to the client disk. You need to decode the base64
-- encoded file before you are able to unzip the content. Also see this blog
-- post how to do this on different operating systems:
-- https://www.igorkromin.net/index.php/2017/04/26/base64-encode-or-decode-on-the-command-line-without-installing-extra-tools-on-linux-windows-or-macos/
-- Example Windows: certutil -decode app_100.zip.base64 app_100.zip
-- Example Mac: base64 -D -i app_100.zip.base64 -o app_100.zip
-- Example Linux: base64 -d app_100.zip.base64 > app_100.zip
set verify off feedback off heading off
set trimout on trimspool on pagesize 0 linesize 5000 long 100000000 longchunksize 32767
whenever sqlerror exit sql.sqlcode rollback
variable contents clob
BEGIN
:contents := plex.to_base64(plex.to_zip(plex.backapp(
p_app_id => 100, -- parameter only available when APEX is installed
p_include_ords_modules => true, -- parameter only available when ORDS is installed
p_include_object_ddl => true,
p_include_data => false,
p_include_templates => true)));
END;
/
set termout off
spool "app_100.zip.base64"
print contents
spool off
set termout on
Get a file collection of an APEX application (or the current user/schema only) including:
- The app export SQL files splitted ready to use for version control and deployment
- Optional the DDL scripts for all objects and grants
- Optional the data in CSV files (this option was implemented to track catalog tables, can be used as logical backup, has the typical CSV limitations...)
- Everything in a (hopefully) nice directory structure
EXAMPLE BASIC USAGE
```sql
DECLARE
l_file_collection plex.tab_export_files;
BEGIN
l_file_collection := plex.backapp(
p_app_id => 100, -- parameter only available when APEX is installed
p_include_ords_modules => true, -- parameter only available when ORDS is installed
p_include_object_ddl => false,
p_include_data => false,
p_include_templates => false);
-- do something with the file collection
FOR i IN 1..l_file_collection.count LOOP
dbms_output.put_line(i || ' | '
|| lpad(round(length(l_file_collection(i).contents) / 1024), 3) || ' kB' || ' | '
|| l_file_collection(i).name);
END LOOP;
END;
/
```
EXAMPLE ZIP FILE PL/SQL
```sql
DECLARE
l_zip_file BLOB;
BEGIN
l_zip_file := plex.to_zip(plex.backapp(
p_app_id => 100, -- parameter only available when APEX is installed
p_include_ords_modules => true, -- parameter only available when ORDS is installed
p_include_object_ddl => true,
p_include_data => false,
p_include_templates => true));
-- do something with the zip file
-- Your code here...
END;
/
```
EXAMPLE ZIP FILE SQL
```sql
-- Inline function because of boolean parameters (needs Oracle 12c or higher).
-- Alternative create a helper function and call that in a SQL context.
WITH
FUNCTION backapp RETURN BLOB IS
BEGIN
RETURN plex.to_zip(plex.backapp(
p_app_id => 100, -- parameter only available when APEX is installed
p_include_ords_modules => true, -- parameter only available when ORDS is installed
p_include_object_ddl => true,
p_include_data => false,
p_include_templates => true));
END backapp;
SELECT backapp FROM dual;
```
EXAMPLE ZIP FILE SQL*Plus
```sql
-- SQL*Plus can only handle CLOBs, no BLOBs - so we are forced to create a CLOB
-- for spooling the content to the client disk. You need to decode the base64
-- encoded file before you are able to unzip the content. Also see this blog
-- post how to do this on different operating systems:
-- https://www.igorkromin.net/index.php/2017/04/26/base64-encode-or-decode-on-the-command-line-without-installing-extra-tools-on-linux-windows-or-macos/
-- Example Windows: certutil -decode app_100.zip.base64 app_100.zip
-- Example Mac: base64 -D -i app_100.zip.base64 -o app_100.zip
-- Example Linux: base64 -d app_100.zip.base64 > app_100.zip
set verify off feedback off heading off
set trimout on trimspool on pagesize 0 linesize 5000 long 100000000 longchunksize 32767
whenever sqlerror exit sql.sqlcode rollback
variable contents clob
BEGIN
:contents := plex.to_base64(plex.to_zip(plex.backapp(
p_app_id => 100, -- parameter only available when APEX is installed
p_include_ords_modules => true, -- parameter only available when ORDS is installed
p_include_object_ddl => true,
p_include_data => false,
p_include_templates => true)));
END;
/
set termout off
spool "app_100.zip.base64"
print contents
spool off
set termout on
```
SIGNATURE
```sql
FUNCTION backapp (
$if $$apex_installed $then
-- APEX App:
p_app_id IN NUMBER DEFAULT null, -- If null, we simply skip the APEX app export.
p_app_date IN BOOLEAN DEFAULT true, -- If true, include export date and time in the result.
p_app_public_reports IN BOOLEAN DEFAULT true, -- If true, include public reports that a user saved.
p_app_private_reports IN BOOLEAN DEFAULT false, -- If true, include private reports that a user saved.
p_app_notifications IN BOOLEAN DEFAULT false, -- If true, include report notifications.
p_app_translations IN BOOLEAN DEFAULT true, -- If true, include application translation mappings and all text from the translation repository.
p_app_pkg_app_mapping IN BOOLEAN DEFAULT false, -- If true, export installed packaged applications with references to the packaged application definition. If FALSE, export them as normal applications.
p_app_original_ids IN BOOLEAN DEFAULT false, -- If true, export with the IDs as they were when the application was imported.
p_app_subscriptions IN BOOLEAN DEFAULT true, -- If true, components contain subscription references.
p_app_comments IN BOOLEAN DEFAULT true, -- If true, include developer comments.
p_app_supporting_objects IN VARCHAR2 DEFAULT null, -- If 'Y', export supporting objects. If 'I', automatically install on import. If 'N', do not export supporting objects. If null, the application's include in export deployment value is used.
p_app_include_single_file IN BOOLEAN DEFAULT false, -- If true, the single sql install file is also included beside the splitted files.
p_app_build_status_run_only IN BOOLEAN DEFAULT false, -- If true, the build status of the app will be overwritten to RUN_ONLY.
$end
$if $$ords_installed $then
-- ORDS Modules:
p_include_ords_modules IN BOOLEAN DEFAULT false, -- If true, include ORDS modules of current user/schema.
$end
-- Schema Objects:
p_include_object_ddl IN BOOLEAN DEFAULT false, -- If true, include DDL of current user/schema and all its objects.
p_object_type_like IN VARCHAR2 DEFAULT null, -- A comma separated list of like expressions to filter the objects - example: '%BODY,JAVA%' will be translated to: ... from user_objects where ... and (object_type like '%BODY' escape '\' or object_type like 'JAVA%' escape '\').
p_object_type_not_like IN VARCHAR2 DEFAULT null, -- A comma separated list of not like expressions to filter the objects - example: '%BODY,JAVA%' will be translated to: ... from user_objects where ... and (object_type not like '%BODY' escape '\' and object_type not like 'JAVA%' escape '\').
p_object_name_like IN VARCHAR2 DEFAULT null, -- A comma separated list of like expressions to filter the objects - example: 'EMP%,DEPT%' will be translated to: ... from user_objects where ... and (object_name like 'EMP%' escape '\' or object_name like 'DEPT%' escape '\').
p_object_name_not_like IN VARCHAR2 DEFAULT null, -- A comma separated list of not like expressions to filter the objects - example: 'EMP%,DEPT%' will be translated to: ... from user_objects where ... and (object_name not like 'EMP%' escape '\' and object_name not like 'DEPT%' escape '\').
p_object_view_remove_col_list IN BOOLEAN DEFAULT true, -- If true, the outer column list, added by Oracle on views during compilation, is removed
-- Table Data:
p_include_data IN BOOLEAN DEFAULT false, -- If true, include CSV data of each table.
p_data_as_of_minutes_ago IN NUMBER DEFAULT 0, -- Read consistent data with the resulting timestamp(SCN).
p_data_max_rows IN NUMBER DEFAULT 1000, -- Maximum number of rows per table.
p_data_table_name_like IN VARCHAR2 DEFAULT null, -- A comma separated list of like expressions to filter the tables - example: 'EMP%,DEPT%' will be translated to: where ... and (table_name like 'EMP%' escape '\' or table_name like 'DEPT%' escape '\').
p_data_table_name_not_like IN VARCHAR2 DEFAULT null, -- A comma separated list of not like expressions to filter the tables - example: 'EMP%,DEPT%' will be translated to: where ... and (table_name not like 'EMP%' escape '\' and table_name not like 'DEPT%' escape '\').
p_data_format IN VARCHAR2 DEFAULT 'csv', -- A comma separated list of formats - currently supported formats are CSV and INSERT - example: 'csv,insert' will export for each table a csv file and a sql file with insert statements. For insert you can also give the number of rows per "insert all" statement (defaults to 20) - example: 'csv,insert:10' or 'insert:5'.
-- General Options:
p_include_templates IN BOOLEAN DEFAULT true, -- If true, include templates for README.md, export and install scripts.
p_include_runtime_log IN BOOLEAN DEFAULT true, -- If true, generate file plex_runtime_log.md with detailed runtime infos.
p_include_error_log IN BOOLEAN DEFAULT true, -- If true, generate file plex_error_log.md with detailed error messages.
p_base_path_backend IN VARCHAR2 DEFAULT 'app_backend', -- The base path in the project root for the Schema objects.
p_base_path_frontend IN VARCHAR2 DEFAULT 'app_frontend', -- The base path in the project root for the APEX app.
p_base_path_web_services IN VARCHAR2 DEFAULT 'app_web_services', -- The base path in the project root for the ORDS modules.
p_base_path_data IN VARCHAR2 DEFAULT 'app_data', -- The base path in the project root for the table data.
p_base_path_docs IN VARCHAR2 DEFAULT 'docs', -- The base path in the project root for the docs.
p_base_path_tests IN VARCHAR2 DEFAULT 'tests', -- The base path in the project root for the tests.
p_base_path_scripts IN VARCHAR2 DEFAULT 'scripts', -- The base path in the project root for the generated deploy scripts.
p_base_path_script_logs IN VARCHAR2 DEFAULT 'scripts/logs', -- The base path in the project root for the deploy script log files.
p_scripts_working_directory IN VARCHAR2 DEFAULT 'scripts') -- The working directory of the shell (relative to the project root) where deploy scripts will be called. Set this to null if you run the deploy scripts from the project root.
FUNCTION backapp (
$if $$apex_installed $then
-- APEX App:
p_app_id IN NUMBER DEFAULT null, -- If null, we simply skip the APEX app export.
p_app_date IN BOOLEAN DEFAULT true, -- If true, include export date and time in the result.
p_app_public_reports IN BOOLEAN DEFAULT true, -- If true, include public reports that a user saved.
p_app_private_reports IN BOOLEAN DEFAULT false, -- If true, include private reports that a user saved.
p_app_notifications IN BOOLEAN DEFAULT false, -- If true, include report notifications.
p_app_translations IN BOOLEAN DEFAULT true, -- If true, include application translation mappings and all text from the translation repository.
p_app_pkg_app_mapping IN BOOLEAN DEFAULT false, -- If true, export installed packaged applications with references to the packaged application definition. If FALSE, export them as normal applications.
p_app_original_ids IN BOOLEAN DEFAULT false, -- If true, export with the IDs as they were when the application was imported.
p_app_subscriptions IN BOOLEAN DEFAULT true, -- If true, components contain subscription references.
p_app_comments IN BOOLEAN DEFAULT true, -- If true, include developer comments.
p_app_supporting_objects IN VARCHAR2 DEFAULT null, -- If 'Y', export supporting objects. If 'I', automatically install on import. If 'N', do not export supporting objects. If null, the application's include in export deployment value is used.
p_app_include_single_file IN BOOLEAN DEFAULT false, -- If true, the single sql install file is also included beside the splitted files.
p_app_build_status_run_only IN BOOLEAN DEFAULT false, -- If true, the build status of the app will be overwritten to RUN_ONLY.
$end
$if $$ords_installed $then
-- ORDS Modules:
p_include_ords_modules IN BOOLEAN DEFAULT false, -- If true, include ORDS modules of current user/schema.
$end
-- Schema Objects:
p_include_object_ddl IN BOOLEAN DEFAULT false, -- If true, include DDL of current user/schema and all its objects.
p_object_type_like IN VARCHAR2 DEFAULT null, -- A comma separated list of like expressions to filter the objects - example: '%BODY,JAVA%' will be translated to: ... from user_objects where ... and (object_type like '%BODY' escape '\' or object_type like 'JAVA%' escape '\').
p_object_type_not_like IN VARCHAR2 DEFAULT null, -- A comma separated list of not like expressions to filter the objects - example: '%BODY,JAVA%' will be translated to: ... from user_objects where ... and (object_type not like '%BODY' escape '\' and object_type not like 'JAVA%' escape '\').
p_object_name_like IN VARCHAR2 DEFAULT null, -- A comma separated list of like expressions to filter the objects - example: 'EMP%,DEPT%' will be translated to: ... from user_objects where ... and (object_name like 'EMP%' escape '\' or object_name like 'DEPT%' escape '\').
p_object_name_not_like IN VARCHAR2 DEFAULT null, -- A comma separated list of not like expressions to filter the objects - example: 'EMP%,DEPT%' will be translated to: ... from user_objects where ... and (object_name not like 'EMP%' escape '\' and object_name not like 'DEPT%' escape '\').
p_object_view_remove_col_list IN BOOLEAN DEFAULT true, -- If true, the outer column list, added by Oracle on views during compilation, is removed
-- Table Data:
p_include_data IN BOOLEAN DEFAULT false, -- If true, include CSV data of each table.
p_data_as_of_minutes_ago IN NUMBER DEFAULT 0, -- Read consistent data with the resulting timestamp(SCN).
p_data_max_rows IN NUMBER DEFAULT 1000, -- Maximum number of rows per table.
p_data_table_name_like IN VARCHAR2 DEFAULT null, -- A comma separated list of like expressions to filter the tables - example: 'EMP%,DEPT%' will be translated to: where ... and (table_name like 'EMP%' escape '\' or table_name like 'DEPT%' escape '\').
p_data_table_name_not_like IN VARCHAR2 DEFAULT null, -- A comma separated list of not like expressions to filter the tables - example: 'EMP%,DEPT%' will be translated to: where ... and (table_name not like 'EMP%' escape '\' and table_name not like 'DEPT%' escape '\').
p_data_format IN VARCHAR2 DEFAULT 'csv', -- A comma separated list of formats - currently supported formats are CSV and INSERT - example: 'csv,insert' will export for each table a csv file and a sql file with insert statements. For insert you can also give the number of rows per "insert all" statement (defaults to 20) - example: 'csv,insert:10' or 'insert:5'.
-- General Options:
p_include_templates IN BOOLEAN DEFAULT true, -- If true, include templates for README.md, export and install scripts.
p_include_runtime_log IN BOOLEAN DEFAULT true, -- If true, generate file plex_runtime_log.md with detailed runtime infos.
p_include_error_log IN BOOLEAN DEFAULT true, -- If true, generate file plex_error_log.md with detailed error messages.
p_base_path_backend IN VARCHAR2 DEFAULT 'app_backend', -- The base path in the project root for the Schema objects.
p_base_path_frontend IN VARCHAR2 DEFAULT 'app_frontend', -- The base path in the project root for the APEX app.
p_base_path_web_services IN VARCHAR2 DEFAULT 'app_web_services', -- The base path in the project root for the ORDS modules.
p_base_path_data IN VARCHAR2 DEFAULT 'app_data', -- The base path in the project root for the table data.
p_base_path_docs IN VARCHAR2 DEFAULT 'docs', -- The base path in the project root for the docs.
p_base_path_tests IN VARCHAR2 DEFAULT 'tests', -- The base path in the project root for the tests.
p_base_path_scripts IN VARCHAR2 DEFAULT 'scripts', -- The base path in the project root for the generated deploy scripts.
p_base_path_script_logs IN VARCHAR2 DEFAULT 'scripts/logs', -- The base path in the project root for the deploy script log files.
p_scripts_working_directory IN VARCHAR2 DEFAULT 'scripts') -- The working directory of the shell (relative to the project root) where deploy scripts will be called. Set this to null if you run the deploy scripts from the project root.
RETURN tab_export_files;
```
@ -213,25 +215,25 @@ RETURN tab_export_files;
<h2><a id="add_query"></a>Procedure add_query</h2>
<!----------------------------------------------->
Add a query to be processed by the method queries_to_csv. You can add as many queries as you like.
EXAMPLE
```sql
BEGIN
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
END;
/
Add a query to be processed by the method queries_to_csv. You can add as many queries as you like.
EXAMPLE
```sql
BEGIN
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
END;
/
```
SIGNATURE
```sql
PROCEDURE add_query (
p_query IN VARCHAR2, -- The query itself
p_file_name IN VARCHAR2, -- File name like 'Path/to/your/file-without-extension'.
PROCEDURE add_query (
p_query IN VARCHAR2, -- The query itself
p_file_name IN VARCHAR2, -- File name like 'Path/to/your/file-without-extension'.
p_max_rows IN NUMBER DEFAULT 1000); -- The maximum number of rows to be included in your file.
```
@ -239,118 +241,118 @@ PROCEDURE add_query (
<h2><a id="queries_to_csv"></a>Function queries_to_csv</h2>
<!-------------------------------------------------------->
Export one or more queries as CSV data within a file collection.
EXAMPLE BASIC USAGE
```sql
DECLARE
l_file_collection plex.tab_export_files;
BEGIN
--fill the queries array
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
plex.add_query(
p_query => 'select * from user_tab_columns',
p_file_name => 'user_tab_columns',
p_max_rows => 10000);
-- process the queries
l_file_collection := plex.queries_to_csv;
-- do something with the file collection
FOR i IN 1..l_file_collection.count LOOP
dbms_output.put_line(i || ' | '
|| lpad(round(length(l_file_collection(i).contents) / 1024), 3) || ' kB' || ' | '
|| l_file_collection(i).name);
END LOOP;
END;
/
```
EXAMPLE EXPORT ZIP FILE PL/SQL
```sql
DECLARE
l_zip_file BLOB;
BEGIN
--fill the queries array
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
plex.add_query(
p_query => 'select * from user_tab_columns',
p_file_name => 'user_tab_columns',
p_max_rows => 10000);
-- process the queries
l_zip_file := plex.to_zip(plex.queries_to_csv);
-- do something with the zip file
-- Your code here...
END;
/
```
EXAMPLE EXPORT ZIP FILE SQL
```sql
WITH
FUNCTION queries_to_csv_zip RETURN BLOB IS
v_return BLOB;
BEGIN
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
plex.add_query(
p_query => 'select * from user_tab_columns',
p_file_name => 'user_tab_columns',
p_max_rows => 10000);
v_return := plex.to_zip(plex.queries_to_csv);
RETURN v_return;
END queries_to_csv_zip;
SELECT queries_to_csv_zip FROM dual;
```
EXAMPLE ZIP FILE SQL*Plus
```sql
-- SQL*Plus can only handle CLOBs, no BLOBs - so we are forced to create a CLOB
-- for spooling the content to the client disk. You need to decode the base64
-- encoded file before you are able to unzip the content. Also see this blog
-- post how to do this on the different operating systems:
-- https://www.igorkromin.net/index.php/2017/04/26/base64-encode-or-decode-on-the-command-line-without-installing-extra-tools-on-linux-windows-or-macos/
-- Example Windows: certutil -decode metadata.zip.base64 metadata.zip
-- Example Mac: base64 -D -i metadata.zip.base64 -o metadata.zip
-- Example Linux: base64 -d metadata.zip.base64 > metadata.zip
set verify off feedback off heading off termout off
set trimout on trimspool on pagesize 0 linesize 5000 long 100000000 longchunksize 32767
whenever sqlerror exit sql.sqlcode rollback
variable contents clob
BEGIN
--fill the queries array
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
plex.add_query(
p_query => 'select * from user_tab_columns',
p_file_name => 'user_tab_columns',
p_max_rows => 10000);
-- process the queries
:contents := plex.to_base64(plex.to_zip(plex.queries_to_csv));
END;
/
spool "metadata.zip.base64"
print contents
spool off
Export one or more queries as CSV data within a file collection.
EXAMPLE BASIC USAGE
```sql
DECLARE
l_file_collection plex.tab_export_files;
BEGIN
--fill the queries array
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
plex.add_query(
p_query => 'select * from user_tab_columns',
p_file_name => 'user_tab_columns',
p_max_rows => 10000);
-- process the queries
l_file_collection := plex.queries_to_csv;
-- do something with the file collection
FOR i IN 1..l_file_collection.count LOOP
dbms_output.put_line(i || ' | '
|| lpad(round(length(l_file_collection(i).contents) / 1024), 3) || ' kB' || ' | '
|| l_file_collection(i).name);
END LOOP;
END;
/
```
EXAMPLE EXPORT ZIP FILE PL/SQL
```sql
DECLARE
l_zip_file BLOB;
BEGIN
--fill the queries array
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
plex.add_query(
p_query => 'select * from user_tab_columns',
p_file_name => 'user_tab_columns',
p_max_rows => 10000);
-- process the queries
l_zip_file := plex.to_zip(plex.queries_to_csv);
-- do something with the zip file
-- Your code here...
END;
/
```
EXAMPLE EXPORT ZIP FILE SQL
```sql
WITH
FUNCTION queries_to_csv_zip RETURN BLOB IS
v_return BLOB;
BEGIN
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
plex.add_query(
p_query => 'select * from user_tab_columns',
p_file_name => 'user_tab_columns',
p_max_rows => 10000);
v_return := plex.to_zip(plex.queries_to_csv);
RETURN v_return;
END queries_to_csv_zip;
SELECT queries_to_csv_zip FROM dual;
```
EXAMPLE ZIP FILE SQL*Plus
```sql
-- SQL*Plus can only handle CLOBs, no BLOBs - so we are forced to create a CLOB
-- for spooling the content to the client disk. You need to decode the base64
-- encoded file before you are able to unzip the content. Also see this blog
-- post how to do this on the different operating systems:
-- https://www.igorkromin.net/index.php/2017/04/26/base64-encode-or-decode-on-the-command-line-without-installing-extra-tools-on-linux-windows-or-macos/
-- Example Windows: certutil -decode metadata.zip.base64 metadata.zip
-- Example Mac: base64 -D -i metadata.zip.base64 -o metadata.zip
-- Example Linux: base64 -d metadata.zip.base64 > metadata.zip
set verify off feedback off heading off termout off
set trimout on trimspool on pagesize 0 linesize 5000 long 100000000 longchunksize 32767
whenever sqlerror exit sql.sqlcode rollback
variable contents clob
BEGIN
--fill the queries array
plex.add_query(
p_query => 'select * from user_tables',
p_file_name => 'user_tables');
plex.add_query(
p_query => 'select * from user_tab_columns',
p_file_name => 'user_tab_columns',
p_max_rows => 10000);
-- process the queries
:contents := plex.to_base64(plex.to_zip(plex.queries_to_csv));
END;
/
spool "metadata.zip.base64"
print contents
spool off
```
SIGNATURE
```sql
FUNCTION queries_to_csv (
p_delimiter IN VARCHAR2 DEFAULT ',', -- The column delimiter.
p_quote_mark IN VARCHAR2 DEFAULT '"', -- Used when the data contains the delimiter character.
p_header_prefix IN VARCHAR2 DEFAULT NULL, -- Prefix the header line with this text.
p_include_runtime_log IN BOOLEAN DEFAULT true, -- If true, generate file plex_runtime_log.md with runtime statistics.
p_include_error_log IN BOOLEAN DEFAULT true) -- If true, generate file plex_error_log.md with detailed error messages.
FUNCTION queries_to_csv (
p_delimiter IN VARCHAR2 DEFAULT ',', -- The column delimiter.
p_quote_mark IN VARCHAR2 DEFAULT '"', -- Used when the data contains the delimiter character.
p_header_prefix IN VARCHAR2 DEFAULT NULL, -- Prefix the header line with this text.
p_include_runtime_log IN BOOLEAN DEFAULT true, -- If true, generate file plex_runtime_log.md with runtime statistics.
p_include_error_log IN BOOLEAN DEFAULT true) -- If true, generate file plex_error_log.md with detailed error messages.
RETURN tab_export_files;
```
@ -358,26 +360,26 @@ RETURN tab_export_files;
<h2><a id="to_zip"></a>Function to_zip</h2>
<!---------------------------------------->
Convert a file collection to a zip file.
EXAMPLE
```sql
DECLARE
l_zip BLOB;
BEGIN
l_zip := plex.to_zip(plex.backapp(
p_app_id => 100,
p_include_object_ddl => true));
-- do something with the zip file...
END;
Convert a file collection to a zip file.
EXAMPLE
```sql
DECLARE
l_zip BLOB;
BEGIN
l_zip := plex.to_zip(plex.backapp(
p_app_id => 100,
p_include_object_ddl => true));
-- do something with the zip file...
END;
```
SIGNATURE
```sql
FUNCTION to_zip (
p_file_collection IN tab_export_files) -- The file collection to zip.
FUNCTION to_zip (
p_file_collection IN tab_export_files) -- The file collection to zip.
RETURN BLOB;
```
@ -385,37 +387,65 @@ RETURN BLOB;
<h2><a id="to_base64"></a>Function to_base64</h2>
<!---------------------------------------------->
Encodes a BLOB into a Base64 CLOB for transfers over a network (like with SQL*Plus). For encoding on the client side see [this blog article](https://www.igorkromin.net/index.php/2017/04/26/base64-encode-or-decode-on-the-command-line-without-installing-extra-tools-on-linux-windows-or-macos/).
```sql
DECLARE
l_clob CLOB;
BEGIN
l_clob := plex.to_base64(plex.to_zip(plex.backapp(
p_app_id => 100,
p_include_object_ddl => true)));
-- do something with the clob...
END;
Encodes a BLOB into a Base64 CLOB for transfers over a network (like with SQL*Plus). For encoding on the client side see [this blog article](https://www.igorkromin.net/index.php/2017/04/26/base64-encode-or-decode-on-the-command-line-without-installing-extra-tools-on-linux-windows-or-macos/).
```sql
DECLARE
l_clob CLOB;
BEGIN
l_clob := plex.to_base64(plex.to_zip(plex.backapp(
p_app_id => 100,
p_include_object_ddl => true)));
-- do something with the clob...
END;
```
SIGNATURE
```sql
FUNCTION to_base64(
p_blob IN BLOB) -- The BLOB to convert.
FUNCTION to_base64(
p_blob IN BLOB) -- The BLOB to convert.
RETURN CLOB;
```
<h2><a id="download"></a>Procedure download</h2>
<!--------------------------------------------->
Download a file based on a BLOB.
SIGNATURE
```sql
PROCEDURE download (
p_blob IN BLOB,
p_name IN VARCHAR2 );
```
<h2><a id="download"></a>Procedure download</h2>
<!--------------------------------------------->
Download a file based on a CLOB.
SIGNATURE
```sql
PROCEDURE download (
p_clob IN CLOB,
p_name IN VARCHAR2 );
```
<h2><a id="view_error_log"></a>Function view_error_log</h2>
<!-------------------------------------------------------->
View the error log from the last plex run. The internal array for the error log is cleared on each call of BackApp or Queries_to_CSV.
EXAMPLE
```sql
SELECT * FROM TABLE(plex.view_error_log);
View the error log from the last plex run. The internal array for the error log is cleared on each call of BackApp or Queries_to_CSV.
EXAMPLE
```sql
SELECT * FROM TABLE(plex.view_error_log);
```
SIGNATURE
@ -428,12 +458,12 @@ FUNCTION view_error_log RETURN tab_error_log PIPELINED;
<h2><a id="view_runtime_log"></a>Function view_runtime_log</h2>
<!------------------------------------------------------------>
View the runtime log from the last plex run. The internal array for the runtime log is cleared on each call of BackApp or Queries_to_CSV.
EXAMPLE
```sql
SELECT * FROM TABLE(plex.view_runtime_log);
View the runtime log from the last plex run. The internal array for the runtime log is cleared on each call of BackApp or Queries_to_CSV.
EXAMPLE
```sql
SELECT * FROM TABLE(plex.view_runtime_log);
```
SIGNATURE

1243
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,10 @@
set define off feedback off
set define off
set serveroutput on
set verify off
set feedback off
set linesize 240
set trimout on
set trimspool on
whenever sqlerror exit sql.sqlcode rollback
prompt
@ -271,7 +277,6 @@ set termout on
**/
PROCEDURE add_query (
p_query IN VARCHAR2, -- The query itself
p_file_name IN VARCHAR2, -- File name like 'Path/to/your/file-without-extension'.
@ -292,7 +297,6 @@ END;
**/
FUNCTION queries_to_csv (
p_delimiter IN VARCHAR2 DEFAULT ',', -- The column delimiter.
p_quote_mark IN VARCHAR2 DEFAULT '"', -- Used when the data contains the delimiter character.
@ -406,7 +410,6 @@ spool off
**/
FUNCTION to_zip (
p_file_collection IN tab_export_files) -- The file collection to zip.
RETURN BLOB;
@ -427,6 +430,7 @@ END;
```
**/
FUNCTION to_base64(
p_blob IN BLOB) -- The BLOB to convert.
RETURN CLOB;
@ -445,6 +449,27 @@ END;
```
**/
PROCEDURE download (
p_blob IN BLOB,
p_name IN VARCHAR2 );
/**
Download a file based on a BLOB.
**/
PROCEDURE download (
p_clob IN CLOB,
p_name IN VARCHAR2 );
/**
Download a file based on a CLOB.
**/
FUNCTION view_error_log RETURN tab_error_log PIPELINED;
/**
View the error log from the last plex run. The internal array for the error log is cleared on each call of BackApp or Queries_to_CSV.
@ -581,10 +606,13 @@ PROCEDURE util_clob_query_to_csv (
p_header_prefix IN VARCHAR2 DEFAULT NULL);
PROCEDURE util_clob_table_to_insert (
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_all_size IN NUMBER DEFAULT 10);
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_style IN VARCHAR2 DEFAULT 'INSERT', -- can be INSERT or OM_TAPIGEN
p_insert_all_size IN NUMBER DEFAULT 10,
p_exclude_columns_list IN VARCHAR2 DEFAULT NULL -- a colon separated list of columns that should be excluded for the insert operation (for example audit columns which are populated automatically) FIXME: support the auto detection of an column prefix
);
PROCEDURE util_clob_create_runtime_log (p_export_files IN OUT NOCOPY tab_export_files);
@ -846,10 +874,13 @@ PROCEDURE util_clob_query_to_csv (
p_header_prefix IN VARCHAR2 DEFAULT NULL);
PROCEDURE util_clob_table_to_insert (
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_all_size IN NUMBER DEFAULT 10);
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_style IN VARCHAR2 DEFAULT 'INSERT', -- can be INSERT or OM_TAPIGEN
p_insert_all_size IN NUMBER DEFAULT 10,
p_exclude_columns_list IN VARCHAR2 DEFAULT NULL -- a list of columns that should be excluded for the insert operation (for example audit columns which are populated automatically) FIXME: support the auto detection of an column prefix
);
PROCEDURE util_clob_create_runtime_log (p_export_files IN OUT NOCOPY tab_export_files);
@ -1595,11 +1626,15 @@ END util_clob_query_to_csv;
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE util_clob_table_to_insert (
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_all_size IN NUMBER DEFAULT 10)
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_style IN VARCHAR2 DEFAULT 'INSERT',
p_insert_all_size IN NUMBER DEFAULT 10,
p_exclude_columns_list IN VARCHAR2 DEFAULT NULL)
IS
v_insert_style VARCHAR2(30);
v_exclude_columns_list VARCHAR2(32767);
v_nls rec_nls;
v_query VARCHAR2(32767);
v_cursor PLS_INTEGER;
@ -1619,6 +1654,12 @@ IS
----------------------------------------------------------------
PROCEDURE init IS
BEGIN
v_insert_style := trim(upper(substr(p_insert_style,1,30)));
v_exclude_columns_list := trim(upper(p_exclude_columns_list)) || ':';
END;
PROCEDURE get_session_nls_params IS
BEGIN
-- Save current values.
@ -1808,6 +1849,7 @@ IS
where table_name = p_table_name
and user_generated = 'YES'
and virtual_column = 'NO'
and instr(v_exclude_columns_list, ':'||column_name||':') = 0
group by table_name
) LOOP
v_query := i.query;
@ -1829,13 +1871,20 @@ IS
ELSE
dbms_sql.define_column(v_cursor, i, v_buffer_varchar2, c_vc2_max_size);
END IF;
v_table_insert_prefix := v_table_insert_prefix || v_desc_tab(i).col_name || ',';
IF v_insert_style = 'INSERT' THEN
v_table_insert_prefix := v_table_insert_prefix || v_desc_tab(i).col_name || ',';
END IF;
END LOOP;
v_table_insert_prefix :=
case when p_insert_all_size > 0
then 'into '
else 'insert into '
end || p_table_name || '(' || rtrim(v_table_insert_prefix, ',' ) || ') values (';
CASE v_insert_style
WHEN 'INSERT' THEN
case when p_insert_all_size > 0
then 'into '
else 'insert into '
end || p_table_name || '(' || rtrim(v_table_insert_prefix, ',' ) || ') values ('
WHEN 'OM_TAPIGEN' THEN
lower(p_table_name) || '_api.create_or_update_row('
END;
v_ignore_me := dbms_sql.execute(v_cursor);
END IF;
END parse_query_and_describe_cols;
@ -1849,8 +1898,10 @@ IS
PROCEDURE create_header IS
BEGIN
util_clob_append('-- Script generated by PLEX version ' || c_plex_version || ' - more infos here: ' || c_plex_url || c_crlf);
util_clob_append('-- Performance Hacks by Connor McDonald: https://connor-mcdonald.com/2019/05/17/hacking-together-faster-inserts/' || c_crlf);
util_clob_append('-- For strange line end replacements a big thank to SQL*Plus: https://support.oracle.com/epmos/faces/DocumentDisplay?id=2377701.1 (SQL Failed With ORA-1756 In Sqlplus But Works In SQL Developer)' || c_crlf);
if v_insert_style = 'INSERT' then
util_clob_append('-- Performance Hacks by Connor McDonald: https://connor-mcdonald.com/2019/05/17/hacking-together-faster-inserts/' || c_crlf);
util_clob_append('-- For strange line end replacements a big thank to SQL*Plus: https://support.oracle.com/epmos/faces/DocumentDisplay?id=2377701.1 (SQL Failed With ORA-1756 In Sqlplus But Works In SQL Developer)' || c_crlf);
end if;
util_clob_append('prompt - insert xxx rows into ' || p_table_name || ' (exported ' || to_char(systimestamp,'YYYY-MM-DD hh24:mi:ssxff TZR') || ')' || c_crlf);
util_clob_append('set define off feedback off sqlblanklines on' || c_crlf);
util_clob_append('alter session set cursor_sharing = force;' || c_crlf);
@ -1871,12 +1922,15 @@ IS
if v_data_count = 1 then
create_header;
end if;
if p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) = 1 then
if v_insert_style = 'INSERT' and p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) = 1 then
util_clob_append('insert all' || c_crlf);
end if;
line_append(v_table_insert_prefix);
--> process row
FOR i IN 1..v_col_count LOOP
IF v_insert_style = 'OM_TAPIGEN' THEN
line_append('p_' || lower(v_desc_tab(i).col_name) || '=>');
END IF;
IF v_desc_tab(i).col_type = c_clob THEN
dbms_sql.column_value(v_cursor, i, v_buffer_clob);
process_clob_buffer;
@ -1900,15 +1954,15 @@ IS
process_varchar2_buffer('STRING');
END IF;
END IF;
if i != v_col_count then
if i != v_col_count then
line_append(',');
else
line_append(')' || case when p_insert_all_size < 1 then ';' end);
line_append(')' || case when v_insert_style = 'OM_TAPIGEN' or p_insert_all_size < 1 then ';' end);
line_flush_cache;
end if;
END LOOP;
--< end process row
if p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) = 0 then
if v_insert_style = 'INSERT' and p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) = 0 then
util_clob_append('select * from dual;' || c_crlf);
end if;
@ -1923,7 +1977,7 @@ IS
if v_data_count = 0 then
util_clob_append('Prompt Nothing to insert - there was no data in the source table ' || p_table_name || c_crlf);
else
if p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) != 0 then
if V_insert_style = 'INSERT' and p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) != 0 then
util_clob_append('select * from dual;' || c_crlf);
end if;
util_clob_append('end;' || c_crlf);
@ -1948,6 +2002,7 @@ IS
BEGIN
IF p_table_name IS NOT NULL THEN
--dbms_lob.createtemporary(v_buffer_clob, true);
init;
get_session_nls_params;
parse_query_and_describe_cols;
create_data;
@ -2955,13 +3010,17 @@ SELECT table_name,
v_file_path := v_path.to_data || '/' || v_rec.table_name || '.sql';
util_log_start(v_file_path);
util_clob_table_to_insert(
p_table_name => v_rec.table_name,
p_data_scn => v_data_scn,
p_max_rows => p_data_max_rows,
p_insert_all_size => to_number(nvl(regexp_substr(p_data_format,'insert:(\d+)',1,1,'i',1), '10')));
p_table_name => v_rec.table_name,
p_data_scn => v_data_scn,
p_max_rows => p_data_max_rows,
p_insert_style => 'INSERT',
p_insert_all_size => to_number(nvl(regexp_substr(p_data_format,':(\d+)',1,1,'i',1), '10')),
p_exclude_columns_list => regexp_substr(p_data_format,'(:\w+){1,}')
);
util_clob_add_to_export_files(
p_export_files => v_export_files,
p_name => v_file_path);
p_name => v_file_path
);
v_files.data_(v_files.data_.count + 1) := v_file_path;
util_log_stop;
EXCEPTION
@ -2969,6 +3028,31 @@ SELECT table_name,
util_log_error(v_file_path);
END;
END IF;
-- insert with table API (OM_TAPIGEN style)
IF upper(p_data_format) LIKE '%OM_TAPIGEN%' THEN
BEGIN
v_file_path := v_path.to_data || '/' || v_rec.table_name || '.api.sql';
util_log_start(v_file_path);
util_clob_table_to_insert(
p_table_name => v_rec.table_name,
p_data_scn => v_data_scn,
p_max_rows => p_data_max_rows,
p_insert_style => 'OM_TAPIGEN',
p_exclude_columns_list => regexp_substr(p_data_format,'(:\w+){1,}')
);
util_clob_add_to_export_files(
p_export_files => v_export_files,
p_name => v_file_path
);
v_files.data_(v_files.data_.count + 1) := v_file_path;
util_log_stop;
EXCEPTION
WHEN OTHERS THEN
util_log_error(v_file_path);
END;
END IF;
END LOOP;
CLOSE v_cur;
@ -3569,7 +3653,7 @@ BEGIN
$end
IF p_include_data THEN
process_data;
if upper(p_data_format) LIKE '%INSERT%' then
if upper(p_data_format) LIKE '%INSERT%' or upper(p_data_format) LIKE '%OM_TAPIGEN%' then
create_load_data_file;
end if;
END IF;
@ -3710,6 +3794,37 @@ END;
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE download (
p_blob IN BLOB,
p_name IN VARCHAR2 )
IS
-- we need to avoid PLS-00363: expression 'P_BLOB' cannot be used as an assignment target
v_blob blob := p_blob;
BEGIN
owa_util.mime_header('application/octet', false);
htp.p('Content-length:' || dbms_lob.getlength(v_blob));
htp.p('Content-Disposition: attachment; filename="' || p_name || '"');
-- the browser should not cache the file
htp.p('Cache-Control: must-revalidate, max-age=0');
htp.p('Expires: Thu, 01 Jan 1970 01:00:00 CET');
owa_util.http_header_close;
wpg_docload.download_file(v_blob);
END download;
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE download (
p_clob IN CLOB,
p_name IN VARCHAR2 )
IS
BEGIN
download(util_clob_to_blob(p_clob), p_name);
END download;
--------------------------------------------------------------------------------------------------------------------------------
FUNCTION view_error_log RETURN tab_error_log PIPELINED IS
BEGIN
FOR i IN 1..g_errlog.count LOOP

View File

@ -1,31 +1,37 @@
set define off feedback off
set define off
set serveroutput on
set verify off
set feedback off
set linesize 240
set trimout on
set trimspool on
whenever sqlerror exit sql.sqlcode rollback
prompt
prompt Uninstalling PL/SQL Export Utilities
prompt ====================================
prompt ============================================================
prompt Drop package plex if exists (body)
BEGIN
FOR i IN (SELECT object_type,
begin
for i in (select object_type,
object_name
FROM user_objects
WHERE object_type = 'PACKAGE BODY'
AND object_name = 'PLEX') LOOP
EXECUTE IMMEDIATE 'DROP ' || i.object_type || ' ' || i.object_name;
END LOOP;
END;
from user_objects
where object_type = 'PACKAGE body'
and object_name = 'PLEX') loop
execute immediate 'drop ' || i.object_type || ' ' || i.object_name;
end loop;
end;
/
prompt Drop package plex if exists (spec)
BEGIN
FOR i IN (SELECT object_type,
begin
for i in (select object_type,
object_name
FROM user_objects
WHERE object_type = 'PACKAGE'
AND object_name = 'PLEX') LOOP
EXECUTE IMMEDIATE 'DROP ' || i.object_type || ' ' || i.object_name;
END LOOP;
END;
from user_objects
where object_type = 'PACKAGE'
and object_name = 'PLEX') loop
execute immediate 'drop ' || i.object_type || ' ' || i.object_name;
end loop;
end;
/
prompt ====================================
prompt ============================================================
prompt Uninstallation Done
prompt
prompt

View File

@ -228,10 +228,13 @@ PROCEDURE util_clob_query_to_csv (
p_header_prefix IN VARCHAR2 DEFAULT NULL);
PROCEDURE util_clob_table_to_insert (
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_all_size IN NUMBER DEFAULT 10);
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_style IN VARCHAR2 DEFAULT 'INSERT', -- can be INSERT or OM_TAPIGEN
p_insert_all_size IN NUMBER DEFAULT 10,
p_exclude_columns_list IN VARCHAR2 DEFAULT NULL -- a list of columns that should be excluded for the insert operation (for example audit columns which are populated automatically) FIXME: support the auto detection of an column prefix
);
PROCEDURE util_clob_create_runtime_log (p_export_files IN OUT NOCOPY tab_export_files);
@ -977,11 +980,15 @@ END util_clob_query_to_csv;
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE util_clob_table_to_insert (
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_all_size IN NUMBER DEFAULT 10)
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_style IN VARCHAR2 DEFAULT 'INSERT',
p_insert_all_size IN NUMBER DEFAULT 10,
p_exclude_columns_list IN VARCHAR2 DEFAULT NULL)
IS
v_insert_style VARCHAR2(30);
v_exclude_columns_list VARCHAR2(32767);
v_nls rec_nls;
v_query VARCHAR2(32767);
v_cursor PLS_INTEGER;
@ -1001,6 +1008,12 @@ IS
----------------------------------------------------------------
PROCEDURE init IS
BEGIN
v_insert_style := trim(upper(substr(p_insert_style,1,30)));
v_exclude_columns_list := trim(upper(p_exclude_columns_list)) || ':';
END;
PROCEDURE get_session_nls_params IS
BEGIN
-- Save current values.
@ -1190,6 +1203,7 @@ IS
where table_name = p_table_name
and user_generated = 'YES'
and virtual_column = 'NO'
and instr(v_exclude_columns_list, ':'||column_name||':') = 0
group by table_name
) LOOP
v_query := i.query;
@ -1211,13 +1225,20 @@ IS
ELSE
dbms_sql.define_column(v_cursor, i, v_buffer_varchar2, c_vc2_max_size);
END IF;
v_table_insert_prefix := v_table_insert_prefix || v_desc_tab(i).col_name || ',';
IF v_insert_style = 'INSERT' THEN
v_table_insert_prefix := v_table_insert_prefix || v_desc_tab(i).col_name || ',';
END IF;
END LOOP;
v_table_insert_prefix :=
case when p_insert_all_size > 0
then 'into '
else 'insert into '
end || p_table_name || '(' || rtrim(v_table_insert_prefix, ',' ) || ') values (';
CASE v_insert_style
WHEN 'INSERT' THEN
case when p_insert_all_size > 0
then 'into '
else 'insert into '
end || p_table_name || '(' || rtrim(v_table_insert_prefix, ',' ) || ') values ('
WHEN 'OM_TAPIGEN' THEN
lower(p_table_name) || '_api.create_or_update_row('
END;
v_ignore_me := dbms_sql.execute(v_cursor);
END IF;
END parse_query_and_describe_cols;
@ -1231,8 +1252,10 @@ IS
PROCEDURE create_header IS
BEGIN
util_clob_append('-- Script generated by PLEX version ' || c_plex_version || ' - more infos here: ' || c_plex_url || c_crlf);
util_clob_append('-- Performance Hacks by Connor McDonald: https://connor-mcdonald.com/2019/05/17/hacking-together-faster-inserts/' || c_crlf);
util_clob_append('-- For strange line end replacements a big thank to SQL*Plus: https://support.oracle.com/epmos/faces/DocumentDisplay?id=2377701.1 (SQL Failed With ORA-1756 In Sqlplus But Works In SQL Developer)' || c_crlf);
if v_insert_style = 'INSERT' then
util_clob_append('-- Performance Hacks by Connor McDonald: https://connor-mcdonald.com/2019/05/17/hacking-together-faster-inserts/' || c_crlf);
util_clob_append('-- For strange line end replacements a big thank to SQL*Plus: https://support.oracle.com/epmos/faces/DocumentDisplay?id=2377701.1 (SQL Failed With ORA-1756 In Sqlplus But Works In SQL Developer)' || c_crlf);
end if;
util_clob_append('prompt - insert xxx rows into ' || p_table_name || ' (exported ' || to_char(systimestamp,'YYYY-MM-DD hh24:mi:ssxff TZR') || ')' || c_crlf);
util_clob_append('set define off feedback off sqlblanklines on' || c_crlf);
util_clob_append('alter session set cursor_sharing = force;' || c_crlf);
@ -1253,12 +1276,15 @@ IS
if v_data_count = 1 then
create_header;
end if;
if p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) = 1 then
if v_insert_style = 'INSERT' and p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) = 1 then
util_clob_append('insert all' || c_crlf);
end if;
line_append(v_table_insert_prefix);
--> process row
FOR i IN 1..v_col_count LOOP
IF v_insert_style = 'OM_TAPIGEN' THEN
line_append('p_' || lower(v_desc_tab(i).col_name) || '=>');
END IF;
IF v_desc_tab(i).col_type = c_clob THEN
dbms_sql.column_value(v_cursor, i, v_buffer_clob);
process_clob_buffer;
@ -1282,15 +1308,15 @@ IS
process_varchar2_buffer('STRING');
END IF;
END IF;
if i != v_col_count then
if i != v_col_count then
line_append(',');
else
line_append(')' || case when p_insert_all_size < 1 then ';' end);
line_append(')' || case when v_insert_style = 'OM_TAPIGEN' or p_insert_all_size < 1 then ';' end);
line_flush_cache;
end if;
END LOOP;
--< end process row
if p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) = 0 then
if v_insert_style = 'INSERT' and p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) = 0 then
util_clob_append('select * from dual;' || c_crlf);
end if;
@ -1305,7 +1331,7 @@ IS
if v_data_count = 0 then
util_clob_append('Prompt Nothing to insert - there was no data in the source table ' || p_table_name || c_crlf);
else
if p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) != 0 then
if V_insert_style = 'INSERT' and p_insert_all_size > 0 and mod(v_data_count, p_insert_all_size) != 0 then
util_clob_append('select * from dual;' || c_crlf);
end if;
util_clob_append('end;' || c_crlf);
@ -1330,6 +1356,7 @@ IS
BEGIN
IF p_table_name IS NOT NULL THEN
--dbms_lob.createtemporary(v_buffer_clob, true);
init;
get_session_nls_params;
parse_query_and_describe_cols;
create_data;
@ -2337,13 +2364,17 @@ SELECT table_name,
v_file_path := v_path.to_data || '/' || v_rec.table_name || '.sql';
util_log_start(v_file_path);
util_clob_table_to_insert(
p_table_name => v_rec.table_name,
p_data_scn => v_data_scn,
p_max_rows => p_data_max_rows,
p_insert_all_size => to_number(nvl(regexp_substr(p_data_format,'insert:(\d+)',1,1,'i',1), '10')));
p_table_name => v_rec.table_name,
p_data_scn => v_data_scn,
p_max_rows => p_data_max_rows,
p_insert_style => 'INSERT',
p_insert_all_size => to_number(nvl(regexp_substr(p_data_format,':(\d+)',1,1,'i',1), '10')),
p_exclude_columns_list => regexp_substr(p_data_format,'(:\w+){1,}')
);
util_clob_add_to_export_files(
p_export_files => v_export_files,
p_name => v_file_path);
p_name => v_file_path
);
v_files.data_(v_files.data_.count + 1) := v_file_path;
util_log_stop;
EXCEPTION
@ -2351,6 +2382,31 @@ SELECT table_name,
util_log_error(v_file_path);
END;
END IF;
-- insert with table API (OM_TAPIGEN style)
IF upper(p_data_format) LIKE '%OM_TAPIGEN%' THEN
BEGIN
v_file_path := v_path.to_data || '/' || v_rec.table_name || '.api.sql';
util_log_start(v_file_path);
util_clob_table_to_insert(
p_table_name => v_rec.table_name,
p_data_scn => v_data_scn,
p_max_rows => p_data_max_rows,
p_insert_style => 'OM_TAPIGEN',
p_exclude_columns_list => regexp_substr(p_data_format,'(:\w+){1,}')
);
util_clob_add_to_export_files(
p_export_files => v_export_files,
p_name => v_file_path
);
v_files.data_(v_files.data_.count + 1) := v_file_path;
util_log_stop;
EXCEPTION
WHEN OTHERS THEN
util_log_error(v_file_path);
END;
END IF;
END LOOP;
CLOSE v_cur;
@ -2951,7 +3007,7 @@ BEGIN
$end
IF p_include_data THEN
process_data;
if upper(p_data_format) LIKE '%INSERT%' then
if upper(p_data_format) LIKE '%INSERT%' or upper(p_data_format) LIKE '%OM_TAPIGEN%' then
create_load_data_file;
end if;
END IF;
@ -3092,6 +3148,37 @@ END;
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE download (
p_blob IN BLOB,
p_name IN VARCHAR2 )
IS
-- we need to avoid PLS-00363: expression 'P_BLOB' cannot be used as an assignment target
v_blob blob := p_blob;
BEGIN
owa_util.mime_header('application/octet', false);
htp.p('Content-length:' || dbms_lob.getlength(v_blob));
htp.p('Content-Disposition: attachment; filename="' || p_name || '"');
-- the browser should not cache the file
htp.p('Cache-Control: must-revalidate, max-age=0');
htp.p('Expires: Thu, 01 Jan 1970 01:00:00 CET');
owa_util.http_header_close;
wpg_docload.download_file(v_blob);
END download;
--------------------------------------------------------------------------------------------------------------------------------
PROCEDURE download (
p_clob IN CLOB,
p_name IN VARCHAR2 )
IS
BEGIN
download(util_clob_to_blob(p_clob), p_name);
END download;
--------------------------------------------------------------------------------------------------------------------------------
FUNCTION view_error_log RETURN tab_error_log PIPELINED IS
BEGIN
FOR i IN 1..g_errlog.count LOOP

View File

@ -224,7 +224,6 @@ set termout on
**/
PROCEDURE add_query (
p_query IN VARCHAR2, -- The query itself
p_file_name IN VARCHAR2, -- File name like 'Path/to/your/file-without-extension'.
@ -245,7 +244,6 @@ END;
**/
FUNCTION queries_to_csv (
p_delimiter IN VARCHAR2 DEFAULT ',', -- The column delimiter.
p_quote_mark IN VARCHAR2 DEFAULT '"', -- Used when the data contains the delimiter character.
@ -359,7 +357,6 @@ spool off
**/
FUNCTION to_zip (
p_file_collection IN tab_export_files) -- The file collection to zip.
RETURN BLOB;
@ -380,6 +377,7 @@ END;
```
**/
FUNCTION to_base64(
p_blob IN BLOB) -- The BLOB to convert.
RETURN CLOB;
@ -398,6 +396,27 @@ END;
```
**/
PROCEDURE download (
p_blob IN BLOB,
p_name IN VARCHAR2 );
/**
Download a file based on a BLOB.
**/
PROCEDURE download (
p_clob IN CLOB,
p_name IN VARCHAR2 );
/**
Download a file based on a CLOB.
**/
FUNCTION view_error_log RETURN tab_error_log PIPELINED;
/**
View the error log from the last plex run. The internal array for the error log is cleared on each call of BackApp or Queries_to_CSV.
@ -534,10 +553,13 @@ PROCEDURE util_clob_query_to_csv (
p_header_prefix IN VARCHAR2 DEFAULT NULL);
PROCEDURE util_clob_table_to_insert (
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_all_size IN NUMBER DEFAULT 10);
p_table_name IN VARCHAR2,
p_data_scn IN NUMBER,
p_max_rows IN NUMBER DEFAULT 1000,
p_insert_style IN VARCHAR2 DEFAULT 'INSERT', -- can be INSERT or OM_TAPIGEN
p_insert_all_size IN NUMBER DEFAULT 10,
p_exclude_columns_list IN VARCHAR2 DEFAULT NULL -- a colon separated list of columns that should be excluded for the insert operation (for example audit columns which are populated automatically) FIXME: support the auto detection of an column prefix
);
PROCEDURE util_clob_create_runtime_log (p_export_files IN OUT NOCOPY tab_export_files);

View File

@ -3,14 +3,14 @@ var fs = require('fs');
fs.writeFileSync(
'plex_install.sql',
fs.readFileSync('src/plex_install.sql', 'utf8')
.replace('@plex.pks', function(){return fs.readFileSync('src/plex.pks', 'utf8')})
.replace('@plex.pkb', function(){return fs.readFileSync('src/plex.pkb', 'utf8')})
.replace('@PLEX.pks', function(){return fs.readFileSync('src/PLEX.pks', 'utf8')})
.replace('@PLEX.pkb', function(){return fs.readFileSync('src/PLEX.pkb', 'utf8')})
// Read what this function thing is doing, without it we get wrong results.
// We have dollar signs in our package body text - the last answer explains:
// https://stackoverflow.com/questions/9423722/string-replace-weird-behavior-when-using-dollar-sign-as-replacement
);
fs.copyFileSync(
'src/plex_uninstall.sql',
'src/plex_uninstall.sql',
'plex_uninstall.sql'
);

View File

@ -1,4 +1,10 @@
set define off feedback off
set define off
set serveroutput on
set verify off
set feedback off
set linesize 240
set trimout on
set trimspool on
whenever sqlerror exit sql.sqlcode rollback
prompt
@ -45,11 +51,11 @@ END;
/
prompt Compile package plex (spec)
@plex.pks
@PLEX.pks
show errors
prompt Compile package plex (body)
@plex.pkb
@PLEX.pkb
show errors
prompt ============================================================

View File

@ -1,31 +1,37 @@
set define off feedback off
set define off
set serveroutput on
set verify off
set feedback off
set linesize 240
set trimout on
set trimspool on
whenever sqlerror exit sql.sqlcode rollback
prompt
prompt Uninstalling PL/SQL Export Utilities
prompt ====================================
prompt ============================================================
prompt Drop package plex if exists (body)
BEGIN
FOR i IN (SELECT object_type,
begin
for i in (select object_type,
object_name
FROM user_objects
WHERE object_type = 'PACKAGE BODY'
AND object_name = 'PLEX') LOOP
EXECUTE IMMEDIATE 'DROP ' || i.object_type || ' ' || i.object_name;
END LOOP;
END;
from user_objects
where object_type = 'PACKAGE body'
and object_name = 'PLEX') loop
execute immediate 'drop ' || i.object_type || ' ' || i.object_name;
end loop;
end;
/
prompt Drop package plex if exists (spec)
BEGIN
FOR i IN (SELECT object_type,
begin
for i in (select object_type,
object_name
FROM user_objects
WHERE object_type = 'PACKAGE'
AND object_name = 'PLEX') LOOP
EXECUTE IMMEDIATE 'DROP ' || i.object_type || ' ' || i.object_name;
END LOOP;
END;
from user_objects
where object_type = 'PACKAGE'
and object_name = 'PLEX') loop
execute immediate 'drop ' || i.object_type || ' ' || i.object_name;
end loop;
end;
/
prompt ====================================
prompt ============================================================
prompt Uninstallation Done
prompt
prompt