diff --git a/README.md b/README.md
index 42dadd3..c3b44c0 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# alexandria-plsql-utils
Oracle PL/SQL Utility Library
-
+
This library is a collection of various utility packages for PL/SQL, as well as links to useful libraries hosted and maintained elsewhere.
@@ -9,8 +9,8 @@ This library is a collection of various utility packages for PL/SQL, as well as
##Generate PDF files using PL/SQL
- * http://technology.amis.nl/2012/04/11/generating-a-pdf-document-with-some-plsql-as_pdf_mini-as_pdf3/
- * http://technology.amis.nl/blog/8650/as_pdf-generating-a-pdf-document-with-some-plsql
+ * https://technology.amis.nl/2012/04/11/generating-a-pdf-document-with-some-plsql-as_pdf_mini-as_pdf3/
+ * https://technology.amis.nl/2010/10/20/as_pdf-generating-a-pdf-document-with-some-plsql/
* http://www.erasme.org/PL-FPDF,1337?lang=en
* http://ora-00001.blogspot.com/2009/10/free-pdf-package-for-plsql.html
* http://sourceforge.net/projects/pljrxml2pdf/
@@ -21,7 +21,7 @@ This library is a collection of various utility packages for PL/SQL, as well as
##Generate Excel files using PL/SQL
- * http://technology.amis.nl/blog/10995/create-an-excel-file-with-plsql
+ * https://technology.amis.nl/2011/02/19/create-an-excel-file-with-plsql/
* http://www.jasonsdevelopercorner.com/?page_id=8
* https://xml-spreadsheet.samplecode.oracle.com/
* http://sanjeev-oracle-world.blogspot.com/2007/06/create-excel-workbook-by-plsql-code.html
@@ -39,7 +39,7 @@ This library is a collection of various utility packages for PL/SQL, as well as



* http://ora-00001.blogspot.com/2011/02/working-with-office-2007-ooxml-files.html
- * http://technology.amis.nl/blog/8090/parsing-a-microsoft-word-docx-and-unzip-zipfiles-with-plsql
+ * https://technology.amis.nl/2010/06/09/parsing-a-microsoft-word-docx-and-unzip-zipfiles-with-plsql/
* http://www.docufy.be/
* OOXML_UTIL_PKG
@@ -47,7 +47,7 @@ This library is a collection of various utility packages for PL/SQL, as well as

- * http://technology.amis.nl/blog/7626/utl_compress-gzip-and-zlib
+ * https://technology.amis.nl/2010/03/13/utl_compress-gzip-and-zlib/
* ZIP_UTIL_PKG
##Generate and parse CSV files using PL/SQL
@@ -250,8 +250,8 @@ This library is a collection of various utility packages for PL/SQL, as well as
##Miscellaneous utilities and demos
- * http://www.toadworld.com/Portals/0/stevenf/demo.zip
- * http://www.toadworld.com/ORACLE/StevenFeuersteinsPLSQLObsession/tabid/153/Default.aspx
+ * http://www.oracle.com/webfolder/technetwork/tutorials/plsql/sfdemo.zip
+ * http://www.toadworld.com/platforms/oracle/w/wiki/8243.plsql-obsession
##PL/SQL Frameworks
@@ -264,7 +264,7 @@ This library is a collection of various utility packages for PL/SQL, as well as
##Unit Testing Frameworks for PL/SQL
- * http://sourceforge.net/projects/utplsql/
+ * https://utplsql.github.io/ (version 3 beta)
* http://code.google.com/p/pluto-test-framework/
##Documentation Generators (JavaDoc-style)
diff --git a/alexandria-logo.jpg b/alexandria-logo.jpg
new file mode 100644
index 0000000..ae52707
Binary files /dev/null and b/alexandria-logo.jpg differ
diff --git a/demos/xlsx_builder_pkg_demo.sql b/demos/xlsx_builder_pkg_demo.sql
index 712cd52..cfd9e99 100755
--- a/demos/xlsx_builder_pkg_demo.sql
+++ b/demos/xlsx_builder_pkg_demo.sql
@@ -1,12 +1,13 @@
-- see http://technology.amis.nl/blog/10995/create-an-excel-file-with-plsql
begin
+ xlsx_builder_pkg.clear_workbook;
xlsx_builder_pkg.new_sheet;
xlsx_builder_pkg.cell( 5, 1, 5 );
xlsx_builder_pkg.cell( 3, 1, 3 );
xlsx_builder_pkg.cell( 2, 2, 45 );
xlsx_builder_pkg.cell( 3, 2, 'Anton Scheffer', p_alignment => xlsx_builder_pkg.get_alignment( p_wraptext => true ) );
- xlsx_builder_pkg.cell( 1, 4, sysdate );
+ xlsx_builder_pkg.cell( 1, 4, sysdate, p_fontId => xlsx_builder_pkg.get_font( 'Calibri', p_rgb => 'FFFF0000' ) );
xlsx_builder_pkg.cell( 2, 4, sysdate, p_numFmtId => xlsx_builder_pkg.get_numFmt( 'dd/mm/yyyy h:mm' ) );
xlsx_builder_pkg.cell( 3, 4, sysdate, p_numFmtId => xlsx_builder_pkg.get_numFmt( xlsx_builder_pkg.orafmt2excel( 'dd/mon/yyyy' ) ) );
xlsx_builder_pkg.cell( 5, 5, 75, p_borderId => xlsx_builder_pkg.get_border( 'double', 'double', 'double', 'double' ) );
@@ -33,5 +34,71 @@ begin
end;
/
+-- Create an Excel file with validation
+begin
+ xlsx_builder_pkg.clear_workbook;
+ xlsx_builder_pkg.new_sheet;
+ xlsx_builder_pkg.cell( 1, 6, 5 );
+ xlsx_builder_pkg.cell( 1, 7, 3 );
+ xlsx_builder_pkg.cell( 1, 8, 7 );
+ xlsx_builder_pkg.new_sheet;
+ xlsx_builder_pkg.cell( 2, 6, 15, p_sheet => 2 );
+ xlsx_builder_pkg.cell( 2, 7, 13, p_sheet => 2 );
+ xlsx_builder_pkg.cell( 2, 8, 17, p_sheet => 2 );
+ xlsx_builder_pkg.list_validation( 6, 3, 1, 6, 1, 8, p_show_error => true, p_sheet => 1 );
+ xlsx_builder_pkg.defined_name( 2, 6, 2, 8, 'Anton', 2 );
+ xlsx_builder_pkg.list_validation
+ ( 6, 1, 'Anton'
+ , p_style => 'information'
+ , p_title => 'valid values are'
+ , p_prompt => '13, 15 and 17'
+ , p_show_error => true
+ , p_error_title => 'Are you sure?'
+ , p_error_txt => 'Valid values are: 13, 15 and 17'
+ , p_sheet => 1 );
+ xlsx_builder_pkg.save( 'MY_DIR', 'my.xlsx' );
+end;
+/
+begin
+ xlsx_builder_pkg.clear_workbook;
+ xlsx_builder_pkg.new_sheet;
+ xlsx_builder_pkg.cell( 1, 6, 5 );
+ xlsx_builder_pkg.cell( 1, 7, 3 );
+ xlsx_builder_pkg.cell( 1, 8, 7 );
+ xlsx_builder_pkg.set_autofilter( 1,1, p_row_start => 5, p_row_end => 8 );
+ xlsx_builder_pkg.new_sheet;
+ xlsx_builder_pkg.cell( 2, 6, 5 );
+ xlsx_builder_pkg.cell( 2, 7, 3 );
+ xlsx_builder_pkg.cell( 2, 8, 7 );
+ xlsx_builder_pkg.set_autofilter( 2,2, p_row_start => 5, p_row_end => 8 );
+ xlsx_builder_pkg.save( 'MY_DIR', 'my.xlsx' );
+end;
+/
+
+-- Create workbook with frozen cells
+begin
+ xlsx_builder_pkg.clear_workbook;
+ xlsx_builder_pkg.new_sheet;
+ for c in 1 .. 10
+ loop
+ xlsx_builder_pkg.cell( c, 1, 'COL' || c );
+ xlsx_builder_pkg.cell( c, 2, 'val' || c );
+ xlsx_builder_pkg.cell( c, 3, c );
+ end loop;
+ xlsx_builder_pkg.freeze_rows( 1 );
+ xlsx_builder_pkg.new_sheet;
+ for r in 1 .. 10
+ loop
+ xlsx_builder_pkg.cell( 1, r, 'ROW' || r );
+ xlsx_builder_pkg.cell( 2, r, 'val' || r );
+ xlsx_builder_pkg.cell( 3, r, r );
+ end loop;
+ xlsx_builder_pkg.freeze_cols( 3 );
+ xlsx_builder_pkg.new_sheet;
+ xlsx_builder_pkg.cell( 3, 3, 'Start freeze' );
+ xlsx_builder_pkg.freeze_pane( 3,3 );
+ xlsx_builder_pkg.save( 'MY_DIR', 'my.xlsx' );
+end;
+/
diff --git a/doc/changelog.txt b/doc/changelog.txt
index 87da56e..d3c0253 100755
--- a/doc/changelog.txt
+++ b/doc/changelog.txt
@@ -1,4 +1,6 @@
+NOTE: This changelog is no longer being maintained. See the Git changelog for an up-to-date list of changes.
+
Version 1.7.1
=============
diff --git a/extras/hash_util_pkg.pkb b/extras/hash_util_pkg.pkb
new file mode 100644
index 0000000..ae28d6b
--- /dev/null
+++ b/extras/hash_util_pkg.pkb
@@ -0,0 +1,1390 @@
+create or replace package body hash_util_pkg
+as
+
+ type ta_number is table of number index by binary_integer;
+
+ type tr_ctx is record (
+ h ta_number,
+ total_length number,
+ leftover_buffer raw(256),
+ leftover_buffer_length number,
+ words_array ta_number
+ );
+
+ m_ctx tr_ctx;
+ m_k ta_number;
+ m_result ta_number;
+
+ -- Constants for message padding
+ c_bits_00 raw(1) := hextoraw('00');
+ c_bits_80 raw(1) := hextoraw('80');
+
+ -- Constant for 32bit bitwise operations
+ c_bits_80000000 number := to_number('80000000','xxxxxxxx');
+ c_bits_ffffffc0 number := to_number('FFFFFFC0','xxxxxxxx');
+ c_bits_ffffffff number := to_number('FFFFFFFF','xxxxxxxx');
+
+ -- Constant for 64bit bitwise operations
+ c_bits_8000000000000000 number := to_number('8000000000000000','xxxxxxxxxxxxxxxx');
+ c_bits_ffffffffffffff80 number := to_number('FFFFFFFFFFFFFF80','xxxxxxxxxxxxxxxx');
+ c_bits_ffffffffffffffff number := to_number('FFFFFFFFFFFFFFFF','xxxxxxxxxxxxxxxx');
+
+ ---
+ --- Bitwise operators
+ ---
+
+ function bitor(x in number, y in number) return number as
+ begin
+ return (x + y - bitand(x, y));
+ end;
+
+ function bitxor(x in number, y in number) return number as
+ begin
+ return bitor(x, y) - bitand(x, y);
+ end;
+
+ function bitnot32(x in number) return number as
+ begin
+ return c_bits_ffffffff - x;
+ end;
+
+ function leftshift32(x in number, y in number) return number as
+ tmp number := x;
+ begin
+ for idx in 1..y
+ loop
+ tmp := tmp * 2;
+ end loop;
+ return bitand(tmp, c_bits_ffffffff);
+ end;
+
+ function rightshift32(x in number, y in number) return number as
+ tmp number := x;
+ begin
+ for idx in 1..y
+ loop
+ tmp := trunc(tmp / 2);
+ end loop;
+ return bitand(tmp, c_bits_ffffffff);
+ end;
+
+ function cyclic32(x in number, y in number) return number as
+ begin
+ return bitor(rightshift32(x, y), leftshift32(x, 32-y));
+ end;
+
+ function bitnot64(x in number) return number as
+ begin
+ return c_bits_ffffffffffffffff - x;
+ end;
+
+ function leftshift64(x in number, y in number) return number as
+ tmp number := x;
+ begin
+ for idx in 1..y
+ loop
+ tmp := tmp * 2;
+ end loop;
+ return bitand(tmp, c_bits_ffffffffffffffff);
+ end;
+
+ function rightshift64(x in number, y in number) return number as
+ tmp number := x;
+ begin
+ for idx in 1..y
+ loop
+ tmp := trunc(tmp / 2);
+ end loop;
+ return bitand(tmp, c_bits_ffffffffffffffff);
+ end;
+
+ function cyclic64(x in number, y in number) return number as
+ begin
+ return bitor(rightshift64(x, y), leftshift64(x, 64-y));
+ end;
+
+ ---
+ --- Operators defined in FIPS 180-2:4.1.2.
+ ---
+
+ function op_maj(x in number, y in number, z in number) return number as
+ begin
+ return bitxor(bitxor(bitand(x,y), bitand(x,z)), bitand(y,z));
+ end;
+
+ function op_ch_32(x in number, y in number, z in number) return number as
+ begin
+ return bitxor(bitand(x, y), bitand(bitnot32(x), z));
+ end;
+
+ function op_s0_32(x in number) return number as
+ begin
+ return bitxor(bitxor(cyclic32(x, 2), cyclic32(x, 13)), cyclic32(x, 22));
+ end;
+
+ function op_s1_32(x in number) return number as
+ begin
+ return bitxor(bitxor(cyclic32(x, 6), cyclic32(x, 11)), cyclic32(x, 25));
+ end;
+
+ function op_r0_32(x in number) return number as
+ begin
+ return bitxor(bitxor(cyclic32(x, 7), cyclic32(x, 18)), rightshift32(x, 3));
+ end;
+
+ function op_r1_32(x in number) return number as
+ begin
+ return bitxor(bitxor(cyclic32(x, 17), cyclic32(x, 19)), rightshift32(x, 10));
+ end;
+
+ function op_ch_64(x in number, y in number, z in number) return number as
+ begin
+ return bitxor(bitand(x, y), bitand(bitnot64(x), z));
+ end;
+
+ function op_s0_64(x in number) return number as
+ begin
+ return bitxor(bitxor(cyclic64(x, 28), cyclic64(x, 34)), cyclic64(x, 39));
+ end;
+
+ function op_s1_64(x in number) return number as
+ begin
+ return bitxor(bitxor(cyclic64(x, 14), cyclic64(x, 18)), cyclic64(x, 41));
+ end;
+
+ function op_r0_64(x in number) return number as
+ begin
+ return bitxor(bitxor(cyclic64(x, 1), cyclic64(x, 8)), rightshift64(x, 7));
+ end;
+
+ function op_r1_64(x in number) return number as
+ begin
+ return bitxor(bitxor(cyclic64(x, 19), cyclic64(x, 61)), rightshift64(x, 6));
+ end;
+
+ --
+ -- SHA-1
+ --
+
+ procedure sha1_init_ctx
+ as
+ begin
+ m_ctx.h(0) := to_number('67452301', 'xxxxxxxx');
+ m_ctx.h(1) := to_number('efcdab89', 'xxxxxxxx');
+ m_ctx.h(2) := to_number('98badcfe', 'xxxxxxxx');
+ m_ctx.h(3) := to_number('10325476', 'xxxxxxxx');
+ m_ctx.h(4) := to_number('c3d2e1f0', 'xxxxxxxx');
+ m_ctx.total_length := 0;
+ m_ctx.leftover_buffer := null;
+ m_ctx.leftover_buffer_length := 0;
+ for idx in 0..15 loop
+ m_ctx.words_array(idx) := 0;
+ end loop;
+ end sha1_init_ctx;
+
+ procedure sha1_process_block(p_words_array in ta_number,
+ p_words_count in number)
+ as
+ l_words_array ta_number := p_words_array;
+ l_words_count number := p_words_count;
+ l_words_idx number;
+ t number;
+ a number := m_ctx.h(0);
+ b number := m_ctx.h(1);
+ c number := m_ctx.h(2);
+ d number := m_ctx.h(3);
+ e number := m_ctx.h(4);
+ w ta_number;
+ a_save number;
+ b_save number;
+ c_save number;
+ d_save number;
+ e_save number;
+ f number;
+ k number;
+ temp number;
+ begin
+ -- Process all bytes in the buffer with 64 bytes in each round of the loop.
+ l_words_idx := 0;
+ while (l_words_count > 0) loop
+ a_save := a;
+ b_save := b;
+ c_save := c;
+ d_save := d;
+ e_save := e;
+ for t in 0..15 loop
+ w(t) := l_words_array(l_words_idx);
+ l_words_idx := l_words_idx + 1;
+ end loop;
+ for t in 16..79 loop
+ w(t) := cyclic32(bitxor(bitxor(bitxor(w(t-3), w(t-8)), w(t-14)), w(t-16)), 32-1);
+ end loop;
+ for t in 0..79 loop
+ if t between 0 and 19 then
+ f := bitor(bitand(b, c), bitand(bitnot32(b), d));
+ k := to_number('5a827999', 'xxxxxxxx');
+ elsif t between 20 and 39 then
+ f := bitxor(bitxor(b, c), d);
+ k := to_number('6ed9eba1', 'xxxxxxxx');
+ elsif t between 40 and 59 then
+ f := bitor(bitor(bitand(b, c), bitand(b, d)), bitand(c, d));
+ k := to_number('8f1bbcdc', 'xxxxxxxx');
+ elsif t between 60 and 79 then
+ f := bitxor(bitxor(b, c), d);
+ k := to_number('ca62c1d6', 'xxxxxxxx');
+ end if;
+ temp := bitand(cyclic32(a, 32-5) + f + e + k + w(t), c_bits_ffffffff);
+ e := d;
+ d := c;
+ c := cyclic32(b, 32-30);
+ b := a;
+ a := temp;
+ end loop;
+ a := bitand(a + a_save, c_bits_ffffffff);
+ b := bitand(b + b_save, c_bits_ffffffff);
+ c := bitand(c + c_save, c_bits_ffffffff);
+ d := bitand(d + d_save, c_bits_ffffffff);
+ e := bitand(e + e_save, c_bits_ffffffff);
+ -- Prepare for the next round.
+ l_words_count := l_words_count - 16;
+ end loop;
+ -- Put checksum in context given as argument.
+ m_ctx.h(0) := a;
+ m_ctx.h(1) := b;
+ m_ctx.h(2) := c;
+ m_ctx.h(3) := d;
+ m_ctx.h(4) := e;
+ end sha1_process_block;
+
+ procedure sha1_process_bytes(p_buffer in raw,
+ p_buffer_length in number)
+ as
+ l_buffer raw(16640);
+ l_buffer_length number;
+ l_words_array ta_number;
+ begin
+ -- First increment the byte count. FIPS 180-2 specifies the possible
+ -- length of the file up to 2^64 bits. Here we only compute the number of
+ -- bytes.
+ m_ctx.total_length := m_ctx.total_length + nvl(p_buffer_length, 0);
+ -- When we already have some bits in our internal buffer concatenate both inputs first.
+ if (m_ctx.leftover_buffer_length = 0) then
+ l_buffer := p_buffer;
+ l_buffer_length := nvl(p_buffer_length, 0);
+ else
+ l_buffer := m_ctx.leftover_buffer || p_buffer;
+ l_buffer_length := m_ctx.leftover_buffer_length + nvl(p_buffer_length, 0);
+ end if;
+ -- Process available complete blocks.
+ if (l_buffer_length >= 64) then
+ declare
+ l_words_count number := bitand(l_buffer_length, c_bits_ffffffc0) / 4;
+ l_max_idx number := l_words_count - 1;
+ l_numberraw raw(4);
+ l_numberhex varchar(8);
+ l_number number;
+ begin
+ for idx in 0..l_max_idx loop
+ l_numberraw := sys.utl_raw.substr(l_buffer, idx * 4 + 1, 4);
+ l_numberhex := rawtohex(l_numberraw);
+ l_number := to_number(l_numberhex, 'xxxxxxxx');
+ l_words_array(idx) := l_number;
+ end loop;
+ sha1_process_block(l_words_array, l_words_count);
+ l_buffer_length := bitand(l_buffer_length, 63);
+ if (l_buffer_length > 0) then
+ l_buffer := sys.utl_raw.substr(l_buffer, l_words_count * 4 + 1, l_buffer_length);
+ end if;
+ end;
+ end if;
+ -- Move remaining bytes into internal buffer.
+ if (l_buffer_length > 0) then
+ m_ctx.leftover_buffer := l_buffer;
+ m_ctx.leftover_buffer_length := l_buffer_length;
+ end if;
+ end sha1_process_bytes;
+
+ procedure sha1_finish_ctx(p_resultbuf out nocopy ta_number)
+ as
+ l_filesizeraw raw(8);
+ begin
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || c_bits_80;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 1;
+ while ((m_ctx.leftover_buffer_length mod 64) <> 56) loop
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || c_bits_00;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 1;
+ end loop;
+ l_filesizeraw := hextoraw(to_char(m_ctx.total_length * 8, 'FM0xxxxxxxxxxxxxxx'));
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || l_filesizeraw;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 8;
+ sha1_process_bytes(null, 0);
+ for idx in 0..4 loop
+ p_resultbuf(idx) := m_ctx.h(idx);
+ end loop;
+ end sha1_finish_ctx;
+
+ function sha1(p_buffer in raw) return sha1_checksum_raw
+ as
+ l_result sha1_checksum_raw;
+ begin
+ sha1_init_ctx;
+ sha1_process_bytes(p_buffer, sys.utl_raw.length(p_buffer));
+ sha1_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0), 'FM0xxxxxxx') ||
+ to_char(m_result(1), 'FM0xxxxxxx') ||
+ to_char(m_result(2), 'FM0xxxxxxx') ||
+ to_char(m_result(3), 'FM0xxxxxxx') ||
+ to_char(m_result(4), 'FM0xxxxxxx')
+ );
+ return l_result;
+ end sha1;
+
+ function sha1(p_buffer in blob) return sha1_checksum_raw
+ as
+ l_result sha1_checksum_raw;
+ l_buffer raw(16384);
+ l_amount number := 16384;
+ l_offset number := 1;
+ begin
+ sha1_init_ctx;
+ begin
+ loop
+ sys.dbms_lob.read(p_buffer, l_amount, l_offset, l_buffer);
+ sha1_process_bytes(l_buffer, l_amount);
+ l_offset := l_offset + l_amount;
+ l_amount := 16384;
+ end loop;
+ exception
+ when no_data_found then
+ null;
+ end;
+ sha1_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0), 'FM0xxxxxxx') ||
+ to_char(m_result(1), 'FM0xxxxxxx') ||
+ to_char(m_result(2), 'FM0xxxxxxx') ||
+ to_char(m_result(3), 'FM0xxxxxxx') ||
+ to_char(m_result(4), 'FM0xxxxxxx')
+ );
+ return l_result;
+ end sha1;
+
+ --
+ -- SHA-256
+ --
+
+ procedure sha256_init_k
+ as
+ begin
+ m_k(0) := to_number('428a2f98', 'xxxxxxxx');
+ m_k(1) := to_number('71374491', 'xxxxxxxx');
+ m_k(2) := to_number('b5c0fbcf', 'xxxxxxxx');
+ m_k(3) := to_number('e9b5dba5', 'xxxxxxxx');
+ m_k(4) := to_number('3956c25b', 'xxxxxxxx');
+ m_k(5) := to_number('59f111f1', 'xxxxxxxx');
+ m_k(6) := to_number('923f82a4', 'xxxxxxxx');
+ m_k(7) := to_number('ab1c5ed5', 'xxxxxxxx');
+ m_k(8) := to_number('d807aa98', 'xxxxxxxx');
+ m_k(9) := to_number('12835b01', 'xxxxxxxx');
+ m_k(10) := to_number('243185be', 'xxxxxxxx');
+ m_k(11) := to_number('550c7dc3', 'xxxxxxxx');
+ m_k(12) := to_number('72be5d74', 'xxxxxxxx');
+ m_k(13) := to_number('80deb1fe', 'xxxxxxxx');
+ m_k(14) := to_number('9bdc06a7', 'xxxxxxxx');
+ m_k(15) := to_number('c19bf174', 'xxxxxxxx');
+ m_k(16) := to_number('e49b69c1', 'xxxxxxxx');
+ m_k(17) := to_number('efbe4786', 'xxxxxxxx');
+ m_k(18) := to_number('0fc19dc6', 'xxxxxxxx');
+ m_k(19) := to_number('240ca1cc', 'xxxxxxxx');
+ m_k(20) := to_number('2de92c6f', 'xxxxxxxx');
+ m_k(21) := to_number('4a7484aa', 'xxxxxxxx');
+ m_k(22) := to_number('5cb0a9dc', 'xxxxxxxx');
+ m_k(23) := to_number('76f988da', 'xxxxxxxx');
+ m_k(24) := to_number('983e5152', 'xxxxxxxx');
+ m_k(25) := to_number('a831c66d', 'xxxxxxxx');
+ m_k(26) := to_number('b00327c8', 'xxxxxxxx');
+ m_k(27) := to_number('bf597fc7', 'xxxxxxxx');
+ m_k(28) := to_number('c6e00bf3', 'xxxxxxxx');
+ m_k(29) := to_number('d5a79147', 'xxxxxxxx');
+ m_k(30) := to_number('06ca6351', 'xxxxxxxx');
+ m_k(31) := to_number('14292967', 'xxxxxxxx');
+ m_k(32) := to_number('27b70a85', 'xxxxxxxx');
+ m_k(33) := to_number('2e1b2138', 'xxxxxxxx');
+ m_k(34) := to_number('4d2c6dfc', 'xxxxxxxx');
+ m_k(35) := to_number('53380d13', 'xxxxxxxx');
+ m_k(36) := to_number('650a7354', 'xxxxxxxx');
+ m_k(37) := to_number('766a0abb', 'xxxxxxxx');
+ m_k(38) := to_number('81c2c92e', 'xxxxxxxx');
+ m_k(39) := to_number('92722c85', 'xxxxxxxx');
+ m_k(40) := to_number('a2bfe8a1', 'xxxxxxxx');
+ m_k(41) := to_number('a81a664b', 'xxxxxxxx');
+ m_k(42) := to_number('c24b8b70', 'xxxxxxxx');
+ m_k(43) := to_number('c76c51a3', 'xxxxxxxx');
+ m_k(44) := to_number('d192e819', 'xxxxxxxx');
+ m_k(45) := to_number('d6990624', 'xxxxxxxx');
+ m_k(46) := to_number('f40e3585', 'xxxxxxxx');
+ m_k(47) := to_number('106aa070', 'xxxxxxxx');
+ m_k(48) := to_number('19a4c116', 'xxxxxxxx');
+ m_k(49) := to_number('1e376c08', 'xxxxxxxx');
+ m_k(50) := to_number('2748774c', 'xxxxxxxx');
+ m_k(51) := to_number('34b0bcb5', 'xxxxxxxx');
+ m_k(52) := to_number('391c0cb3', 'xxxxxxxx');
+ m_k(53) := to_number('4ed8aa4a', 'xxxxxxxx');
+ m_k(54) := to_number('5b9cca4f', 'xxxxxxxx');
+ m_k(55) := to_number('682e6ff3', 'xxxxxxxx');
+ m_k(56) := to_number('748f82ee', 'xxxxxxxx');
+ m_k(57) := to_number('78a5636f', 'xxxxxxxx');
+ m_k(58) := to_number('84c87814', 'xxxxxxxx');
+ m_k(59) := to_number('8cc70208', 'xxxxxxxx');
+ m_k(60) := to_number('90befffa', 'xxxxxxxx');
+ m_k(61) := to_number('a4506ceb', 'xxxxxxxx');
+ m_k(62) := to_number('bef9a3f7', 'xxxxxxxx');
+ m_k(63) := to_number('c67178f2', 'xxxxxxxx');
+ end sha256_init_k;
+
+ procedure sha256_init_ctx
+ as
+ begin
+ m_ctx.h(0) := to_number('6a09e667', 'xxxxxxxx');
+ m_ctx.h(1) := to_number('bb67ae85', 'xxxxxxxx');
+ m_ctx.h(2) := to_number('3c6ef372', 'xxxxxxxx');
+ m_ctx.h(3) := to_number('a54ff53a', 'xxxxxxxx');
+ m_ctx.h(4) := to_number('510e527f', 'xxxxxxxx');
+ m_ctx.h(5) := to_number('9b05688c', 'xxxxxxxx');
+ m_ctx.h(6) := to_number('1f83d9ab', 'xxxxxxxx');
+ m_ctx.h(7) := to_number('5be0cd19', 'xxxxxxxx');
+ m_ctx.total_length := 0;
+ m_ctx.leftover_buffer := null;
+ m_ctx.leftover_buffer_length := 0;
+ for idx in 0..15 loop
+ m_ctx.words_array(idx) := 0;
+ end loop;
+ end sha256_init_ctx;
+
+ procedure sha256_process_block(p_words_array in ta_number,
+ p_words_count in number)
+ as
+ l_words_array ta_number := p_words_array;
+ l_words_count number := p_words_count;
+ l_words_idx number;
+ t number;
+ a number := m_ctx.h(0);
+ b number := m_ctx.h(1);
+ c number := m_ctx.h(2);
+ d number := m_ctx.h(3);
+ e number := m_ctx.h(4);
+ f number := m_ctx.h(5);
+ g number := m_ctx.h(6);
+ h number := m_ctx.h(7);
+ w ta_number;
+ a_save number;
+ b_save number;
+ c_save number;
+ d_save number;
+ e_save number;
+ f_save number;
+ g_save number;
+ h_save number;
+ t1 number;
+ t2 number;
+ begin
+ -- Process all bytes in the buffer with 64 bytes in each round of the loop.
+ l_words_idx := 0;
+ while (l_words_count > 0) loop
+ a_save := a;
+ b_save := b;
+ c_save := c;
+ d_save := d;
+ e_save := e;
+ f_save := f;
+ g_save := g;
+ h_save := h;
+ -- Compute the message schedule according to FIPS 180-2:6.2.2 step 2.
+ for t in 0..15 loop
+ w(t) := l_words_array(l_words_idx);
+ l_words_idx := l_words_idx + 1;
+ end loop;
+ for t in 16..63 loop
+ w(t) := bitand(op_r1_32(w(t - 2)) + w(t - 7) + op_r0_32(w(t - 15)) + w(t - 16), c_bits_ffffffff);
+ end loop;
+ -- The actual computation according to FIPS 180-2:6.2.2 step 3.
+ for t in 0..63 loop
+ t1 := bitand(h + op_s1_32(e) + op_ch_32(e, f, g) + m_k(t) + w(t), c_bits_ffffffff);
+ t2 := bitand(op_s0_32(a) + op_maj(a, b, c), c_bits_ffffffff);
+ h := g;
+ g := f;
+ f := e;
+ e := bitand(d + t1, c_bits_ffffffff);
+ d := c;
+ c := b;
+ b := a;
+ a := bitand(t1 + t2, c_bits_ffffffff);
+ end loop;
+ -- Add the starting values of the context according to FIPS 180-2:6.2.2 step 4.
+ a := bitand(a + a_save, c_bits_ffffffff);
+ b := bitand(b + b_save, c_bits_ffffffff);
+ c := bitand(c + c_save, c_bits_ffffffff);
+ d := bitand(d + d_save, c_bits_ffffffff);
+ e := bitand(e + e_save, c_bits_ffffffff);
+ f := bitand(f + f_save, c_bits_ffffffff);
+ g := bitand(g + g_save, c_bits_ffffffff);
+ h := bitand(h + h_save, c_bits_ffffffff);
+ -- Prepare for the next round.
+ l_words_count := l_words_count - 16;
+ end loop;
+ -- Put checksum in context given as argument.
+ m_ctx.h(0) := a;
+ m_ctx.h(1) := b;
+ m_ctx.h(2) := c;
+ m_ctx.h(3) := d;
+ m_ctx.h(4) := e;
+ m_ctx.h(5) := f;
+ m_ctx.h(6) := g;
+ m_ctx.h(7) := h;
+ end sha256_process_block;
+
+ procedure sha256_process_bytes(p_buffer in raw,
+ p_buffer_length in number)
+ as
+ l_buffer raw(16640);
+ l_buffer_length number;
+ l_words_array ta_number;
+ begin
+ -- First increment the byte count. FIPS 180-2 specifies the possible
+ -- length of the file up to 2^64 bits. Here we only compute the number of
+ -- bytes.
+ m_ctx.total_length := m_ctx.total_length + nvl(p_buffer_length, 0);
+ -- When we already have some bits in our internal buffer concatenate both inputs first.
+ if (m_ctx.leftover_buffer_length = 0) then
+ l_buffer := p_buffer;
+ l_buffer_length := nvl(p_buffer_length, 0);
+ else
+ l_buffer := m_ctx.leftover_buffer || p_buffer;
+ l_buffer_length := m_ctx.leftover_buffer_length + nvl(p_buffer_length, 0);
+ end if;
+ -- Process available complete blocks.
+ if (l_buffer_length >= 64) then
+ declare
+ l_words_count number := bitand(l_buffer_length, c_bits_ffffffc0) / 4;
+ l_max_idx number := l_words_count - 1;
+ l_numberraw raw(4);
+ l_numberhex varchar2(8);
+ l_number number;
+ begin
+ for idx in 0..l_max_idx loop
+ l_numberraw := sys.utl_raw.substr(l_buffer, idx * 4 + 1, 4);
+ l_numberhex := rawtohex(l_numberraw);
+ l_number := to_number(l_numberhex,'xxxxxxxx');
+ l_words_array(idx) := l_number;
+ end loop;
+ sha256_process_block(l_words_array, l_words_count);
+ l_buffer_length := bitand(l_buffer_length, 63);
+ if (l_buffer_length > 0) then
+ l_buffer := sys.utl_raw.substr(l_buffer, l_words_count * 4 + 1, l_buffer_length);
+ end if;
+ end;
+ end if;
+ -- Move remaining bytes into internal buffer.
+ if (l_buffer_length > 0) then
+ m_ctx.leftover_buffer := l_buffer;
+ m_ctx.leftover_buffer_length := l_buffer_length;
+ end if;
+ end sha256_process_bytes;
+
+ procedure sha256_finish_ctx(p_resultbuf out nocopy ta_number)
+ as
+ l_filesizeraw raw(8);
+ begin
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || c_bits_80;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 1;
+ while ((m_ctx.leftover_buffer_length mod 64) <> 56) loop
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || c_bits_00;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 1;
+ end loop;
+ l_filesizeraw := hextoraw(to_char(m_ctx.total_length * 8, 'FM0xxxxxxxxxxxxxxx'));
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || l_filesizeraw;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 8;
+ sha256_process_bytes(null, 0);
+ for idx in 0..7 loop
+ p_resultbuf(idx) := m_ctx.h(idx);
+ end loop;
+ end sha256_finish_ctx;
+
+ function sha256(p_buffer in raw) return sha256_checksum_raw
+ as
+ l_result sha256_checksum_raw;
+ begin
+ sha256_init_k;
+ sha256_init_ctx;
+ sha256_process_bytes(p_buffer, sys.utl_raw.length(p_buffer));
+ sha256_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxx') ||
+ to_char(m_result(4),'FM0xxxxxxx') ||
+ to_char(m_result(5),'FM0xxxxxxx') ||
+ to_char(m_result(6),'FM0xxxxxxx') ||
+ to_char(m_result(7),'FM0xxxxxxx')
+ );
+ return l_result;
+ end sha256;
+
+ function sha256(p_buffer in blob) return sha256_checksum_raw
+ as
+ l_result sha256_checksum_raw;
+ l_buffer raw(16384);
+ l_amount number := 16384;
+ l_offset number := 1;
+ begin
+ sha256_init_k;
+ sha256_init_ctx;
+ begin
+ loop
+ sys.dbms_lob.read(p_buffer, l_amount, l_offset, l_buffer);
+ sha256_process_bytes(l_buffer, l_amount);
+ l_offset := l_offset + l_amount;
+ l_amount := 16384;
+ end loop;
+ exception
+ when no_data_found then
+ null;
+ end;
+ sha256_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxx') ||
+ to_char(m_result(4),'FM0xxxxxxx') ||
+ to_char(m_result(5),'FM0xxxxxxx') ||
+ to_char(m_result(6),'FM0xxxxxxx') ||
+ to_char(m_result(7),'FM0xxxxxxx')
+ );
+ return l_result;
+ end sha256;
+
+ --
+ -- SHA-224
+ --
+
+ procedure sha224_init_ctx
+ as
+ begin
+ m_ctx.h(0) := to_number('c1059ed8', 'xxxxxxxx');
+ m_ctx.h(1) := to_number('367cd507', 'xxxxxxxx');
+ m_ctx.h(2) := to_number('3070dd17', 'xxxxxxxx');
+ m_ctx.h(3) := to_number('f70e5939', 'xxxxxxxx');
+ m_ctx.h(4) := to_number('ffc00b31', 'xxxxxxxx');
+ m_ctx.h(5) := to_number('68581511', 'xxxxxxxx');
+ m_ctx.h(6) := to_number('64f98fa7', 'xxxxxxxx');
+ m_ctx.h(7) := to_number('befa4fa4', 'xxxxxxxx');
+ m_ctx.total_length := 0;
+ m_ctx.leftover_buffer := null;
+ m_ctx.leftover_buffer_length := 0;
+ for idx in 0..15 loop
+ m_ctx.words_array(idx) := 0;
+ end loop;
+ end sha224_init_ctx;
+
+ function sha224(p_buffer in raw) return sha224_checksum_raw
+ as
+ l_result sha224_checksum_raw;
+ begin
+ sha256_init_k;
+ sha224_init_ctx;
+ sha256_process_bytes(p_buffer, sys.utl_raw.length(p_buffer));
+ sha256_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxx') ||
+ to_char(m_result(4),'FM0xxxxxxx') ||
+ to_char(m_result(5),'FM0xxxxxxx') ||
+ to_char(m_result(6),'FM0xxxxxxx')
+ );
+ return l_result;
+ end sha224;
+
+ function sha224(p_buffer in blob) return sha224_checksum_raw
+ as
+ l_result sha224_checksum_raw;
+ l_buffer raw(16384);
+ l_amount number := 16384;
+ l_offset number := 1;
+ begin
+ sha256_init_k;
+ sha224_init_ctx;
+ begin
+ loop
+ sys.dbms_lob.read(p_buffer, l_amount, l_offset, l_buffer);
+ sha256_process_bytes(l_buffer, l_amount);
+ l_offset := l_offset + l_amount;
+ l_amount := 16384;
+ end loop;
+ exception
+ when no_data_found then
+ null;
+ end;
+ sha256_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxx') ||
+ to_char(m_result(4),'FM0xxxxxxx') ||
+ to_char(m_result(5),'FM0xxxxxxx') ||
+ to_char(m_result(6),'FM0xxxxxxx')
+ );
+ return l_result;
+ end sha224;
+
+ --
+ -- SHA-512
+ --
+
+ procedure sha512_init_k
+ as
+ begin
+ m_k(0) := to_number('428a2f98d728ae22', 'xxxxxxxxxxxxxxxx');
+ m_k(1) := to_number('7137449123ef65cd', 'xxxxxxxxxxxxxxxx');
+ m_k(2) := to_number('b5c0fbcfec4d3b2f', 'xxxxxxxxxxxxxxxx');
+ m_k(3) := to_number('e9b5dba58189dbbc', 'xxxxxxxxxxxxxxxx');
+ m_k(4) := to_number('3956c25bf348b538', 'xxxxxxxxxxxxxxxx');
+ m_k(5) := to_number('59f111f1b605d019', 'xxxxxxxxxxxxxxxx');
+ m_k(6) := to_number('923f82a4af194f9b', 'xxxxxxxxxxxxxxxx');
+ m_k(7) := to_number('ab1c5ed5da6d8118', 'xxxxxxxxxxxxxxxx');
+ m_k(8) := to_number('d807aa98a3030242', 'xxxxxxxxxxxxxxxx');
+ m_k(9) := to_number('12835b0145706fbe', 'xxxxxxxxxxxxxxxx');
+ m_k(10) := to_number('243185be4ee4b28c', 'xxxxxxxxxxxxxxxx');
+ m_k(11) := to_number('550c7dc3d5ffb4e2', 'xxxxxxxxxxxxxxxx');
+ m_k(12) := to_number('72be5d74f27b896f', 'xxxxxxxxxxxxxxxx');
+ m_k(13) := to_number('80deb1fe3b1696b1', 'xxxxxxxxxxxxxxxx');
+ m_k(14) := to_number('9bdc06a725c71235', 'xxxxxxxxxxxxxxxx');
+ m_k(15) := to_number('c19bf174cf692694', 'xxxxxxxxxxxxxxxx');
+ m_k(16) := to_number('e49b69c19ef14ad2', 'xxxxxxxxxxxxxxxx');
+ m_k(17) := to_number('efbe4786384f25e3', 'xxxxxxxxxxxxxxxx');
+ m_k(18) := to_number('0fc19dc68b8cd5b5', 'xxxxxxxxxxxxxxxx');
+ m_k(19) := to_number('240ca1cc77ac9c65', 'xxxxxxxxxxxxxxxx');
+ m_k(20) := to_number('2de92c6f592b0275', 'xxxxxxxxxxxxxxxx');
+ m_k(21) := to_number('4a7484aa6ea6e483', 'xxxxxxxxxxxxxxxx');
+ m_k(22) := to_number('5cb0a9dcbd41fbd4', 'xxxxxxxxxxxxxxxx');
+ m_k(23) := to_number('76f988da831153b5', 'xxxxxxxxxxxxxxxx');
+ m_k(24) := to_number('983e5152ee66dfab', 'xxxxxxxxxxxxxxxx');
+ m_k(25) := to_number('a831c66d2db43210', 'xxxxxxxxxxxxxxxx');
+ m_k(26) := to_number('b00327c898fb213f', 'xxxxxxxxxxxxxxxx');
+ m_k(27) := to_number('bf597fc7beef0ee4', 'xxxxxxxxxxxxxxxx');
+ m_k(28) := to_number('c6e00bf33da88fc2', 'xxxxxxxxxxxxxxxx');
+ m_k(29) := to_number('d5a79147930aa725', 'xxxxxxxxxxxxxxxx');
+ m_k(30) := to_number('06ca6351e003826f', 'xxxxxxxxxxxxxxxx');
+ m_k(31) := to_number('142929670a0e6e70', 'xxxxxxxxxxxxxxxx');
+ m_k(32) := to_number('27b70a8546d22ffc', 'xxxxxxxxxxxxxxxx');
+ m_k(33) := to_number('2e1b21385c26c926', 'xxxxxxxxxxxxxxxx');
+ m_k(34) := to_number('4d2c6dfc5ac42aed', 'xxxxxxxxxxxxxxxx');
+ m_k(35) := to_number('53380d139d95b3df', 'xxxxxxxxxxxxxxxx');
+ m_k(36) := to_number('650a73548baf63de', 'xxxxxxxxxxxxxxxx');
+ m_k(37) := to_number('766a0abb3c77b2a8', 'xxxxxxxxxxxxxxxx');
+ m_k(38) := to_number('81c2c92e47edaee6', 'xxxxxxxxxxxxxxxx');
+ m_k(39) := to_number('92722c851482353b', 'xxxxxxxxxxxxxxxx');
+ m_k(40) := to_number('a2bfe8a14cf10364', 'xxxxxxxxxxxxxxxx');
+ m_k(41) := to_number('a81a664bbc423001', 'xxxxxxxxxxxxxxxx');
+ m_k(42) := to_number('c24b8b70d0f89791', 'xxxxxxxxxxxxxxxx');
+ m_k(43) := to_number('c76c51a30654be30', 'xxxxxxxxxxxxxxxx');
+ m_k(44) := to_number('d192e819d6ef5218', 'xxxxxxxxxxxxxxxx');
+ m_k(45) := to_number('d69906245565a910', 'xxxxxxxxxxxxxxxx');
+ m_k(46) := to_number('f40e35855771202a', 'xxxxxxxxxxxxxxxx');
+ m_k(47) := to_number('106aa07032bbd1b8', 'xxxxxxxxxxxxxxxx');
+ m_k(48) := to_number('19a4c116b8d2d0c8', 'xxxxxxxxxxxxxxxx');
+ m_k(49) := to_number('1e376c085141ab53', 'xxxxxxxxxxxxxxxx');
+ m_k(50) := to_number('2748774cdf8eeb99', 'xxxxxxxxxxxxxxxx');
+ m_k(51) := to_number('34b0bcb5e19b48a8', 'xxxxxxxxxxxxxxxx');
+ m_k(52) := to_number('391c0cb3c5c95a63', 'xxxxxxxxxxxxxxxx');
+ m_k(53) := to_number('4ed8aa4ae3418acb', 'xxxxxxxxxxxxxxxx');
+ m_k(54) := to_number('5b9cca4f7763e373', 'xxxxxxxxxxxxxxxx');
+ m_k(55) := to_number('682e6ff3d6b2b8a3', 'xxxxxxxxxxxxxxxx');
+ m_k(56) := to_number('748f82ee5defb2fc', 'xxxxxxxxxxxxxxxx');
+ m_k(57) := to_number('78a5636f43172f60', 'xxxxxxxxxxxxxxxx');
+ m_k(58) := to_number('84c87814a1f0ab72', 'xxxxxxxxxxxxxxxx');
+ m_k(59) := to_number('8cc702081a6439ec', 'xxxxxxxxxxxxxxxx');
+ m_k(60) := to_number('90befffa23631e28', 'xxxxxxxxxxxxxxxx');
+ m_k(61) := to_number('a4506cebde82bde9', 'xxxxxxxxxxxxxxxx');
+ m_k(62) := to_number('bef9a3f7b2c67915', 'xxxxxxxxxxxxxxxx');
+ m_k(63) := to_number('c67178f2e372532b', 'xxxxxxxxxxxxxxxx');
+ m_k(64) := to_number('ca273eceea26619c', 'xxxxxxxxxxxxxxxx');
+ m_k(65) := to_number('d186b8c721c0c207', 'xxxxxxxxxxxxxxxx');
+ m_k(66) := to_number('eada7dd6cde0eb1e', 'xxxxxxxxxxxxxxxx');
+ m_k(67) := to_number('f57d4f7fee6ed178', 'xxxxxxxxxxxxxxxx');
+ m_k(68) := to_number('06f067aa72176fba', 'xxxxxxxxxxxxxxxx');
+ m_k(69) := to_number('0a637dc5a2c898a6', 'xxxxxxxxxxxxxxxx');
+ m_k(70) := to_number('113f9804bef90dae', 'xxxxxxxxxxxxxxxx');
+ m_k(71) := to_number('1b710b35131c471b', 'xxxxxxxxxxxxxxxx');
+ m_k(72) := to_number('28db77f523047d84', 'xxxxxxxxxxxxxxxx');
+ m_k(73) := to_number('32caab7b40c72493', 'xxxxxxxxxxxxxxxx');
+ m_k(74) := to_number('3c9ebe0a15c9bebc', 'xxxxxxxxxxxxxxxx');
+ m_k(75) := to_number('431d67c49c100d4c', 'xxxxxxxxxxxxxxxx');
+ m_k(76) := to_number('4cc5d4becb3e42b6', 'xxxxxxxxxxxxxxxx');
+ m_k(77) := to_number('597f299cfc657e2a', 'xxxxxxxxxxxxxxxx');
+ m_k(78) := to_number('5fcb6fab3ad6faec', 'xxxxxxxxxxxxxxxx');
+ m_k(79) := to_number('6c44198c4a475817', 'xxxxxxxxxxxxxxxx');
+ end sha512_init_k;
+
+ procedure sha512_init_ctx
+ as
+ begin
+ m_ctx.h(0) := to_number('6a09e667f3bcc908', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(1) := to_number('bb67ae8584caa73b', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(2) := to_number('3c6ef372fe94f82b', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(3) := to_number('a54ff53a5f1d36f1', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(4) := to_number('510e527fade682d1', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(5) := to_number('9b05688c2b3e6c1f', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(6) := to_number('1f83d9abfb41bd6b', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(7) := to_number('5be0cd19137e2179', 'xxxxxxxxxxxxxxxx');
+ m_ctx.total_length := 0;
+ m_ctx.leftover_buffer := null;
+ m_ctx.leftover_buffer_length := 0;
+ for idx in 0..15 loop
+ m_ctx.words_array(idx) := 0;
+ end loop;
+ end sha512_init_ctx;
+
+ procedure sha512_process_block(p_words_array in ta_number,
+ p_words_count in number)
+ as
+ l_words_array ta_number := p_words_array;
+ l_words_count number := p_words_count;
+ l_words_idx number;
+ t number;
+ a number := m_ctx.h(0);
+ b number := m_ctx.h(1);
+ c number := m_ctx.h(2);
+ d number := m_ctx.h(3);
+ e number := m_ctx.h(4);
+ f number := m_ctx.h(5);
+ g number := m_ctx.h(6);
+ h number := m_ctx.h(7);
+ w ta_number;
+ a_save number;
+ b_save number;
+ c_save number;
+ d_save number;
+ e_save number;
+ f_save number;
+ g_save number;
+ h_save number;
+ t1 number;
+ t2 number;
+ begin
+ -- Process all bytes in the buffer with 64 bytes in each round of the loop.
+ l_words_idx := 0;
+ while (l_words_count > 0) loop
+ a_save := a;
+ b_save := b;
+ c_save := c;
+ d_save := d;
+ e_save := e;
+ f_save := f;
+ g_save := g;
+ h_save := h;
+ -- Compute the message schedule according to FIPS 180-2:6.2.2 step 2.
+ for t in 0..15 loop
+ w(t) := l_words_array(l_words_idx);
+ l_words_idx := l_words_idx + 1;
+ end loop;
+ for t in 16..79 loop
+ w(t) := bitand(op_r1_64(w(t - 2)) + w(t - 7) + op_r0_64(w(t - 15)) + w(t - 16), c_bits_ffffffffffffffff);
+ end loop;
+ -- The actual computation according to FIPS 180-2:6.2.2 step 3.
+ for t in 0..79 loop
+ t1 := bitand(h + op_s1_64(e) + op_ch_64(e, f, g) + m_k(t) + w(t), c_bits_ffffffffffffffff);
+ t2 := bitand(op_s0_64(a) + op_maj(a, b, c), c_bits_ffffffffffffffff);
+ h := g;
+ g := f;
+ f := e;
+ e := bitand(d + t1, c_bits_ffffffffffffffff);
+ d := c;
+ c := b;
+ b := a;
+ a := bitand(t1 + t2, c_bits_ffffffffffffffff);
+ end loop;
+ -- Add the starting values of the context according to FIPS 180-2:6.2.2 step 4.
+ a := bitand(a + a_save, c_bits_ffffffffffffffff);
+ b := bitand(b + b_save, c_bits_ffffffffffffffff);
+ c := bitand(c + c_save, c_bits_ffffffffffffffff);
+ d := bitand(d + d_save, c_bits_ffffffffffffffff);
+ e := bitand(e + e_save, c_bits_ffffffffffffffff);
+ f := bitand(f + f_save, c_bits_ffffffffffffffff);
+ g := bitand(g + g_save, c_bits_ffffffffffffffff);
+ h := bitand(h + h_save, c_bits_ffffffffffffffff);
+ -- Prepare for the next round.
+ l_words_count := l_words_count - 16;
+ end loop;
+ -- Put checksum in context given as argument.
+ m_ctx.h(0) := a;
+ m_ctx.h(1) := b;
+ m_ctx.h(2) := c;
+ m_ctx.h(3) := d;
+ m_ctx.h(4) := e;
+ m_ctx.h(5) := f;
+ m_ctx.h(6) := g;
+ m_ctx.h(7) := h;
+ end sha512_process_block;
+
+ procedure sha512_process_bytes(p_buffer in raw,
+ p_buffer_length in number)
+ as
+ l_buffer raw(16640);
+ l_buffer_length number;
+ l_words_array ta_number;
+ begin
+ m_ctx.total_length := m_ctx.total_length + nvl(p_buffer_length, 0);
+ -- When we already have some bits in our internal buffer concatenate both inputs first.
+ if (m_ctx.leftover_buffer_length = 0) then
+ l_buffer := p_buffer;
+ l_buffer_length := nvl(p_buffer_length, 0);
+ else
+ l_buffer := m_ctx.leftover_buffer || p_buffer;
+ l_buffer_length := m_ctx.leftover_buffer_length + nvl(p_buffer_length, 0);
+ end if;
+ -- Process available complete blocks.
+ if (l_buffer_length >= 128) then
+ declare
+ l_words_count number := bitand(l_buffer_length, c_bits_ffffffffffffff80) / 8;
+ l_max_idx number := l_words_count - 1;
+ l_numberraw raw(8);
+ l_numberhex varchar2(16);
+ l_number number;
+ begin
+ for idx in 0..l_max_idx loop
+ l_numberraw := sys.utl_raw.substr(l_buffer, idx * 8 + 1, 8);
+ l_numberhex := rawtohex(l_numberraw);
+ l_number := to_number(l_numberhex,'xxxxxxxxxxxxxxxx');
+ l_words_array(idx) := l_number;
+ end loop;
+ sha512_process_block(l_words_array, l_words_count);
+ l_buffer_length := bitand(l_buffer_length, 127);
+ if (l_buffer_length > 0) then
+ l_buffer := sys.utl_raw.substr(l_buffer, l_words_count * 8 + 1, l_buffer_length);
+ end if;
+ end;
+ end if;
+ -- Move remaining bytes into internal buffer.
+ if (l_buffer_length > 0) then
+ m_ctx.leftover_buffer := l_buffer;
+ m_ctx.leftover_buffer_length := l_buffer_length;
+ end if;
+ end sha512_process_bytes;
+
+ procedure sha512_finish_ctx(p_resultbuf out nocopy ta_number)
+ as
+ l_filesizeraw raw(16);
+ begin
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || c_bits_80;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 1;
+ while ((m_ctx.leftover_buffer_length mod 128) <> 112) loop
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || c_bits_00;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 1;
+ end loop;
+ l_filesizeraw := hextoraw(to_char(m_ctx.total_length * 8, 'FM0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'));
+ m_ctx.leftover_buffer := m_ctx.leftover_buffer || l_filesizeraw;
+ m_ctx.leftover_buffer_length := m_ctx.leftover_buffer_length + 16;
+ sha512_process_bytes(null, 0);
+ for idx in 0..7 loop
+ p_resultbuf(idx) := m_ctx.h(idx);
+ end loop;
+ end sha512_finish_ctx;
+
+ function sha512(p_buffer in raw) return sha512_checksum_raw
+ as
+ l_result sha512_checksum_raw;
+ begin
+ sha512_init_k;
+ sha512_init_ctx;
+ sha512_process_bytes(p_buffer, sys.utl_raw.length(p_buffer));
+ sha512_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(4),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(5),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(6),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(7),'FM0xxxxxxxxxxxxxxx')
+ );
+ return l_result;
+ end sha512;
+
+ function sha512(p_buffer in blob) return sha512_checksum_raw
+ as
+ l_result sha512_checksum_raw;
+ l_buffer raw(16384);
+ l_amount number := 16384;
+ l_offset number := 1;
+ begin
+ sha512_init_k;
+ sha512_init_ctx;
+ begin
+ loop
+ sys.dbms_lob.read(p_buffer, l_amount, l_offset, l_buffer);
+ sha512_process_bytes(l_buffer, l_amount);
+ l_offset := l_offset + l_amount;
+ l_amount := 16384;
+ end loop;
+ exception
+ when no_data_found then
+ null;
+ end;
+ sha512_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(4),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(5),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(6),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(7),'FM0xxxxxxxxxxxxxxx')
+ );
+ return l_result;
+ end sha512;
+
+ --
+ -- SHA-384
+ --
+
+ procedure sha384_init_ctx
+ as
+ begin
+ m_ctx.h(0) := to_number('CBBB9D5DC1059ED8', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(1) := to_number('629A292A367CD507', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(2) := to_number('9159015A3070DD17', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(3) := to_number('152FECD8F70E5939', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(4) := to_number('67332667FFC00B31', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(5) := to_number('8EB44A8768581511', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(6) := to_number('DB0C2E0D64F98FA7', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(7) := to_number('47B5481DBEFA4FA4', 'xxxxxxxxxxxxxxxx');
+ m_ctx.total_length := 0;
+ m_ctx.leftover_buffer := null;
+ m_ctx.leftover_buffer_length := 0;
+ for idx in 0..15 loop
+ m_ctx.words_array(idx) := 0;
+ end loop;
+ end sha384_init_ctx;
+
+ function sha384(p_buffer in raw) return sha384_checksum_raw
+ as
+ l_result sha384_checksum_raw;
+ begin
+ sha512_init_k;
+ sha384_init_ctx;
+ sha512_process_bytes(p_buffer, sys.utl_raw.length(p_buffer));
+ sha512_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(4),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(5),'FM0xxxxxxxxxxxxxxx')
+ );
+ return l_result;
+ end sha384;
+
+ function sha384(p_buffer in blob) return sha384_checksum_raw
+ as
+ l_result sha384_checksum_raw;
+ l_buffer raw(16384);
+ l_amount number := 16384;
+ l_offset number := 1;
+ begin
+ sha512_init_k;
+ sha384_init_ctx;
+ begin
+ loop
+ sys.dbms_lob.read(p_buffer, l_amount, l_offset, l_buffer);
+ sha512_process_bytes(l_buffer, l_amount);
+ l_offset := l_offset + l_amount;
+ l_amount := 16384;
+ end loop;
+ exception
+ when no_data_found then
+ null;
+ end;
+ sha512_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(4),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(5),'FM0xxxxxxxxxxxxxxx')
+ );
+ return l_result;
+ end sha384;
+
+ --
+ -- SHA-512/224
+ --
+
+ procedure sha512_224_init_ctx
+ as
+ begin
+ m_ctx.h(0) := to_number('8C3D37C819544DA2', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(1) := to_number('73E1996689DCD4D6', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(2) := to_number('1DFAB7AE32FF9C82', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(3) := to_number('679DD514582F9FCF', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(4) := to_number('0F6D2B697BD44DA8', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(5) := to_number('77E36F7304C48942', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(6) := to_number('3F9D85A86A1D36C8', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(7) := to_number('1112E6AD91D692A1', 'xxxxxxxxxxxxxxxx');
+ m_ctx.total_length := 0;
+ m_ctx.leftover_buffer := null;
+ m_ctx.leftover_buffer_length := 0;
+ for idx in 0..15 loop
+ m_ctx.words_array(idx) := 0;
+ end loop;
+ end sha512_224_init_ctx;
+
+ function sha512_224(p_buffer in raw) return sha224_checksum_raw
+ as
+ l_result sha224_checksum_raw;
+ begin
+ sha512_init_k;
+ sha512_224_init_ctx;
+ sha512_process_bytes(p_buffer, sys.utl_raw.length(p_buffer));
+ sha512_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxxxxxxxxxx') ||
+ substr(to_char(m_result(3),'FM0xxxxxxxxxxxxxxx'), 1, 8)
+ );
+ return l_result;
+ end sha512_224;
+
+ function sha512_224(p_buffer in blob) return sha224_checksum_raw
+ as
+ l_result sha224_checksum_raw;
+ l_buffer raw(16384);
+ l_amount number := 16384;
+ l_offset number := 1;
+ begin
+ sha512_init_k;
+ sha512_224_init_ctx;
+ begin
+ loop
+ sys.dbms_lob.read(p_buffer, l_amount, l_offset, l_buffer);
+ sha512_process_bytes(l_buffer, l_amount);
+ l_offset := l_offset + l_amount;
+ l_amount := 16384;
+ end loop;
+ exception
+ when no_data_found then
+ null;
+ end;
+ sha512_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxxxxxxxxxx') ||
+ substr(to_char(m_result(3),'FM0xxxxxxxxxxxxxxx'), 1, 8)
+ );
+ return l_result;
+ end sha512_224;
+
+ --
+ -- SHA-512/256
+ --
+
+ procedure sha512_256_init_ctx
+ as
+ begin
+ m_ctx.h(0) := to_number('22312194FC2BF72C', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(1) := to_number('9F555FA3C84C64C2', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(2) := to_number('2393B86B6F53B151', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(3) := to_number('963877195940EABD', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(4) := to_number('96283EE2A88EFFE3', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(5) := to_number('BE5E1E2553863992', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(6) := to_number('2B0199FC2C85B8AA', 'xxxxxxxxxxxxxxxx');
+ m_ctx.h(7) := to_number('0EB72DDC81C52CA2', 'xxxxxxxxxxxxxxxx');
+ m_ctx.total_length := 0;
+ m_ctx.leftover_buffer := null;
+ m_ctx.leftover_buffer_length := 0;
+ for idx in 0..15 loop
+ m_ctx.words_array(idx) := 0;
+ end loop;
+ end sha512_256_init_ctx;
+
+ function sha512_256(p_buffer in raw) return sha256_checksum_raw
+ as
+ l_result sha256_checksum_raw;
+ begin
+ sha512_init_k;
+ sha512_256_init_ctx;
+ sha512_process_bytes(p_buffer, sys.utl_raw.length(p_buffer));
+ sha512_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxxxxxxxxxx')
+ );
+ return l_result;
+ end sha512_256;
+
+ function sha512_256(p_buffer in blob) return sha256_checksum_raw
+ as
+ l_result sha256_checksum_raw;
+ l_buffer raw(16384);
+ l_amount number := 16384;
+ l_offset number := 1;
+ begin
+ sha512_init_k;
+ sha512_256_init_ctx;
+ begin
+ loop
+ sys.dbms_lob.read(p_buffer, l_amount, l_offset, l_buffer);
+ sha512_process_bytes(l_buffer, l_amount);
+ l_offset := l_offset + l_amount;
+ l_amount := 16384;
+ end loop;
+ exception
+ when no_data_found then
+ null;
+ end;
+ sha512_finish_ctx(m_result);
+ l_result := hextoraw(
+ to_char(m_result(0),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(1),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(2),'FM0xxxxxxxxxxxxxxx') ||
+ to_char(m_result(3),'FM0xxxxxxxxxxxxxxx')
+ );
+ return l_result;
+ end sha512_256;
+
+ ---
+ --- Unittest
+ ---
+
+ procedure unittest
+ as
+ l_blob blob;
+ l_raw raw(100);
+ begin
+
+ dbms_lob.createtemporary(l_blob, true);
+ l_raw := sys.utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog' || chr(13) || chr(10));
+ for i in 1..1000 loop
+ dbms_lob.writeappend(l_blob, sys.utl_raw.length(l_raw), l_raw);
+ end loop;
+
+ if lower(rawtohex(sha1(sys.utl_raw.cast_to_raw('')))) = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' then
+ dbms_output.put_line('SHA-1. Test 1 passed');
+ else
+ dbms_output.put_line('SHA-1. Test 1 failed');
+ end if;
+ if lower(rawtohex(sha1(sys.utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog')))) = '2fd4e1c67a2d28fced849ee1bb76e7391b93eb12' then
+ dbms_output.put_line('SHA-1. Test 2 passed');
+ else
+ dbms_output.put_line('SHA-1. Test 2 failed');
+ end if;
+ if lower(rawtohex(sha1(l_blob))) = '65d489cb70cae1cd7661a3043dc6ee51e41efb01' then
+ dbms_output.put_line('SHA-1. Test 3 passed');
+ else
+ dbms_output.put_line('SHA-1. Test 3 failed');
+ end if;
+
+ if lower(rawtohex(sha224(sys.utl_raw.cast_to_raw('')))) = 'd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f' then
+ dbms_output.put_line('SHA-224. Test 1 passed');
+ else
+ dbms_output.put_line('SHA-224. Test 1 failed');
+ end if;
+ if lower(rawtohex(sha224(sys.utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog')))) = '730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525' then
+ dbms_output.put_line('SHA-224. Test 2 passed');
+ else
+ dbms_output.put_line('SHA-224. Test 2 failed');
+ end if;
+ if lower(rawtohex(sha224(l_blob))) = 'daa4ac7e3d679550368d98cbf59e0805fbccbdd9c88b41c879a3ad6c' then
+ dbms_output.put_line('SHA-224. Test 3 passed');
+ else
+ dbms_output.put_line('SHA-224. Test 3 failed');
+ end if;
+
+ if lower(rawtohex(sha256(sys.utl_raw.cast_to_raw('')))) = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' then
+ dbms_output.put_line('SHA-256. Test 1 passed');
+ else
+ dbms_output.put_line('SHA-256. Test 1 failed');
+ end if;
+ if lower(rawtohex(sha256(sys.utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog')))) = 'd7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592' then
+ dbms_output.put_line('SHA-256. Test 2 passed');
+ else
+ dbms_output.put_line('SHA-256. Test 2 failed');
+ end if;
+ if lower(rawtohex(sha256(l_blob))) = '6a8fd57827ee3c24359730e5c64b6badc41da43758990964ff1b20e5d62ea5f0' then
+ dbms_output.put_line('SHA-256. Test 3 passed');
+ else
+ dbms_output.put_line('SHA-256. Test 3 failed');
+ end if;
+
+ if lower(rawtohex(sha384(sys.utl_raw.cast_to_raw('')))) = '38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b' then
+ dbms_output.put_line('SHA-384. Test 1 passed');
+ else
+ dbms_output.put_line('SHA-384. Test 1 failed');
+ end if;
+ if lower(rawtohex(sha384(sys.utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog')))) = 'ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1' then
+ dbms_output.put_line('SHA-384. Test 2 passed');
+ else
+ dbms_output.put_line('SHA-384. Test 2 failed');
+ end if;
+ if lower(rawtohex(sha384(l_blob))) = '0f35cb80adadaede011868e4cb79d760ca5bc80a7e84075b1b3e703ca12cc13366bd60b42e699e2d3d1744a617ab50da' then
+ dbms_output.put_line('SHA-384. Test 3 passed');
+ else
+ dbms_output.put_line('SHA-384. Test 3 failed');
+ end if;
+
+ if lower(rawtohex(sha512(sys.utl_raw.cast_to_raw('')))) = 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e' then
+ dbms_output.put_line('SHA-512. Test 1 passed');
+ else
+ dbms_output.put_line('SHA-512. Test 1 failed');
+ end if;
+ if lower(rawtohex(sha512(sys.utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog')))) = '07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6' then
+ dbms_output.put_line('SHA-512. Test 2 passed');
+ else
+ dbms_output.put_line('SHA-512. Test 2 failed');
+ end if;
+ if lower(rawtohex(sha512(l_blob))) = '3211cc7c5868f4f14878f93ab5dc82a12d5e8b9dbdc65eb7c7793a368cc93fbb5d9c130333b87db538a1cf86911aa60e1da4248ab8c6bb5bc14d381f556b99f4' then
+ dbms_output.put_line('SHA-512. Test 3 passed');
+ else
+ dbms_output.put_line('SHA-512. Test 3 failed');
+ end if;
+
+ if lower(rawtohex(sha512_224(sys.utl_raw.cast_to_raw('')))) = '6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4' then
+ dbms_output.put_line('SHA-512/224. Test 1 passed');
+ else
+ dbms_output.put_line('SHA-512/224. Test 1 failed');
+ end if;
+ if lower(rawtohex(sha512_224(sys.utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog')))) = '944cd2847fb54558d4775db0485a50003111c8e5daa63fe722c6aa37' then
+ dbms_output.put_line('SHA-512/224. Test 2 passed');
+ else
+ dbms_output.put_line('SHA-512/224. Test 2 failed');
+ end if;
+
+ if lower(rawtohex(sha512_256(sys.utl_raw.cast_to_raw('')))) = 'c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a' then
+ dbms_output.put_line('SHA-512/256. Test 1 passed');
+ else
+ dbms_output.put_line('SHA-512/256. Test 1 failed');
+ end if;
+ if lower(rawtohex(sha512_256(sys.utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog')))) = 'dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d' then
+ dbms_output.put_line('SHA-512/256. Test 2 passed');
+ else
+ dbms_output.put_line('SHA-512/256. Test 2 failed');
+ end if;
+
+ end unittest;
+
+end hash_util_pkg;
+/
+
diff --git a/extras/hash_util_pkg.pks b/extras/hash_util_pkg.pks
new file mode 100644
index 0000000..2015392
--- /dev/null
+++ b/extras/hash_util_pkg.pks
@@ -0,0 +1,50 @@
+create or replace package hash_util_pkg as
+
+ /*
+
+ Purpose:
+ Calculates SHA-1 and SHA-2 hashes.
+ Pure PL/SQL implementation. Uses decimal caclulations, works very slow.
+ Use DBMS_CRYPTO or external Java functions for quick hash calculation, whenever possible.
+
+ Author: Vadim Dvorovenko
+
+ Changes:
+ Who Date Description
+ ------ ---------- --------------------------------
+ DVN 09.12.2014 Created
+ DVN 26.11.2016 Formatting code for Alexandria library, BLOB verison
+ DVN 27.11.2016 Fully rewritten process_bytes procedures.
+ DVN 27.11.2016 Added SHA-224, SHA-384, SHA-512, SHA-512/256, SHA-512/224
+
+ */
+
+ subtype sha1_checksum_raw is raw(20);
+ subtype sha224_checksum_raw is raw(28);
+ subtype sha256_checksum_raw is raw(32);
+ subtype sha384_checksum_raw is raw(48);
+ subtype sha512_checksum_raw is raw(64);
+
+ -- Raw versions. Max p_buffer length - 16384 bytes
+ function sha1(p_buffer in raw) return sha1_checksum_raw;
+ function sha224(p_buffer in raw) return sha224_checksum_raw;
+ function sha256(p_buffer in raw) return sha256_checksum_raw;
+ function sha384(p_buffer in raw) return sha384_checksum_raw;
+ function sha512(p_buffer in raw) return sha512_checksum_raw;
+ function sha512_224(p_buffer in raw) return sha224_checksum_raw;
+ function sha512_256(p_buffer in raw) return sha256_checksum_raw;
+
+ -- Blob versions.
+ function sha1(p_buffer in blob) return sha1_checksum_raw;
+ function sha224(p_buffer in blob) return sha224_checksum_raw;
+ function sha256(p_buffer in blob) return sha256_checksum_raw;
+ function sha384(p_buffer in blob) return sha384_checksum_raw;
+ function sha512(p_buffer in blob) return sha512_checksum_raw;
+ function sha512_224(p_buffer in blob) return sha224_checksum_raw;
+ function sha512_256(p_buffer in blob) return sha256_checksum_raw;
+
+ procedure unittest;
+
+end hash_util_pkg;
+/
+
diff --git a/extras/hash_util_pkg_demo.sql b/extras/hash_util_pkg_demo.sql
new file mode 100644
index 0000000..553d983
--- /dev/null
+++ b/extras/hash_util_pkg_demo.sql
@@ -0,0 +1,14 @@
+select rawtohex(hash_util_pkg.sha1(utl_raw.cast_to_raw(''))) from dual;
+select rawtohex(hash_util_pkg.sha1(utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog'))) from dual;
+select rawtohex(hash_util_pkg.sha224(utl_raw.cast_to_raw(''))) from dual;
+select rawtohex(hash_util_pkg.sha224(utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog'))) from dual;
+select rawtohex(hash_util_pkg.sha256(utl_raw.cast_to_raw(''))) from dual;
+select rawtohex(hash_util_pkg.sha256(utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog'))) from dual;
+select rawtohex(hash_util_pkg.sha384(utl_raw.cast_to_raw(''))) from dual;
+select rawtohex(hash_util_pkg.sha384(utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog'))) from dual;
+select rawtohex(hash_util_pkg.sha512(utl_raw.cast_to_raw(''))) from dual;
+select rawtohex(hash_util_pkg.sha512(utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog'))) from dual;
+select rawtohex(hash_util_pkg.sha512_224(utl_raw.cast_to_raw(''))) from dual;
+select rawtohex(hash_util_pkg.sha512_224(utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog'))) from dual;
+select rawtohex(hash_util_pkg.sha512_256(utl_raw.cast_to_raw(''))) from dual;
+select rawtohex(hash_util_pkg.sha512_256(utl_raw.cast_to_raw('The quick brown fox jumps over the lazy dog'))) from dual;
diff --git a/ora/amazon_aws_s3_pkg.pkb b/ora/amazon_aws_s3_pkg.pkb
index 4ec3620..940a37f 100755
--- a/ora/amazon_aws_s3_pkg.pkb
+++ b/ora/amazon_aws_s3_pkg.pkb
@@ -533,14 +533,16 @@ begin
end get_bucket_region;
-procedure get_object_list (p_bucket_name in varchar2,
- p_prefix in varchar2,
- p_max_keys in number,
- p_list out t_object_list,
- p_more_marker in out varchar2)
+procedure get_object_list (p_bucket_name in varchar2,
+ p_prefix in varchar2,
+ p_max_keys in number,
+ p_list out t_object_list,
+ p_next_continuation_token in out varchar2)
as
l_clob clob;
l_xml xmltype;
+ l_xml_is_truncated xmltype;
+ l_xml_next_continuation xmltype;
l_date_str varchar2(255);
l_auth_str varchar2(255);
@@ -548,7 +550,6 @@ as
l_header_names t_str_array := t_str_array();
l_header_values t_str_array := t_str_array();
- l_count pls_integer := 0;
l_returnvalue t_object_list;
begin
@@ -557,7 +558,7 @@ begin
Purpose: get objects
- Remarks: see http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTObjectGET.html
+ Remarks: see http://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html
see http://code.google.com/p/plsql-utils/issues/detail?id=16
@@ -573,6 +574,7 @@ begin
------ ---------- -------------------------------------
MBR 15.01.2011 Created
JKEMP 14.08.2012 Rewritten as private procedure, see remarks above
+ KJS 06.10.2016 Modified to use newest S3 API which performs much better on large buckets. Changed for-loop to bulk operation.
*/
@@ -594,38 +596,36 @@ begin
l_header_values.extend;
l_header_values(3) := l_auth_str;
- if p_more_marker is not null then
- l_clob := make_request (get_url(p_bucket_name) || '?marker=' || p_more_marker || '&max-keys=' || p_max_keys || '&prefix=' || utl_url.escape(p_prefix), 'GET', l_header_names, l_header_values, null);
+ if p_next_continuation_token is not null then
+ l_clob := make_request (get_url(p_bucket_name) || '?list-type=2&continuation-token=' || utl_url.escape(p_next_continuation_token) || '&max-keys=' || p_max_keys || '&prefix=' || utl_url.escape(p_prefix), 'GET', l_header_names, l_header_values, null);
else
- l_clob := make_request (get_url(p_bucket_name) || '?max-keys=' || p_max_keys || '&prefix=' || utl_url.escape(p_prefix), 'GET', l_header_names, l_header_values, null);
+ l_clob := make_request (get_url(p_bucket_name) || '?list-type=2&max-keys=' || p_max_keys || '&prefix=' || utl_url.escape(p_prefix), 'GET', l_header_names, l_header_values, null);
end if;
-
if (l_clob is not null) and (length(l_clob) > 0) then
l_xml := xmltype (l_clob);
check_for_errors (l_xml);
- for l_rec in (
- select extractValue(value(t), '*/Key', g_aws_namespace_s3_full) as key,
- extractValue(value(t), '*/Size', g_aws_namespace_s3_full) as size_bytes,
- extractValue(value(t), '*/LastModified', g_aws_namespace_s3_full) as last_modified
- from table(xmlsequence(l_xml.extract('//ListBucketResult/Contents', g_aws_namespace_s3_full))) t
- ) loop
- l_count := l_count + 1;
- l_returnvalue(l_count).key := l_rec.key;
- l_returnvalue(l_count).size_bytes := l_rec.size_bytes;
- l_returnvalue(l_count).last_modified := to_date(l_rec.last_modified, g_date_format_xml);
- end loop;
+ select extractValue(value(t), '*/Key', g_aws_namespace_s3_full),
+ extractValue(value(t), '*/Size', g_aws_namespace_s3_full),
+ to_date(extractValue(value(t), '*/LastModified', g_aws_namespace_s3_full), g_date_format_xml)
+ bulk collect into l_returnvalue
+ from table(xmlsequence(l_xml.extract('//ListBucketResult/Contents', g_aws_namespace_s3_full))) t;
+
+ -- check if this is the last set of data or not, and set the in/out p_next_continuation_token as expected
+ l_xml_is_truncated := l_xml.extract('//ListBucketResult/IsTruncated/text()', g_aws_namespace_s3_full);
- -- check if this is the last set of data or not
-
- l_xml := l_xml.extract('//ListBucketResult/IsTruncated/text()', g_aws_namespace_s3_full);
-
- if l_xml is not null and l_xml.getStringVal = 'true' then
- p_more_marker := l_returnvalue(l_returnvalue.last).key;
+ if l_xml_is_truncated is not null and l_xml_is_truncated.getStringVal = 'true' then
+ l_xml_next_continuation := l_xml.extract('//ListBucketResult/NextContinuationToken/text()', g_aws_namespace_s3_full);
+ if l_xml_next_continuation is not null then
+ p_next_continuation_token := l_xml_next_continuation.getStringVal;
+ else
+ p_next_continuation_token := null;
+ end if;
+ else
+ p_next_continuation_token := null;
end if;
-
end if;
p_list := l_returnvalue;
@@ -638,7 +638,7 @@ function get_object_list (p_bucket_name in varchar2,
p_max_keys in number := null) return t_object_list
as
l_object_list t_object_list;
- l_more_marker varchar2(4000);
+ l_next_continuation_token varchar2(4000);
begin
/*
@@ -654,11 +654,11 @@ begin
*/
get_object_list (
- p_bucket_name => p_bucket_name,
- p_prefix => p_prefix,
- p_max_keys => p_max_keys,
- p_list => l_object_list,
- p_more_marker => l_more_marker --ignored by this function
+ p_bucket_name => p_bucket_name,
+ p_prefix => p_prefix,
+ p_max_keys => p_max_keys,
+ p_list => l_object_list,
+ p_next_continuation_token => l_next_continuation_token --ignored by this function
);
return l_object_list;
@@ -671,7 +671,7 @@ function get_object_tab (p_bucket_name in varchar2,
p_max_keys in number := null) return t_object_tab pipelined
as
l_object_list t_object_list;
- l_more_marker varchar2(4000);
+ l_next_continuation_token varchar2(4000);
begin
/*
@@ -689,18 +689,18 @@ begin
loop
get_object_list (
- p_bucket_name => p_bucket_name,
- p_prefix => p_prefix,
- p_max_keys => p_max_keys,
- p_list => l_object_list,
- p_more_marker => l_more_marker
+ p_bucket_name => p_bucket_name,
+ p_prefix => p_prefix,
+ p_max_keys => p_max_keys,
+ p_list => l_object_list,
+ p_next_continuation_token => l_next_continuation_token
);
for i in 1 .. l_object_list.count loop
pipe row (l_object_list(i));
end loop;
- exit when l_more_marker is null;
+ exit when l_next_continuation_token is null;
end loop;
diff --git a/ora/ooxml_util_pkg.pkb b/ora/ooxml_util_pkg.pkb
index cff309e..574a432 100755
--- a/ora/ooxml_util_pkg.pkb
+++ b/ora/ooxml_util_pkg.pkb
@@ -308,63 +308,71 @@ begin
end get_xlsx_properties;
-function get_xlsx_column_number (p_column_ref in varchar2) return number
+function get_xlsx_column_number( p_column_ref in varchar2 ) return number
as
- l_returnvalue number;
+ l_returnvalue number;
+ l_char_num number;
+ l_power number;
+ l_factor decimal;
begin
- /*
-
- Purpose: get column number from column reference
-
- Remarks:
-
- Who Date Description
- ------ ---------- --------------------------------
- MBR 11.07.2011 Created
-
- */
+ /*
+
+ Purpose: get column number from column reference
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 11.07.2011 Created
+ JMW 29.11.2016 Modified to support columns up to the limit (currently XFD or 16384)
+
+ */
+
+ l_power := length( p_column_ref ) - 1;
+
+ for i in 1..length( p_column_ref ) loop
+ l_char_num := ascii( substr( p_column_ref, i, 1 ));
+ l_factor := ( l_char_num - 65 ) + 1;
+ l_returnvalue := ( l_factor * power( 26, l_power )) + NVL( l_returnvalue, 0 );
+ l_power := l_power - 1;
+ end loop;
+
+ return l_returnvalue;
- if length(p_column_ref) > 1 then
- l_returnvalue := ascii(substr(p_column_ref,1,1)) - 64;
- l_returnvalue := l_returnvalue * 26;
- l_returnvalue := l_returnvalue + (ascii(substr(p_column_ref,2,1)) - 64);
- else
- l_returnvalue := ascii(p_column_ref) - 64;
- end if;
-
- return l_returnvalue;
-
end get_xlsx_column_number;
-function get_xlsx_column_ref (p_column_number in varchar2) return varchar2
+function get_xlsx_column_ref( p_column_number in number ) return varchar2
as
- l_offset number;
- l_returnvalue varchar2(2);
+ l_dividend decimal;
+ l_modulo decimal;
+ l_returnvalue varchar2(3);
begin
-
- /*
-
- Purpose: get column reference from column number
-
- Remarks:
-
- Who Date Description
- ------ ---------- --------------------------------
- MBR 11.07.2011 Created
-
- */
- if p_column_number < 27 then
- l_returnvalue := chr(p_column_number + 64);
- else
- l_offset := trunc(p_column_number/26);
- l_returnvalue := chr(l_offset + 64);
- l_returnvalue := l_returnvalue || chr(p_column_number - (l_offset * 26) + 64);
- end if;
-
- return l_returnvalue;
+ /*
+
+ Purpose: get column reference from column number
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 11.07.2011 Created
+ JMW 29.11.2016 Modified to support columns up to the limit (currently XFD or 16384)
+ and to fix the bug where the '@' is returned at multiples of 26
+
+ */
+
+ l_dividend := p_column_number;
+
+ while l_dividend > 0 loop
+ l_modulo := mod( l_dividend - 1, 26 );
+ l_returnvalue := to_char( chr( l_modulo + 65 )) || l_returnvalue;
+ l_dividend := (( l_dividend - l_modulo ) / 26 );
+ end loop;
+
+ return l_returnvalue;
end get_xlsx_column_ref;
@@ -879,6 +887,3 @@ end get_pptx_plaintext;
end ooxml_util_pkg;
/
-
-
-
diff --git a/ora/ooxml_util_pkg.pks b/ora/ooxml_util_pkg.pks
index 241ba6c..d6c4636 100755
--- a/ora/ooxml_util_pkg.pks
+++ b/ora/ooxml_util_pkg.pks
@@ -107,7 +107,7 @@ as
function get_xlsx_column_number (p_column_ref in varchar2) return number;
-- get column reference from column number
- function get_xlsx_column_ref (p_column_number in varchar2) return varchar2;
+ function get_xlsx_column_ref (p_column_number in number) return varchar2;
-- get cell value from XLSX file
function get_xlsx_cell_value (p_xlsx in blob,
@@ -148,5 +148,4 @@ as
p_note in number := null) return clob;
end ooxml_util_pkg;
-/
-
+/
\ No newline at end of file
diff --git a/ora/paypal_util_pkg.pkb b/ora/paypal_util_pkg.pkb
new file mode 100644
index 0000000..864ffcc
--- /dev/null
+++ b/ora/paypal_util_pkg.pkb
@@ -0,0 +1,626 @@
+create or replace package body paypal_util_pkg
+as
+
+ /*
+
+ Purpose: Package handles PayPal REST API
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ g_api_base_url_sandbox string_util_pkg.t_max_db_varchar2 := 'https://api.sandbox.paypal.com';
+ g_api_base_url_live string_util_pkg.t_max_db_varchar2 := 'https://api.paypal.com';
+
+ g_api_base_url string_util_pkg.t_max_db_varchar2 := g_api_base_url_live;
+
+ g_wallet_path string_util_pkg.t_max_db_varchar2;
+ g_wallet_password string_util_pkg.t_max_db_varchar2;
+
+
+procedure set_api_base_url (p_sandbox_url in varchar2,
+ p_live_url in varchar2)
+as
+begin
+
+ /*
+
+ Purpose: set API base URL
+
+ Remarks: useful if you need to use a proxy for HTTPS requests from the database
+ see http://blog.rhjmartens.nl/2015/07/making-https-webservice-requests-from.html
+ see http://ora-00001.blogspot.com/2016/04/how-to-set-up-iis-as-ssl-proxy-for-utl-http-in-oracle-xe.html
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 06.03.2016 Created
+
+ */
+
+ -- set available URLs
+ g_api_base_url_sandbox := p_sandbox_url;
+ g_api_base_url_live := p_live_url;
+
+ -- set the "live" URL as the default
+ g_api_base_url := g_api_base_url_live;
+
+end set_api_base_url;
+
+
+function make_request (p_url in varchar2,
+ p_body in clob := null,
+ p_http_method in varchar2 := 'POST',
+ p_access_token in t_access_token := null,
+ p_username in varchar2 := null,
+ p_password in varchar2 := null) return clob
+as
+ l_returnvalue clob;
+begin
+
+ /*
+
+ Purpose: make HTTP request
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ apex_web_service.g_request_headers.delete;
+
+ if (p_access_token.access_token is not null) then
+ apex_web_service.g_request_headers(1).name := 'Content-Type';
+ apex_web_service.g_request_headers(1).value := 'application/json';
+ apex_web_service.g_request_headers(2).name := 'Authorization';
+ apex_web_service.g_request_headers(2).value := p_access_token.token_type || ' ' || p_access_token.access_token;
+ else
+ apex_web_service.g_request_headers(1).name := 'Content-Type';
+ apex_web_service.g_request_headers(1).value := 'application/x-www-form-urlencoded';
+ apex_web_service.g_request_headers(2).name := 'Accept';
+ apex_web_service.g_request_headers(2).value := 'application/json';
+ apex_web_service.g_request_headers(3).name := 'Accept-Language';
+ apex_web_service.g_request_headers(3).value := 'en_US';
+ end if;
+
+ debug_pkg.printf('%1 %2', p_http_method, p_url);
+
+ l_returnvalue := apex_web_service.make_rest_request(
+ p_url => p_url,
+ p_http_method => p_http_method,
+ p_body => p_body,
+ p_username => p_username,
+ p_password => p_password,
+ p_wallet_path => g_wallet_path,
+ p_wallet_pwd => g_wallet_password
+ );
+
+ return l_returnvalue;
+
+end make_request;
+
+
+function decode_json_value (p_json_value in varchar2) return varchar2
+as
+ l_returnvalue varchar2(32000);
+begin
+
+ /*
+
+ Purpose: decode JSON value
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 26.01.2010 Created
+
+ */
+
+ l_returnvalue := replace(p_json_value, '\''', '''');
+ l_returnvalue := replace(l_returnvalue, '\"', '"');
+ l_returnvalue := replace(l_returnvalue, '\b', chr(9)); -- backspace
+ l_returnvalue := replace(l_returnvalue, '\t', chr(9)); -- tab
+ l_returnvalue := replace(l_returnvalue, '\n', chr(10)); -- line feed
+ l_returnvalue := replace(l_returnvalue, '\f', chr(12)); -- form feed
+ l_returnvalue := replace(l_returnvalue, '\r', chr(13)); -- carriage return
+
+ l_returnvalue := unistr(replace(l_returnvalue, '\u', '\')); -- unicode character
+
+ return l_returnvalue;
+
+end decode_json_value;
+
+
+function encode_json_value (p_value in varchar2) return varchar2
+as
+ l_returnvalue varchar2(32000);
+begin
+
+ /*
+
+ Purpose: encode JSON value
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 19.04.2013 Created
+ MBR 15.04.2015 Handle unicode chars properly, based on code from https://technology.amis.nl/wp-content/uploads/2015/03/json_agg.txt
+
+ */
+
+ l_returnvalue := asciistr(p_value);
+ l_returnvalue := replace(l_returnvalue, '\', '\u');
+ l_returnvalue := replace(l_returnvalue, '"', '\"');
+ l_returnvalue := replace(l_returnvalue, '\u005C', '\\');
+ l_returnvalue := replace(l_returnvalue, '/', '\/');
+ l_returnvalue := replace(l_returnvalue, '''', '\''');
+ l_returnvalue := replace(l_returnvalue, chr(8), '\b'); -- backspace
+ l_returnvalue := replace(l_returnvalue, chr(9), '\t'); -- tab
+ l_returnvalue := replace(l_returnvalue, chr(10), '\n'); -- line feed
+ l_returnvalue := replace(l_returnvalue, chr(12), '\f'); -- form feed
+ l_returnvalue := replace(l_returnvalue, chr(13), '\r'); -- carriage return
+
+ return l_returnvalue;
+
+end encode_json_value;
+
+
+function encode_json_boolean (p_value in boolean) return varchar2
+as
+ l_returnvalue varchar2(32000);
+begin
+
+ /*
+
+ Purpose: encode JSON boolean value
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 17.11.2015 Created
+
+ */
+
+ if p_value then
+ l_returnvalue := 'true';
+ else
+ l_returnvalue := 'false';
+ end if;
+
+ return l_returnvalue;
+
+end encode_json_boolean;
+
+
+function get_json_value (p_data in clob,
+ p_name in varchar2) return varchar2
+as
+ l_returnvalue varchar2(4000) := null;
+ l_start_pos pls_integer;
+ l_end_pos pls_integer;
+begin
+
+ /*
+
+ Purpose: get JSON value
+
+ Remarks: TODO: this is not a proper JSON parser, just a crude string parser, but will do for now. Refactor using APEX_JSON later...
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 26.01.2010 Created
+ MBR 21.05.2010 Trim strings
+
+ */
+
+ -- assumes that values are always enclosed in double quotes (no null values)
+
+ l_start_pos := instr(p_data, '"' || p_name || '":"');
+
+ if l_start_pos > 0 then
+ l_start_pos := l_start_pos + length ('"' || p_name || '":"');
+ l_end_pos := instr(p_data, '",', l_start_pos);
+ if l_end_pos = 0 then
+ l_end_pos := instr(p_data, '"}', l_start_pos);
+ end if;
+
+ l_returnvalue := substr(p_data, l_start_pos, l_end_pos - l_start_pos);
+ l_returnvalue := trim(replace(l_returnvalue, chr(160), ''));
+
+ end if;
+
+ if l_returnvalue is not null then
+ l_returnvalue := substr(decode_json_value (l_returnvalue),1,4000);
+ end if;
+
+ return l_returnvalue;
+
+end get_json_value;
+
+
+procedure check_response_for_errors (p_response in clob)
+as
+ l_error_name string_util_pkg.t_max_pl_varchar2;
+ l_error_message string_util_pkg.t_max_pl_varchar2;
+ l_error_info_url string_util_pkg.t_max_pl_varchar2;
+begin
+
+ /*
+
+ Purpose: check response for errors
+
+ Remarks: see https://developer.paypal.com/webapps/developer/docs/api/#errors
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ -- TODO: should pass the HTTP error code to this procedure as well (is it possible to get it via apex_web_service.g_headers???), for now just check response body
+
+ debug_pkg.printf('response length = %1', length(p_response));
+ debug_pkg.printf('first 32K characters of response = %1', substr(p_response,1,32000));
+
+ -- note: this type of error response is not mentioned in the docs (linked above), but has been seen "in the wild"
+ l_error_name := get_json_value (p_response, 'error');
+ l_error_message := get_json_value (p_response, 'error_description');
+
+ if l_error_name is not null then
+ raise_application_error (-20000, 'The PayPal API returned error ' || l_error_name || ': ' || l_error_message, true);
+ end if;
+
+ -- check for errors as described by API documentation
+ l_error_name := get_json_value (p_response, 'name');
+ l_error_message := get_json_value (p_response, 'message');
+ l_error_info_url := get_json_value (p_response, 'information_link');
+
+ if l_error_name is not null then
+ raise_application_error (-20000, 'The PayPal API returned error ' || l_error_name || ': ' || l_error_message || ', see ' || l_error_info_url, true);
+ end if;
+
+end check_response_for_errors;
+
+
+procedure switch_to_sandbox
+as
+begin
+
+ /*
+
+ Purpose: switch to sandbox (test) environment
+
+ Remarks: the default environment is live (production), use this procedure to switch to the sandbox for testing
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ g_api_base_url := g_api_base_url_sandbox;
+
+end switch_to_sandbox;
+
+
+procedure set_wallet (p_wallet_path in varchar2,
+ p_wallet_password in varchar2)
+as
+begin
+
+ /*
+
+ Purpose: set SSL wallet properties
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ g_wallet_path := p_wallet_path;
+ g_wallet_password := p_wallet_password;
+
+end set_wallet;
+
+
+function get_access_token (p_client_id in varchar2,
+ p_secret in varchar2) return t_access_token
+as
+ l_request clob;
+ l_response clob;
+ l_returnvalue t_access_token;
+begin
+
+ /*
+
+ Purpose: get access token for other API requests
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ l_request := 'grant_type=client_credentials';
+
+ l_response := make_request (p_url => g_api_base_url || '/v1/oauth2/token', p_body => l_request, p_username => p_client_id, p_password => p_secret);
+
+ check_response_for_errors (l_response);
+
+ l_returnvalue.access_token := get_json_value (l_response, 'access_token');
+ l_returnvalue.token_type := get_json_value (l_response, 'token_type');
+ -- TODO: retrieving this value fails because the current JSON parser only handles strings (ie values in double quotes), will be fixed by using APEX_JSON as the parser
+ l_returnvalue.duration_seconds := to_number(get_json_value (l_response, 'expires_in'));
+ l_returnvalue.created_date := sysdate;
+ l_returnvalue.expires_date := l_returnvalue.created_date + (l_returnvalue.duration_seconds / (60*60));
+
+ return l_returnvalue;
+
+end get_access_token;
+
+
+function get_payment_from_response (p_response in clob) return t_payment
+as
+ l_clob clob;
+ l_end_pos pls_integer;
+ l_returnvalue t_payment;
+begin
+
+ /*
+
+ Purpose: parse the response into a payment record
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ l_returnvalue.payment_id := get_json_value (p_response, 'id');
+ l_returnvalue.intent := get_json_value (p_response, 'intent');
+ l_returnvalue.state := get_json_value (p_response, 'state');
+
+ -- TODO: this ain't pretty, use a real JSON parser (APEX_JSON) instead... ! (code will break if rel/href tags switch position)
+
+ l_end_pos := instr(p_response, '"approval_url"');
+ if l_end_pos > 0 then
+ l_clob := p_response;
+ l_clob := substr(l_clob, 1, l_end_pos);
+ l_returnvalue.approval_url := substr(l_clob, instr(l_clob, '"href"', -1));
+ l_returnvalue.approval_url := get_json_value (l_returnvalue.approval_url, 'href');
+ end if;
+
+ return l_returnvalue;
+
+end get_payment_from_response;
+
+
+function create_payment (p_access_token in t_access_token,
+ p_amount in number,
+ p_currency in varchar2,
+ p_description in varchar2,
+ p_return_url in varchar2,
+ p_cancel_url in varchar2,
+ p_payment_experience_id in varchar2 := null) return t_payment
+as
+ l_request clob;
+ l_response clob;
+ l_returnvalue t_payment;
+begin
+
+ /*
+
+ Purpose: create payment
+
+ Remarks: after calling this, redirect the user to the "approval_url" on the PayPal site,
+ so that the user can approve the payment.
+ The user must approve the payment before you can execute and complete the sale.
+
+ When the user approves the payment, PayPal redirects the user to the "return_url"
+ that was specified when the payment was created. A payer ID is appended to the return URL, as PayerID:
+ http://?token=EC-60U79048BN7719609(ampersand)PayerID=7E7MGXCWTTKK2
+ The token value appended to the return URL is not needed when you execute the payment.
+
+ To execute the payment after the user's approval, make a call to execute_payment and pass the payer_id received via the return_url
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+ MBR 17.11.2015 Added optional payment experience parameter
+
+ */
+
+ -- TODO: use a JSON builder to generate the request
+
+ l_request := '{
+ "intent":"sale",' || case when p_payment_experience_id is not null then '"experience_profile_id":"' || p_payment_experience_id || '",' end ||
+ '"redirect_urls":{
+ "return_url":"' || encode_json_value (p_return_url) || '",
+ "cancel_url":"' || encode_json_value (p_cancel_url) || '"
+ },
+ "payer":{
+ "payment_method":"paypal"
+ },
+ "transactions":[
+ {
+ "amount":{
+ "total":"' || trim(to_char(p_amount, '999999D99', 'NLS_NUMERIC_CHARACTERS = ''. ''')) || '",
+ "currency":"' || p_currency || '"
+ },
+ "description":"' || encode_json_value (p_description) || '"
+ }
+ ]
+} ]
+}';
+
+ l_response := make_request (p_url => g_api_base_url || '/v1/payments/payment', p_body => l_request, p_access_token => p_access_token);
+
+ check_response_for_errors (l_response);
+
+ l_returnvalue := get_payment_from_response (l_response);
+
+ return l_returnvalue;
+
+end create_payment;
+
+
+function execute_payment (p_access_token in t_access_token,
+ p_payment_id in varchar2,
+ p_payer_id in varchar2) return t_payment
+as
+ l_request clob;
+ l_response clob;
+ l_returnvalue t_payment;
+begin
+
+ /*
+
+ Purpose: execute payment
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ l_request := '{"payer_id":"' || p_payer_id || '"}';
+
+ l_response := make_request (p_url => g_api_base_url || '/v1/payments/payment/' || p_payment_id || '/execute/', p_body => l_request, p_access_token => p_access_token);
+
+ check_response_for_errors (l_response);
+
+ l_returnvalue := get_payment_from_response (l_response);
+
+ return l_returnvalue;
+
+end execute_payment;
+
+
+function get_payment (p_access_token in t_access_token,
+ p_payment_id in varchar2) return t_payment
+as
+ l_response clob;
+ l_returnvalue t_payment;
+begin
+
+ /*
+
+ Purpose: get payment
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+
+ */
+
+ l_response := make_request (p_url => g_api_base_url || '/v1/payments/payment/' || p_payment_id, p_http_method => 'GET', p_access_token => p_access_token);
+
+ check_response_for_errors (l_response);
+
+ l_returnvalue := get_payment_from_response (l_response);
+
+ return l_returnvalue;
+
+end get_payment;
+
+
+function create_payment_experience (p_access_token in t_access_token,
+ p_payment_experience in t_payment_experience) return varchar2
+as
+ l_request clob;
+ l_response clob;
+ l_returnvalue varchar2(255);
+begin
+
+ /*
+
+ Purpose: create payment experience
+
+ Remarks: see https://developer.paypal.com/docs/integration/direct/rest-experience-overview/
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 17.11.2015 Created
+
+ */
+
+ -- TODO: use a JSON builder to generate the request
+
+ l_request := '{
+ "name":"' || encode_json_value (p_payment_experience.payment_experience_name) || '",
+ "presentation":{
+ "brand_name":"' || encode_json_value (p_payment_experience.presentation.brand_name) || '",
+ "logo_image":"' || encode_json_value (p_payment_experience.presentation.logo_image) || '",
+ "locale_code":"' || encode_json_value (p_payment_experience.presentation.locale_code) || '"
+ },
+ "input_fields":{
+ "allow_note":"' || encode_json_boolean (p_payment_experience.input_fields.allow_note) || '",
+ "no_shipping":"' || p_payment_experience.input_fields.no_shipping || '",
+ "address_override":"' || p_payment_experience.input_fields.address_override || '"
+ },
+ "flow_config":{
+ "landing_page_type":"' || encode_json_value (p_payment_experience.flow_config.landing_page_type) || '"
+ }
+}';
+
+ l_response := make_request (p_url => g_api_base_url || '/v1/payment-experience/web-profiles', p_body => l_request, p_access_token => p_access_token);
+
+ -- TODO: according to the docs, the response should only contain "id",
+ -- but the response actually contains the "name" of the payment experience, which throws a false error
+ --check_response_for_errors (l_response);
+
+ l_returnvalue := get_json_value (l_response, 'id');
+
+ return l_returnvalue;
+
+end create_payment_experience;
+
+
+procedure delete_payment_experience (p_access_token in t_access_token,
+ p_payment_experience_id in varchar2)
+as
+ l_response clob;
+begin
+
+ /*
+
+ Purpose: delete payment experience
+
+ Remarks:
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 17.11.2015 Created
+
+ */
+
+ l_response := make_request (p_url => g_api_base_url || '/v1/payment-experience/web-profiles/' || p_payment_experience_id, p_http_method => 'DELETE', p_access_token => p_access_token);
+
+ check_response_for_errors (l_response);
+
+end delete_payment_experience;
+
+
+end paypal_util_pkg;
+/
+
diff --git a/ora/paypal_util_pkg.pks b/ora/paypal_util_pkg.pks
new file mode 100644
index 0000000..c6953d6
--- /dev/null
+++ b/ora/paypal_util_pkg.pks
@@ -0,0 +1,115 @@
+create or replace package paypal_util_pkg
+as
+
+ /*
+
+ Purpose: Package handles PayPal REST API
+
+ Remarks: see https://developer.paypal.com/webapps/developer/docs/api/
+ see https://developer.paypal.com/webapps/developer/docs/integration/direct/make-your-first-call/
+ see https://devtools-paypal.com/hateoas/index.html
+ see https://www.youtube.com/watch?v=EdkQahMUvAY
+
+ Who Date Description
+ ------ ---------- --------------------------------
+ MBR 23.08.2014 Created
+ MBR 06.03.2016 Procedure to set API base URL
+
+ */
+
+ -- access token
+ type t_access_token is record (
+ access_token varchar2(4000),
+ token_type varchar2(255),
+ duration_seconds number,
+ created_date date,
+ expires_date date
+ );
+
+ -- payment
+ type t_payment is record (
+ payment_id varchar2(255),
+ intent varchar2(255),
+ state varchar2(255),
+ approval_url varchar2(4000)
+ );
+
+ -- payment experience flow config
+ type t_pe_flow_config is record (
+ landing_page_type varchar2(255)
+ );
+
+ -- payment experience input fields
+ type t_pe_input_fields is record (
+ allow_note boolean,
+ no_shipping pls_integer,
+ address_override pls_integer
+ );
+
+ -- payment experience presentation
+ type t_pe_presentation is record (
+ brand_name varchar2(255),
+ logo_image varchar2(255),
+ locale_code varchar2(255)
+ );
+
+ -- payment web experience profile
+ type t_payment_experience is record (
+ payment_experience_id varchar2(255),
+ payment_experience_name varchar2(255),
+ flow_config t_pe_flow_config,
+ input_fields t_pe_input_fields,
+ presentation t_pe_presentation
+ );
+
+ -- payment states
+ g_state_created constant varchar2(255) := 'created';
+ g_state_approved constant varchar2(255) := 'approved';
+ g_state_failed constant varchar2(255) := 'failed';
+ g_state_canceled constant varchar2(255) := 'canceled';
+ g_state_expired constant varchar2(255) := 'expired';
+
+ -- set API base URL
+ procedure set_api_base_url (p_sandbox_url in varchar2,
+ p_live_url in varchar2);
+
+ -- switch to sandbox (test) environment
+ procedure switch_to_sandbox;
+
+ -- set SSL wallet properties
+ procedure set_wallet (p_wallet_path in varchar2,
+ p_wallet_password in varchar2);
+
+ -- get access token for other API requests
+ function get_access_token (p_client_id in varchar2,
+ p_secret in varchar2) return t_access_token;
+
+ -- create payment
+ function create_payment (p_access_token in t_access_token,
+ p_amount in number,
+ p_currency in varchar2,
+ p_description in varchar2,
+ p_return_url in varchar2,
+ p_cancel_url in varchar2,
+ p_payment_experience_id in varchar2 := null) return t_payment;
+
+ -- execute payment
+ function execute_payment (p_access_token in t_access_token,
+ p_payment_id in varchar2,
+ p_payer_id in varchar2) return t_payment;
+
+ -- get payment
+ function get_payment (p_access_token in t_access_token,
+ p_payment_id in varchar2) return t_payment;
+
+ -- create payment experience
+ function create_payment_experience (p_access_token in t_access_token,
+ p_payment_experience in t_payment_experience) return varchar2;
+
+ -- delete payment experience
+ procedure delete_payment_experience (p_access_token in t_access_token,
+ p_payment_experience_id in varchar2);
+
+end paypal_util_pkg;
+/
+
diff --git a/ora/xlsx_builder_pkg.pkb b/ora/xlsx_builder_pkg.pkb
index ae28410..8f4ad26 100755
--- a/ora/xlsx_builder_pkg.pkb
+++ b/ora/xlsx_builder_pkg.pkb
@@ -1,5 +1,9 @@
CREATE OR REPLACE package body xlsx_builder_pkg
as
+--
+ c_LOCAL_FILE_HEADER constant raw(4) := hextoraw( '504B0304' ); -- Local file header signature
+ c_END_OF_CENTRAL_DIRECTORY constant raw(4) := hextoraw( '504B0506' ); -- End of central directory signature
+--
type tp_XF_fmt is record
( numFmtId pls_integer
, fontId pls_integer
@@ -10,7 +14,6 @@ as
type tp_col_fmts is table of tp_XF_fmt index by pls_integer;
type tp_row_fmts is table of tp_XF_fmt index by pls_integer;
type tp_widths is table of number index by pls_integer;
- type tp_strings is table of varchar2(32767) index by pls_integer;
type tp_cell is record
( value number
, style varchar2(50)
@@ -29,11 +32,11 @@ as
, url varchar2(1000)
);
type tp_hyperlinks is table of tp_hyperlink index by pls_integer;
- subtype tp_author is varchar2(32767);
+ subtype tp_author is varchar2(32767 char);
type tp_authors is table of pls_integer index by tp_author;
authors tp_authors;
type tp_comment is record
- ( text varchar2(32767)
+ ( text varchar2(32767 char)
, author tp_author
, row pls_integer
, column pls_integer
@@ -42,6 +45,21 @@ as
);
type tp_comments is table of tp_comment index by pls_integer;
type tp_mergecells is table of varchar2(21) index by pls_integer;
+ type tp_validation is record
+ ( type varchar2(10)
+ , errorstyle varchar2(32)
+ , showinputmessage boolean
+ , prompt varchar2(32767 char)
+ , title varchar2(32767 char)
+ , error_title varchar2(32767 char)
+ , error_txt varchar2(32767 char)
+ , showerrormessage boolean
+ , formula1 varchar2(32767 char)
+ , formula2 varchar2(32767 char)
+ , allowBlank boolean
+ , sqref varchar2(32767 char)
+ );
+ type tp_validations is table of tp_validation index by pls_integer;
type tp_sheet is record
( rows tp_rows
, widths tp_widths
@@ -54,6 +72,7 @@ as
, row_fmts tp_row_fmts
, comments tp_comments
, mergecells tp_mergecells
+ , validations tp_validations
);
type tp_sheets is table of tp_sheet index by pls_integer;
type tp_numFmt is record
@@ -72,6 +91,7 @@ as
, family pls_integer
, fontsize number
, theme pls_integer
+ , RGB varchar2(8)
, underline boolean
, italic boolean
, bold boolean
@@ -85,15 +105,26 @@ as
);
type tp_borders is table of tp_border index by pls_integer;
type tp_numFmtIndexes is table of pls_integer index by pls_integer;
+ type tp_strings is table of pls_integer index by varchar2(32767 char);
+ type tp_str_ind is table of varchar2(32767 char) index by pls_integer;
+ type tp_defined_name is record
+ ( name varchar2(32767 char)
+ , ref varchar2(32767 char)
+ , sheet pls_integer
+ );
+ type tp_defined_names is table of tp_defined_name index by pls_integer;
type tp_book is record
( sheets tp_sheets
, strings tp_strings
+ , str_ind tp_str_ind
+ , str_cnt pls_integer := 0
, fonts tp_fonts
, fills tp_fills
, borders tp_borders
, numFmts tp_numFmts
, cellXfs tp_cellXfs
, numFmtIndexes tp_numFmtIndexes
+ , defined_names tp_defined_names
);
workbook tp_book;
--
@@ -121,6 +152,13 @@ as
end loop;
utl_file.fclose( t_fh );
end;
+--
+ function raw2num( p_raw raw, p_len integer, p_pos integer )
+ return number
+ is
+ begin
+ return utl_raw.cast_to_binary_integer( utl_raw.substr( p_raw, p_pos, p_len ), utl_raw.little_endian );
+ end;
--
function little_endian( p_big number, p_bytes pls_integer := 4 )
return raw
@@ -128,29 +166,58 @@ as
begin
return utl_raw.substr( utl_raw.cast_from_binary_integer( p_big, utl_raw.little_endian ), 1, p_bytes );
end;
+--
+ function blob2num( p_blob blob, p_len integer, p_pos integer )
+ return number
+ is
+ begin
+ return utl_raw.cast_to_binary_integer( dbms_lob.substr( p_blob, p_len, p_pos ), utl_raw.little_endian );
+ end;
--
procedure add1file
- ( p_zipped_blob in out nocopy blob
+ ( p_zipped_blob in out blob
, p_name varchar2
, p_content blob
)
is
t_now date;
t_blob blob;
+ t_len integer;
t_clen integer;
+ t_crc32 raw(4) := hextoraw( '00000000' );
+ t_compressed boolean := false;
+ t_name raw(32767);
begin
t_now := sysdate;
- t_blob := utl_compress.lz_compress( p_content );
- t_clen := dbms_lob.getlength( t_blob );
+ t_len := nvl( dbms_lob.getlength( p_content ), 0 );
+ if t_len > 0
+ then
+ t_blob := utl_compress.lz_compress( p_content );
+ t_clen := dbms_lob.getlength( t_blob ) - 18;
+ t_compressed := t_clen < t_len;
+ t_crc32 := dbms_lob.substr( t_blob, 4, t_clen + 11 );
+ end if;
+ if not t_compressed
+ then
+ t_clen := t_len;
+ t_blob := p_content;
+ end if;
if p_zipped_blob is null
then
dbms_lob.createtemporary( p_zipped_blob, true );
end if;
+ t_name := utl_i18n.string_to_raw( p_name, 'AL32UTF8' );
dbms_lob.append( p_zipped_blob
- , utl_raw.concat( hextoraw( '504B0304' ) -- Local file header signature
- , hextoraw( '1400' ) -- version 2.0
- , hextoraw( '0000' ) -- no General purpose bits
- , hextoraw( '0800' ) -- deflate
+ , utl_raw.concat( c_LOCAL_FILE_HEADER -- Local file header signature
+ , hextoraw( '1400' ) -- version 2.0
+ , case when t_name = utl_i18n.string_to_raw( p_name, 'US8PC437' )
+ then hextoraw( '0000' ) -- no General purpose bits
+ else hextoraw( '0008' ) -- set Language encoding flag (EFS)
+ end
+ , case when t_compressed
+ then hextoraw( '0800' ) -- deflate
+ else hextoraw( '0000' ) -- stored
+ end
, little_endian( to_number( to_char( t_now, 'ss' ) ) / 2
+ to_number( to_char( t_now, 'mi' ) ) * 32
+ to_number( to_char( t_now, 'hh24' ) ) * 2048
@@ -161,29 +228,38 @@ as
+ ( to_number( to_char( t_now, 'yyyy' ) ) - 1980 ) * 512
, 2
) -- File last modification date
- , dbms_lob.substr( t_blob, 4, t_clen - 7 ) -- CRC-32
- , little_endian( t_clen - 18 ) -- compressed size
- , little_endian( dbms_lob.getlength( p_content ) ) -- uncompressed size
- , little_endian( length( p_name ), 2 ) -- File name length
- , hextoraw( '0000' ) -- Extra field length
- , utl_raw.cast_to_raw( p_name ) -- File name
+ , t_crc32 -- CRC-32
+ , little_endian( t_clen ) -- compressed size
+ , little_endian( t_len ) -- uncompressed size
+ , little_endian( utl_raw.length( t_name ), 2 ) -- File name length
+ , hextoraw( '0000' ) -- Extra field length
+ , t_name -- File name
)
);
- dbms_lob.copy( p_zipped_blob, t_blob, t_clen - 18, dbms_lob.getlength( p_zipped_blob ) + 1, 11 ); -- compressed content
- dbms_lob.freetemporary( t_blob );
+ if t_compressed
+ then
+ dbms_lob.copy( p_zipped_blob, t_blob, t_clen, dbms_lob.getlength( p_zipped_blob ) + 1, 11 ); -- compressed content
+ elsif t_clen > 0
+ then
+ dbms_lob.copy( p_zipped_blob, t_blob, t_clen, dbms_lob.getlength( p_zipped_blob ) + 1, 1 ); -- content
+ end if;
+ if dbms_lob.istemporary( t_blob ) = 1
+ then
+ dbms_lob.freetemporary( t_blob );
+ end if;
end;
--
- procedure finish_zip( p_zipped_blob in out nocopy blob )
+ procedure finish_zip( p_zipped_blob in out blob )
is
t_cnt pls_integer := 0;
t_offs integer;
t_offs_dir_header integer;
t_offs_end_header integer;
- t_comment raw(32767) := utl_raw.cast_to_raw( 'Zip-implementation by Anton Scheffer' );
+ t_comment raw(32767) := utl_raw.cast_to_raw( 'Implementation by Anton Scheffer' );
begin
t_offs_dir_header := dbms_lob.getlength( p_zipped_blob );
- t_offs := dbms_lob.instr( p_zipped_blob, hextoraw( '504B0304' ), 1 );
- while t_offs > 0
+ t_offs := 1;
+ while dbms_lob.substr( p_zipped_blob, utl_raw.length( c_LOCAL_FILE_HEADER ), t_offs ) = c_LOCAL_FILE_HEADER
loop
t_cnt := t_cnt + 1;
dbms_lob.append( p_zipped_blob
@@ -192,26 +268,39 @@ as
, dbms_lob.substr( p_zipped_blob, 26, t_offs + 4 )
, hextoraw( '0000' ) -- File comment length
, hextoraw( '0000' ) -- Disk number where file starts
- , hextoraw( '0100' ) -- Internal file attributes
- , hextoraw( '2000B681' ) -- External file attributes
+ , hextoraw( '0000' ) -- Internal file attributes =>
+ -- 0000 binary file
+ -- 0100 (ascii)text file
+ , case
+ when dbms_lob.substr( p_zipped_blob
+ , 1
+ , t_offs + 30 + blob2num( p_zipped_blob, 2, t_offs + 26 ) - 1
+ ) in ( hextoraw( '2F' ) -- /
+ , hextoraw( '5C' ) -- \
+ )
+ then hextoraw( '10000000' ) -- a directory/folder
+ else hextoraw( '2000B681' ) -- a file
+ end -- External file attributes
, little_endian( t_offs - 1 ) -- Relative offset of local file header
, dbms_lob.substr( p_zipped_blob
- , utl_raw.cast_to_binary_integer( dbms_lob.substr( p_zipped_blob, 2, t_offs + 26 ), utl_raw.little_endian )
+ , blob2num( p_zipped_blob, 2, t_offs + 26 )
, t_offs + 30
) -- File name
)
);
- t_offs := dbms_lob.instr( p_zipped_blob, hextoraw( '504B0304' ), t_offs + 32 );
+ t_offs := t_offs + 30 + blob2num( p_zipped_blob, 4, t_offs + 18 ) -- compressed size
+ + blob2num( p_zipped_blob, 2, t_offs + 26 ) -- File name length
+ + blob2num( p_zipped_blob, 2, t_offs + 28 ); -- Extra field length
end loop;
t_offs_end_header := dbms_lob.getlength( p_zipped_blob );
dbms_lob.append( p_zipped_blob
- , utl_raw.concat( hextoraw( '504B0506' ) -- End of central directory signature
+ , utl_raw.concat( c_END_OF_CENTRAL_DIRECTORY -- End of central directory signature
, hextoraw( '0000' ) -- Number of this disk
, hextoraw( '0000' ) -- Disk where central directory starts
, little_endian( t_cnt, 2 ) -- Number of central directory records on this disk
, little_endian( t_cnt, 2 ) -- Total number of central directory records
, little_endian( t_offs_end_header - t_offs_dir_header ) -- Size of central directory
- , little_endian( t_offs_dir_header ) -- Relative offset of local file header
+ , little_endian( t_offs_dir_header ) -- Offset of start of central directory, relative to start of archive
, little_endian( nvl( utl_raw.length( t_comment ), 0 ), 2 ) -- ZIP file comment length
, t_comment
)
@@ -258,13 +347,16 @@ as
workbook.sheets( s ).row_fmts.delete();
workbook.sheets( s ).comments.delete();
workbook.sheets( s ).mergecells.delete();
+ workbook.sheets( s ).validations.delete();
end loop;
workbook.strings.delete();
+ workbook.str_ind.delete();
workbook.fonts.delete();
workbook.fills.delete();
workbook.borders.delete();
workbook.numFmts.delete();
workbook.cellXfs.delete();
+ workbook.defined_names.delete();
workbook := null;
end;
--
@@ -274,6 +366,10 @@ as
t_ind pls_integer;
begin
workbook.sheets( t_nr ).name := nvl( dbms_xmlgen.convert( translate( p_sheetname, 'a/\[]*:?', 'a' ) ), 'Sheet' || t_nr );
+ if workbook.strings.count() = 0
+ then
+ workbook.str_cnt := 0;
+ end if;
if workbook.fonts.count() = 0
then
t_ind := get_font( 'Calibri' );
@@ -375,6 +471,7 @@ as
, p_underline boolean := false
, p_italic boolean := false
, p_bold boolean := false
+ , p_rgb varchar2 := null -- this is a hex ALPHA Red Green Blue value
)
return pls_integer
is
@@ -391,6 +488,9 @@ as
and workbook.fonts( f ).underline = p_underline
and workbook.fonts( f ).italic = p_italic
and workbook.fonts( f ).bold = p_bold
+ and ( workbook.fonts( f ).rgb = p_rgb
+ or ( workbook.fonts( f ).rgb is null and p_rgb is null )
+ )
)
then
return f;
@@ -405,6 +505,7 @@ as
workbook.fonts( t_ind ).underline := p_underline;
workbook.fonts( t_ind ).italic := p_italic;
workbook.fonts( t_ind ).bold := p_bold;
+ workbook.fonts( t_ind ).rgb := p_rgb;
return t_ind;
end;
--
@@ -548,19 +649,6 @@ as
end if;
return 's="' || t_XfId || '"';
end;
---
- function clean_string (p_string in varchar2)
- return varchar2
- is
- invalid_ascii constant varchar2(32) :=
- chr(00)||chr(01)||chr(02)||chr(03)||chr(04)||chr(05)||chr(06)||chr(07)||
- chr(08)|| chr(11)||chr(12)|| chr(14)||chr(15)||
- chr(16)||chr(17)||chr(18)||chr(19)||chr(20)||chr(21)||chr(22)||chr(23)||
- chr(24)||chr(25)||chr(26)||chr(27)||chr(28)||chr(29)||chr(30)||chr(31)
- ;
- begin
- return translate(translate( p_string, invalid_ascii, chr(1)), chr(0)||chr(1), ' ');
- end;
--
procedure cell
( p_col pls_integer
@@ -580,6 +668,23 @@ as
workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := null;
workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := get_XfId( t_sheet, p_col, p_row, p_numFmtId, p_fontId, p_fillId, p_borderId, p_alignment );
end;
+--
+ function add_string( p_string varchar2 )
+ return pls_integer
+ is
+ t_cnt pls_integer;
+ begin
+ if workbook.strings.exists( p_string )
+ then
+ t_cnt := workbook.strings( p_string );
+ else
+ t_cnt := workbook.strings.count();
+ workbook.str_ind( t_cnt ) := p_string;
+ workbook.strings( nvl( p_string, '' ) ) := t_cnt;
+ end if;
+ workbook.str_cnt := workbook.str_cnt + 1;
+ return t_cnt;
+ end;
--
procedure cell
( p_col pls_integer
@@ -596,8 +701,7 @@ as
t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
t_alignment tp_alignment := p_alignment;
begin
- workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := workbook.strings.count();
- workbook.strings( workbook.strings.count() ) := clean_string( p_value );
+ workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := add_string( p_value );
if t_alignment.wrapText is null and instr( p_value, chr(13) ) > 0
then
t_alignment.wrapText := true;
@@ -645,8 +749,7 @@ as
t_ind pls_integer;
t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
begin
- workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := workbook.strings.count();
- workbook.strings( workbook.strings.count() ) := clean_string( nvl( p_value, p_url ) );
+ workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := add_string( nvl( p_value, p_url ) );
workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := 't="s" ' || get_XfId( t_sheet, p_col, p_row, '', get_font( 'Calibri', p_theme => 10, p_underline => true ) );
t_ind := workbook.sheets( t_sheet ).hyperlinks.count() + 1;
workbook.sheets( t_sheet ).hyperlinks( t_ind ).cell := alfan_col( p_col ) || p_row;
@@ -689,6 +792,111 @@ as
t_ind := workbook.sheets( t_sheet ).mergecells.count() + 1;
workbook.sheets( t_sheet ).mergecells( t_ind ) := alfan_col( p_tl_col ) || p_tl_row || ':' || alfan_col( p_br_col ) || p_br_row;
end;
+--
+ procedure add_validation
+ ( p_type varchar2
+ , p_sqref varchar2
+ , p_style varchar2 := 'stop' -- stop, warning, information
+ , p_formula1 varchar2 := null
+ , p_formula2 varchar2 := null
+ , p_title varchar2 := null
+ , p_prompt varchar := null
+ , p_show_error boolean := false
+ , p_error_title varchar2 := null
+ , p_error_txt varchar2 := null
+ , p_sheet pls_integer := null
+ )
+ is
+ t_ind pls_integer;
+ t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
+ begin
+ t_ind := workbook.sheets( t_sheet ).validations.count() + 1;
+ workbook.sheets( t_sheet ).validations( t_ind ).type := p_type;
+ workbook.sheets( t_sheet ).validations( t_ind ).errorstyle := p_style;
+ workbook.sheets( t_sheet ).validations( t_ind ).sqref := p_sqref;
+ workbook.sheets( t_sheet ).validations( t_ind ).formula1 := p_formula1;
+ workbook.sheets( t_sheet ).validations( t_ind ).error_title := p_error_title;
+ workbook.sheets( t_sheet ).validations( t_ind ).error_txt := p_error_txt;
+ workbook.sheets( t_sheet ).validations( t_ind ).title := p_title;
+ workbook.sheets( t_sheet ).validations( t_ind ).prompt := p_prompt;
+ workbook.sheets( t_sheet ).validations( t_ind ).showerrormessage := p_show_error;
+ end;
+--
+ procedure list_validation
+ ( p_sqref_col pls_integer
+ , p_sqref_row pls_integer
+ , p_tl_col pls_integer -- top left
+ , p_tl_row pls_integer
+ , p_br_col pls_integer -- bottom right
+ , p_br_row pls_integer
+ , p_style varchar2 := 'stop' -- stop, warning, information
+ , p_title varchar2 := null
+ , p_prompt varchar := null
+ , p_show_error boolean := false
+ , p_error_title varchar2 := null
+ , p_error_txt varchar2 := null
+ , p_sheet pls_integer := null
+ )
+ is
+ begin
+ add_validation( 'list'
+ , alfan_col( p_sqref_col ) || p_sqref_row
+ , p_style => lower( p_style )
+ , p_formula1 => '$' || alfan_col( p_tl_col ) || '$' || p_tl_row || ':$' || alfan_col( p_br_col ) || '$' || p_br_row
+ , p_title => p_title
+ , p_prompt => p_prompt
+ , p_show_error => p_show_error
+ , p_error_title => p_error_title
+ , p_error_txt => p_error_txt
+ , p_sheet => p_sheet
+ );
+ end;
+--
+ procedure list_validation
+ ( p_sqref_col pls_integer
+ , p_sqref_row pls_integer
+ , p_defined_name varchar2
+ , p_style varchar2 := 'stop' -- stop, warning, information
+ , p_title varchar2 := null
+ , p_prompt varchar := null
+ , p_show_error boolean := false
+ , p_error_title varchar2 := null
+ , p_error_txt varchar2 := null
+ , p_sheet pls_integer := null
+ )
+ is
+ begin
+ add_validation( 'list'
+ , alfan_col( p_sqref_col ) || p_sqref_row
+ , p_style => lower( p_style )
+ , p_formula1 => p_defined_name
+ , p_title => p_title
+ , p_prompt => p_prompt
+ , p_show_error => p_show_error
+ , p_error_title => p_error_title
+ , p_error_txt => p_error_txt
+ , p_sheet => p_sheet
+ );
+ end;
+--
+ procedure defined_name
+ ( p_tl_col pls_integer -- top left
+ , p_tl_row pls_integer
+ , p_br_col pls_integer -- bottom right
+ , p_br_row pls_integer
+ , p_name varchar2
+ , p_sheet pls_integer := null
+ , p_localsheet pls_integer := null
+ )
+ is
+ t_ind pls_integer;
+ t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
+ begin
+ t_ind := workbook.defined_names.count() + 1;
+ workbook.defined_names( t_ind ).name := p_name;
+ workbook.defined_names( t_ind ).ref := 'Sheet' || t_sheet || '!$' || alfan_col( p_tl_col ) || '$' || p_tl_row || ':$' || alfan_col( p_br_col ) || '$' || p_br_row;
+ workbook.defined_names( t_ind ).sheet := p_localsheet;
+ end;
--
procedure set_column_width
( p_col pls_integer
@@ -743,8 +951,10 @@ as
, p_sheet pls_integer := null
)
is
+ t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
begin
- workbook.sheets( nvl( p_sheet, workbook.sheets.count() ) ).freeze_rows := p_nr_rows;
+ workbook.sheets( t_sheet ).freeze_cols := null;
+ workbook.sheets( t_sheet ).freeze_rows := p_nr_rows;
end;
--
procedure freeze_cols
@@ -752,8 +962,22 @@ as
, p_sheet pls_integer := null
)
is
+ t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
begin
- workbook.sheets( nvl( p_sheet, workbook.sheets.count() ) ).freeze_cols := p_nr_cols;
+ workbook.sheets( t_sheet ).freeze_rows := null;
+ workbook.sheets( t_sheet ).freeze_cols := p_nr_cols;
+ end;
+--
+ procedure freeze_pane
+ ( p_col pls_integer
+ , p_row pls_integer
+ , p_sheet pls_integer := null
+ )
+ is
+ t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
+ begin
+ workbook.sheets( t_sheet ).freeze_rows := p_row;
+ workbook.sheets( t_sheet ).freeze_cols := p_col;
end;
--
procedure set_autofilter
@@ -767,52 +991,88 @@ as
t_ind pls_integer;
t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
begin
- t_ind := workbook.sheets( t_sheet ).autofilters.count() + 1;
+ t_ind := 1;
workbook.sheets( t_sheet ).autofilters( t_ind ).column_start := p_column_start;
workbook.sheets( t_sheet ).autofilters( t_ind ).column_end := p_column_end;
workbook.sheets( t_sheet ).autofilters( t_ind ).row_start := p_row_start;
workbook.sheets( t_sheet ).autofilters( t_ind ).row_end := p_row_end;
+ defined_name
+ ( p_column_start
+ , p_row_start
+ , p_column_end
+ , p_row_end
+ , '_xlnm._FilterDatabase'
+ , t_sheet
+ , t_sheet - 1
+ );
end;
--
- procedure add1xml
- ( p_excel in out nocopy blob
- , p_filename varchar2
- , p_xml clob
- )
- is
+/*
+ procedure add1xml
+ ( p_excel in out nocopy blob
+ , p_filename varchar2
+ , p_xml clob
+ )
+ is
t_tmp blob;
- l_count binary_integer;
- begin
+ c_step constant number := 24396;
+ begin
dbms_lob.createtemporary( t_tmp, true );
- l_count := trunc( length( p_xml ) / 4000 );
-
- if mod( length( p_xml ), 4000 ) = 0 then
- l_count := greatest(l_count - 1, 0);
- end if;
-
- for i in 0 .. l_count
- loop
- dbms_lob.append( t_tmp, utl_i18n.string_to_raw( substr( p_xml, i * 4000 + 1, 4000 ), 'AL32UTF8') );
- end loop;
- add1file( p_excel, p_filename, t_tmp );
- dbms_lob.freetemporary( t_tmp );
- end;
+ for i in 0 .. trunc( length( p_xml ) / c_step )
+ loop
+ dbms_lob.append( t_tmp, utl_i18n.string_to_raw( substr( p_xml, i * c_step + 1, c_step ), 'AL32UTF8' ) );
+ end loop;
+ add1file( p_excel, p_filename, t_tmp );
+ dbms_lob.freetemporary( t_tmp );
+ end;
+*/
+--
+ procedure add1xml
+ ( p_excel in out nocopy blob
+ , p_filename varchar2
+ , p_xml clob
+ )
+ is
+ t_tmp blob;
+ dest_offset integer := 1;
+ src_offset integer := 1;
+ lang_context integer;
+ warning integer;
+ begin
+ lang_context := dbms_lob.DEFAULT_LANG_CTX;
+ dbms_lob.createtemporary( t_tmp, true );
+ dbms_lob.converttoblob
+ ( t_tmp
+ , p_xml
+ , dbms_lob.lobmaxsize
+ , dest_offset
+ , src_offset
+ , nls_charset_id( 'AL32UTF8' )
+ , lang_context
+ , warning
+ );
+ add1file( p_excel, p_filename, t_tmp );
+ dbms_lob.freetemporary( t_tmp );
+ end;
--
function finish
return blob
is
t_excel blob;
t_xxx clob;
- t_tmp clob;
+ t_tmp varchar2(32767 char);
+ t_str varchar2(32767 char);
t_c number;
t_h number;
t_w number;
t_cw number;
- t_cell varchar2(1000);
+ t_cell varchar2(1000 char);
t_row_ind pls_integer;
t_col_min pls_integer;
t_col_max pls_integer;
t_col_ind pls_integer;
+ t_len pls_integer;
+ts timestamp := systimestamp;
begin
dbms_lob.createtemporary( t_excel, true );
t_xxx := '
@@ -907,10 +1167,13 @@ as
case when workbook.fonts( f ).italic then '' end ||
case when workbook.fonts( f ).underline then '' end ||
'
-
+
-
+
';
end loop;
t_xxx := t_xxx || '
@@ -979,10 +1242,20 @@ as
t_xxx := t_xxx || '
';
end loop;
- t_xxx := t_xxx || '
-
-
-';
+ t_xxx := t_xxx || '';
+ if workbook.defined_names.count() > 0
+ then
+ t_xxx := t_xxx || '';
+ for s in 1 .. workbook.defined_names.count()
+ loop
+ t_xxx := t_xxx || '
+' || workbook.defined_names( s ).ref || '';
+ end loop;
+ t_xxx := t_xxx || '';
+ end if;
+ t_xxx := t_xxx || '';
add1xml( t_excel, 'xl/workbook.xml', t_xxx );
t_xxx := '
@@ -1284,13 +1557,22 @@ as
';
- if workbook.sheets( s ).freeze_rows > 0
+ if workbook.sheets( s ).freeze_rows > 0 and workbook.sheets( s ).freeze_cols > 0
then
- t_xxx := t_xxx || '';
- end if;
- if workbook.sheets( s ).freeze_cols > 0
- then
- t_xxx := t_xxx || '';
+ t_xxx := t_xxx || ( ''
+ );
+ else
+ if workbook.sheets( s ).freeze_rows > 0
+ then
+ t_xxx := t_xxx || '';
+ end if;
+ if workbook.sheets( s ).freeze_cols > 0
+ then
+ t_xxx := t_xxx || '';
+ end if;
end if;
t_xxx := t_xxx || '
@@ -1302,7 +1584,7 @@ as
while t_col_ind is not null
loop
t_xxx := t_xxx ||
- '';
+ '';
t_col_ind := workbook.sheets( s ).widths.next( t_col_ind );
end loop;
t_xxx := t_xxx || '';
@@ -1313,26 +1595,31 @@ as
while t_row_ind is not null
loop
t_tmp := t_tmp || '';
+ t_len := length( t_tmp );
t_col_ind := workbook.sheets( s ).rows( t_row_ind ).first();
while t_col_ind is not null
loop
- t_cell := '';
- t_cell := t_cell || to_char( workbook.sheets( s ).rows( t_row_ind )( t_col_ind ).value, 'TM9', 'NLS_NUMERIC_CHARACTERS=.,' );
- t_cell := t_cell || '';
+ t_cell := ''
+ || to_char( workbook.sheets( s ).rows( t_row_ind )( t_col_ind ).value, 'TM9', 'NLS_NUMERIC_CHARACTERS=.,' )
+ || '';
+ if t_len > 32000
+ then
+ dbms_lob.writeappend( t_xxx, t_len, t_tmp );
+ t_tmp := null;
+ t_len := 0;
+ end if;
t_tmp := t_tmp || t_cell;
+ t_len := t_len + length( t_cell );
t_col_ind := workbook.sheets( s ).rows( t_row_ind ).next( t_col_ind );
end loop;
t_tmp := t_tmp || '
';
- if length( t_tmp ) > 8000
- then
- t_xxx := t_xxx || t_tmp;
- t_tmp := null;
- end if;
t_row_ind := workbook.sheets( s ).rows.next( t_row_ind );
end loop;
- t_xxx := t_xxx || t_tmp || '';
+ t_tmp := t_tmp || '';
+ t_len := length( t_tmp );
+ dbms_lob.writeappend( t_xxx, t_len, t_tmp );
for a in 1 .. workbook.sheets( s ).autofilters.count()
loop
t_xxx := t_xxx || '';
+ for m in 1 .. workbook.sheets( s ).validations.count()
+ loop
+ t_xxx := t_xxx || '';
+ if workbook.sheets( s ).validations( m ).formula1 is not null
+ then
+ t_xxx := t_xxx || '' || workbook.sheets( s ).validations( m ).formula1 || '';
+ end if;
+ if workbook.sheets( s ).validations( m ).formula2 is not null
+ then
+ t_xxx := t_xxx || '' || workbook.sheets( s ).validations( m ).formula2 || '';
+ end if;
+ t_xxx := t_xxx || '';
+ end loop;
+ t_xxx := t_xxx || '';
+ end if;
+--
if workbook.sheets( s ).hyperlinks.count() > 0
then
t_xxx := t_xxx || '';
@@ -1364,6 +1696,7 @@ as
then
t_xxx := t_xxx || '';
end if;
+--
t_xxx := t_xxx || '';
add1xml( t_excel, 'xl/worksheets/sheet' || s || '.xml', t_xxx );
if workbook.sheets( s ).hyperlinks.count() > 0 or workbook.sheets( s ).comments.count() > 0
@@ -1476,16 +1809,17 @@ style="position:absolute;margin-left:35.25pt;margin-top:3pt;z-index:' || to_char
t_xxx := t_xxx || '';
add1xml( t_excel, 'xl/_rels/workbook.xml.rels', t_xxx );
t_xxx := '
-';
+';
t_tmp := null;
- for i in 0 .. workbook.strings.count() - 1
+ for i in 0 .. workbook.str_ind.count() - 1
loop
- t_tmp := t_tmp || '' || dbms_xmlgen.convert( substr( workbook.strings( i ), 1, 32000 ) ) || '';
- if length( t_tmp ) > 8000
+ t_str := '' || dbms_xmlgen.convert( substr( workbook.str_ind( i ), 1, 32000 ) ) || '';
+ if length( t_tmp ) + length( t_str ) > 32000
then
t_xxx := t_xxx || t_tmp;
t_tmp := null;
end if;
+ t_tmp := t_tmp || t_str;
end loop;
t_xxx := t_xxx || t_tmp || '';
add1xml( t_excel, 'xl/sharedStrings.xml', t_xxx );
@@ -1567,7 +1901,10 @@ style="position:absolute;margin-left:35.25pt;margin-top:3pt;z-index:' || to_char
dbms_sql.column_value( t_c, c, n_tab );
for i in 0 .. t_r - 1
loop
- cell( c, t_cur_row + i, n_tab( i + n_tab.first() ), p_sheet => t_sheet );
+ if n_tab( i + n_tab.first() ) is not null
+ then
+ cell( c, t_cur_row + i, n_tab( i + n_tab.first() ), p_sheet => t_sheet );
+ end if;
end loop;
n_tab.delete;
when t_desc_tab( c ).col_type in ( 12, 178, 179, 180, 181 , 231 )
@@ -1575,7 +1912,10 @@ style="position:absolute;margin-left:35.25pt;margin-top:3pt;z-index:' || to_char
dbms_sql.column_value( t_c, c, d_tab );
for i in 0 .. t_r - 1
loop
- cell( c, t_cur_row + i, d_tab( i + d_tab.first() ), p_sheet => t_sheet );
+ if d_tab( i + d_tab.first() ) is not null
+ then
+ cell( c, t_cur_row + i, d_tab( i + d_tab.first() ), p_sheet => t_sheet );
+ end if;
end loop;
d_tab.delete;
when t_desc_tab( c ).col_type in ( 1, 8, 9, 96, 112 )
@@ -1583,7 +1923,10 @@ style="position:absolute;margin-left:35.25pt;margin-top:3pt;z-index:' || to_char
dbms_sql.column_value( t_c, c, v_tab );
for i in 0 .. t_r - 1
loop
- cell( c, t_cur_row + i, v_tab( i + v_tab.first() ), p_sheet => t_sheet );
+ if v_tab( i + v_tab.first() ) is not null
+ then
+ cell( c, t_cur_row + i, v_tab( i + v_tab.first() ), p_sheet => t_sheet );
+ end if;
end loop;
v_tab.delete;
else
diff --git a/ora/xlsx_builder_pkg.pks b/ora/xlsx_builder_pkg.pks
index 97c805c..67f922e 100755
--- a/ora/xlsx_builder_pkg.pks
+++ b/ora/xlsx_builder_pkg.pks
@@ -6,6 +6,7 @@ as
** Date: 19-02-2011
** Website: http://technology.amis.nl/blog
** See also: http://technology.amis.nl/blog/?p=10995
+** See also: https://technology.amis.nl/2011/02/19/create-an-excel-file-with-plsql/
**
** Changelog:
** Date: 21-02-2011
@@ -18,7 +19,43 @@ as
** Fixed issue with timezone's set to a region(name) instead of a offset
** Date: 08-04-2011
** Fixed issue with XML-escaping from text
+** Date: 27-05-2011
+** Added MIT-license
+** Date: 11-08-2011
+** Fixed NLS-issue with column width
+** Date: 29-09-2011
+** Added font color
+** Date: 16-10-2011
+** fixed bug in add_string
+** Date: 26-04-2012
+** Fixed set_autofilter (only one autofilter per sheet, added _xlnm._FilterDatabase)
+** Added list_validation = drop-down
+** Date: 27-08-2013
+** Added freeze_pane
**
+******************************************************************************
+******************************************************************************
+Copyright (C) 2011, 2012 by Anton Scheffer
+
+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.
+
+******************************************************************************
******************************************** */
--
type tp_alignment is record
@@ -45,6 +82,7 @@ as
, p_underline boolean := false
, p_italic boolean := false
, p_bold boolean := false
+ , p_rgb varchar2 := null -- this is a hex ALPHA Red Green Blue value
)
return pls_integer;
--
@@ -163,6 +201,45 @@ top
, p_br_row pls_integer
, p_sheet pls_integer := null
);
+--
+ procedure list_validation
+ ( p_sqref_col pls_integer
+ , p_sqref_row pls_integer
+ , p_tl_col pls_integer -- top left
+ , p_tl_row pls_integer
+ , p_br_col pls_integer -- bottom right
+ , p_br_row pls_integer
+ , p_style varchar2 := 'stop' -- stop, warning, information
+ , p_title varchar2 := null
+ , p_prompt varchar := null
+ , p_show_error boolean := false
+ , p_error_title varchar2 := null
+ , p_error_txt varchar2 := null
+ , p_sheet pls_integer := null
+ );
+--
+ procedure list_validation
+ ( p_sqref_col pls_integer
+ , p_sqref_row pls_integer
+ , p_defined_name varchar2
+ , p_style varchar2 := 'stop' -- stop, warning, information
+ , p_title varchar2 := null
+ , p_prompt varchar := null
+ , p_show_error boolean := false
+ , p_error_title varchar2 := null
+ , p_error_txt varchar2 := null
+ , p_sheet pls_integer := null
+ );
+--
+ procedure defined_name
+ ( p_tl_col pls_integer -- top left
+ , p_tl_row pls_integer
+ , p_br_col pls_integer -- bottom right
+ , p_br_row pls_integer
+ , p_name varchar2
+ , p_sheet pls_integer := null
+ , p_localsheet pls_integer := null
+ );
--
procedure set_column_width
( p_col pls_integer
@@ -199,6 +276,12 @@ top
( p_nr_cols pls_integer := 1
, p_sheet pls_integer := null
);
+--
+ procedure freeze_pane
+ ( p_col pls_integer
+ , p_row pls_integer
+ , p_sheet pls_integer := null
+ );
--
procedure set_autofilter
( p_column_start pls_integer := null
@@ -223,8 +306,5 @@ top
, p_filename varchar2 := null
, p_sheet pls_integer := null
);
---
-
-end xlsx_builder_pkg;
+end;
/
-
diff --git a/setup/grants.sql b/setup/grants.sql
index 0edbaa8..694d05b 100755
--- a/setup/grants.sql
+++ b/setup/grants.sql
@@ -4,3 +4,5 @@
-- required for NTLM utilities
grant execute on dbms_crypto to &&your_schema;
+-- Required for XLSX_BUILDER_PKG
+grant execute on sys.utl_file to &&your_schema;
diff --git a/setup/install.sql b/setup/install.sql
index 93470c4..03e66f0 100755
--- a/setup/install.sql
+++ b/setup/install.sql
@@ -34,6 +34,7 @@ prompt Creating package specifications
@../ora/ntlm_http_pkg.pks
@../ora/ooxml_util_pkg.pks
@../ora/owa_util_pkg.pks
+@../ora/paypal_util_pkg.pks
@../ora/pdf_builder_pkg.pks
@../ora/random_util_pkg.pks
@../ora/raw_util_pkg.pks
@@ -85,6 +86,7 @@ prompt Creating package bodies
@../ora/ntlm_http_pkg.pkb
@../ora/ooxml_util_pkg.pkb
@../ora/owa_util_pkg.pkb
+@../ora/paypal_util_pkg.pkb
@../ora/pdf_builder_pkg.pkb
@../ora/random_util_pkg.pkb
@../ora/raw_util_pkg.pkb
diff --git a/setup/install_paypal.sql b/setup/install_paypal.sql
new file mode 100755
index 0000000..b04f25e
--- /dev/null
+++ b/setup/install_paypal.sql
@@ -0,0 +1,15 @@
+
+set scan off;
+
+
+prompt Creating PAYPAL package specifications
+
+@../ora/paypal_util_pkg.pks
+
+prompt Creating PAYPAL package bodies
+
+@../ora/paypal_util_pkg.pkb
+
+
+prompt Done!
+