PL SQL Apex_JSON - Parse element name - plsql

I am trying to get the value from the following array -
{
"list:" [
{
"User.name":"AAA"
},
{
"User.Name":"BBB"
}
]
}
I tried to get the Name value but I got null -
for i in 1..Apex_Json.get_count('list') loop
l_name := Apex_Json.Get_varchar2('list[%d].User.Name', i);
end loop;
How can I get the name value?

"list" is json array of elements. AFAIK you can't access an element directly but you can loop through the array to check if an element with a specific name exists. Here is an example:
DECLARE
l_json_text VARCHAR2(4000);
l_json_values apex_json.t_values;
l_name VARCHAR2(4000);
BEGIN
l_json_text := q'!
{
"list": [
{
"User.name":"AAA"
},
{
"User.Name":"BBB"
}
]
}
!';
apex_json.parse(
p_values => l_json_values,
p_source => l_json_text
);
-- get nr of elements in the array and loop through it.
FOR r IN 1 .. APEX_JSON.get_count(p_path => 'list', p_values => l_json_values) LOOP
-- get the value for User.Name for this array index.
-- If it is doesn't exist it will be NULL
l_name := apex_json.get_varchar2(p_path => 'list[%d]."User.name"', p0 => r, p_values => l_json_values);
IF l_name IS NOT NULL THEN
DBMS_OUTPUT.put_line('"User.name":'||l_name);
END IF;
END LOOP;
END;
/
Note that there is a typo in your question json: "list:" [ should be "list": [

Related

An array of records containing variable-length strings

I would like to have a record with an integer and a variable-length string in it, something like this:
type Entry is
record
Value: Integer;
Label: String;
end record;
I ran into the issue that you can't put an unconstrained String in a record type, so following the advice at that link I tried
type Entry(Label_Length : Natural) is
record
Value: Integer;
Label: String(1..Label_Length);
end record;
But now the problem is, I want an array of these things:
Entries : Array(1..2) of Entry := (
(Label_Length => 0, Value => 1, Label => ""),
(Label_Length => 0, Value => 2, Label => "")
);
and I'm getting told
main.adb:17:28: unconstrained element type in array declaration
I just want to be able to declare a (constant) array of these things and type in the labels and values in an intuitive way (I already wasn't crazy about having to count string lengths and type in Label_Length by hand). How can I do this?
If you have no idea of the maximum size of the label field you can use Ada.Strings.Unbounded.
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Text_IO.Unbounded_IO; use Ada.Text_IO.Unbounded_IO;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Main is
type Ent is record
Value : Integer;
Label : Unbounded_String;
end record;
type ent_array is array (1 .. 4) of Ent;
Foo : ent_array;
begin
for I of Foo loop
Put ("Enter a value: ");
Get (I.Value);
Skip_Line;
Put ("Enter a label: ");
I.Label := Get_Line;
New_Line;
end loop;
Put_Line ("Array Foo contents:");
for I of Foo loop
Put (I.Value'Image & " ");
Put_Line (I.Label);
end loop;
end Main;
[entry is a reserved word.]
If you want an array, all the entries have to be the same size. The size of your second record is Label_Length (4) + Value (4) + Label (Character (1) * Label_Length) i.e. anything between 8 and just over 2**31 bytes.
The trick is to fix the maximum size and give a default value:
subtype Ent_Label_Length is Natural range 0 .. 32;
type Ent (Label_Length : Ent_Label_Length := Ent_Label_Length'Last) is
record
Value : Integer;
Label : String (1 .. Label_Length);
end record;
You can save yourself the trouble of writing this (and working out the length of each string) by using Ada.Strings.Bounded (ARM A.4.4).
If you don't mind slightly different syntax, you can also consider using the Ada.Containers.Indefinite_Vectors package in place of arrays. Then each element can be a different size. And vectors can be used in for loops just like arrays can:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Vectors; use Ada.Containers;
procedure Main is
type Entry_Info(Label_Length : Natural) is
record
Value: Integer;
Label: String(1..Label_Length);
end record;
package Vectors is new Indefinite_Vectors
(Index_Type => Positive,
Element_Type => Entry_Info);
use type Vectors.Vector; -- so you can use the & operator
Entries : Vectors.Vector := Vectors.Empty_Vector
& (Label_Length => 0, Value => 1, Label => "")
& (Label_Length => 1, Value => 2, Label => "A");
begin
for Info of Entries loop
Put_Line(Info.Value'Image & " => " & Info.Label);
end loop;
end Main;
Yet another, but perhaps cruder, method is to put the strings on the heap and use access values:
type String_Ref is access String;
type Entry_T is record
Value: Integer;
Label: String_Ref;
end record;
To allocate the strings, use "new" with an initial value:
Entries : constant array(1..2) of Entry_T := (
(Value => 1, Label => new String'("First entry")),
(Value => 2, Label => new String'("Second entry"))
);
To get the value of a Label, deference with ".all":
for E of Entries loop
Ada.Text_IO.Put_Line (
"Value" & E.Value'Image
& ", label " & E.Label.all);
end loop;
If we're posting odd solutions, you can also use a holder:
package String_Holders is new Ada.Containers.Indefinite_Holders
(Element_Type => String);
type Entry_Is_Reserved is record
Value : Integer;
Label : String_Holders.Holder;
end record;

Backup procedure using Datapump API

Hi I try to create Oracle procedure which take backup from user schema (POSData) and place it in directory called "Backups", POSData grant read, write on directory "Backups" using this code from system user.
DECLARE
h2 NUMBER;
BEGIN
h2 := DBMS_DATAPUMP.OPEN('EXPORT', 'SCHEMA');
DBMS_DATAPUMP.ADD_FILE(h2,'POSData.dmp','Backups');
DBMS_DATAPUMP.METADATA_FILTER(h2,'SCHEMA_EXP','IN (''POSData'')');
DBMS_DATAPUMP.START_JOB(h2);
dbms_datapump.detach(h2);
END;
but I always get this error
Error starting at line : 1 in command -
DECLARE
h2 NUMBER;
BEGIN
h2 := DBMS_DATAPUMP.OPEN('EXPORT', 'SCHEMA');
DBMS_DATAPUMP.ADD_FILE(h2,'POSData.dmp','Backups');
DBMS_DATAPUMP.METADATA_FILTER(h2,'SCHEMA_EXP','IN (''POSData'')');
DBMS_DATAPUMP.START_JOB(h2);
dbms_datapump.detach(h2);
END;
Error report -
ORA-39001: invalid argument value
ORA-06512: at "SYS.DBMS_SYS_ERROR", line 79
ORA-06512: at "SYS.DBMS_DATAPUMP", line 3507
ORA-06512: at "SYS.DBMS_DATAPUMP", line 3756
ORA-06512: at line 5
39001. 00000 - "invalid argument value"
*Cause: The user specified API parameters were of the wrong type or
value range. Subsequent messages supplied by
DBMS_DATAPUMP.GET_STATUS will further describe the error.
*Action: Correct the bad argument and retry the API.
help please?
DBMS_DATAPUMP has some really good error messages, but you have to dig in to get them. In the example below, I added a nested procedure output_expdp_error to dump the detailed message. You should of course modify this to do appropriate logging for your environment:
DECLARE
h1 NUMBER;
l_filename VARCHAR2( 100 ) := 'deleteme.dmp';
l_directory VARCHAR2( 100 ) := 'CIFS_DIR';
PROCEDURE output_expdpd_error( p_handle IN NUMBER ) AS
-- *******************************************************************
-- Output EXPDP Error
-- Purpose:
-- Send detailed EXPDP error to DBMS_OUTPUT
-- Modified:
-- 2020.03.02 - BFL Created
-- Notes:
-- Borrowed from: https://docs.oracle.com/database/121/SUTIL/GUID-5AAC848B-5A2B-4FD1-97ED-D3A048263118.htm#SUTIL977
-- *******************************************************************
l_status ku$_status; --ku$_jobstatus;
l_logentry ku$_logentry;
l_job_state VARCHAR2( 30 );
l_ind NUMBER;
l_pos NUMBER;
l_length NUMBER;
l_linesize CONSTANT NUMBER := 1000;
l_message VARCHAR2( 1000 );
BEGIN
-- Original had "if sqlcode = dbms_datapump.success_with_info_num" but this
-- hides some errors. Just always process the handle.
DBMS_OUTPUT.put_line( 'Data Pump job started with info available:' );
DBMS_DATAPUMP.get_status( p_handle
, DBMS_DATAPUMP.ku$_status_job_error
, 0
, l_job_state
, l_status );
IF (BITAND( l_status.mask, DBMS_DATAPUMP.ku$_status_job_error ) != 0)
THEN
l_logentry := l_status.error;
IF l_logentry IS NOT NULL
THEN
l_ind := l_logentry.FIRST;
WHILE l_ind IS NOT NULL
LOOP
l_pos := 1;
l_length := LENGTH( l_logentry( l_ind ).logtext );
l_length := CASE WHEN l_linesize < l_length THEN l_linesize ELSE l_length END;
WHILE l_length > 0
LOOP
l_message :=
SUBSTR( l_logentry( l_ind ).logtext
, l_pos
, l_length );
DBMS_OUTPUT.put_line( l_message );
l_pos := l_pos + l_linesize;
l_length := LENGTH( l_logentry( l_ind ).logtext ) + 1 - l_pos;
END LOOP;
l_ind := l_logentry.NEXT( l_ind );
END LOOP;
END IF;
END IF;
END;
BEGIN
DBMS_OUTPUT.put_line( 'open' );
h1 := DBMS_DATAPUMP.open( 'EXPORT', 'SCHEMA' );
DBMS_OUTPUT.put_line( 'add_file' );
DBMS_DATAPUMP.add_file( handle => h1
, filename => l_filename
, directory => l_directory
, filetype => DBMS_DATAPUMP.ku$_file_type_dump_file
, reusefile => 1 );
DBMS_OUTPUT.put_line( 'hr filter' );
DBMS_DATAPUMP.metadata_filter( h1
, 'SCHEMA_EXPR'
, 'IN (''BOGUS'')' );
DBMS_OUTPUT.put_line( 'BOGUS filter' );
DBMS_DATAPUMP.metadata_filter( h1
, 'SCHEMA_EXPR'
, 'IN (''BOGUS'')' );
DBMS_OUTPUT.put_line( 'start_job' );
DBMS_DATAPUMP.start_job( h1 );
DBMS_DATAPUMP.detach( h1 );
EXCEPTION
WHEN OTHERS
THEN
DECLARE
l_message VARCHAR2( 1000 );
BEGIN
output_expdpd_error( p_handle => h1 );
DBMS_DATAPUMP.detach( h1 );
l_message :=
SUBSTR(
SQLERRM
|| UTL_TCP.crlf
|| DBMS_UTILITY.format_error_backtrace
|| UTL_TCP.crlf
|| UTL_TCP.crlf
, 1
, 1000 );
DBMS_OUTPUT.put_line( l_message );
raise_application_error( -20000, l_message );
END;
END;
When I ran this with the non-existent schema, I got the wonderfully specific error message:
ORA-39001: invalid argument value
ORA-39170: Schema expression IN ('BOGUS') does not correspond to any schemas.
ORA-39001: invalid argument value
ORA-06512: at "SYS.DBMS_SYS_ERROR", line 79
ORA-06512: at "SYS.DBMS_DATAPUMP", line 3507
ORA-06512: at "SYS.DBMS_DATAPUMP", line 4825
ORA-06512: at line 76
I found the solution for my problem and here the code
DECLARE
h1 number;
errorvarchar varchar2(100):= 'ERROR';
tryGetStatus number := 0;
begin
h1 := dbms_datapump.open (operation => 'EXPORT', job_mode => 'SCHEMA', job_name => 'EXPORT_JOB_SQLDEV_2344', version => 'COMPATIBLE');
tryGetStatus := 1;
dbms_datapump.set_parallel(handle => h1, degree => 1);
dbms_datapump.add_file(handle => h1, filename => 'EXPDAT.LOG', directory => 'BACKUPS', filetype => 3);
dbms_datapump.set_parameter(handle => h1, name => 'KEEP_MASTER', value => 0);
dbms_datapump.metadata_filter(handle => h1, name => 'SCHEMA_EXPR', value => 'IN(''POSDATA'')');
dbms_datapump.add_file(handle => h1, filename => 'POSData.DMP', directory => 'BACKUPS', filesize => '100M', filetype => 1, reusefile => 1);
dbms_datapump.set_parameter(handle => h1, name => 'INCLUDE_METADATA', value => 1);
dbms_datapump.set_parameter(handle => h1, name => 'DATA_ACCESS_METHOD', value => 'AUTOMATIC');
dbms_datapump.set_parameter(handle => h1, name => 'ESTIMATE', value => 'BLOCKS');
dbms_datapump.start_job(handle => h1, skip_current => 0, abort_step => 0);
dbms_datapump.detach(handle => h1);
errorvarchar := 'NO_ERROR';
EXCEPTION
WHEN OTHERS THEN
BEGIN
IF ((errorvarchar = 'ERROR')AND(tryGetStatus=1)) THEN
DBMS_DATAPUMP.DETACH(h1);
END IF;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
RAISE;
END;

Oracle - check if queue table exists before flushing it

How do you check if the queue table exist before flushing it.
I have used the below syntax to create queue table
CREATE type Message_typ as object (
subject VARCHAR2(30),
text VARCHAR2(80));
BEGIN
dbms_aqadm.CREATE_QUEUE_TABLE (
queue_table => 'XX'
,queue_payload_type => 'Message_typ'
);
END;
And the below code to purge the flush table:
declare
l_type dbms_aqadm.aq$_purge_options_t;
begin
l_type.block := true;
l_type.delivery_mode := dbms_aq.buffered;
dbms_aqadm.purge_queue_table(queue_table => 'XX',
purge_condition => '',
purge_options => l_type);
l_type.block := true;
l_type.delivery_mode := dbms_aq.persistent;
dbms_aqadm.purge_queue_table(queue_table => 'XX',
purge_condition => '',
purge_options => l_type);
end;
Please guide how to check if the queue table exists and if exists do a purge.
Thanks.

Passing parameters to a PL/SQL function in Oracle apps and calling it on a procedure

I currently have a working function for encryption of banking data which has to be done before inserting data on the staging table.
But I don't know how to call the function. Below is the function
FUNCTION FN_ENCRYPT_3DES(P_SUPPLIER_BANK_ACCOUNT_NUMBER INVOICES.SUPPLIER_BANK_ACCOUNT_NUMBER%TYPE
P_CARD_NUMBER ) RETURN RAW IS
-- --------------------------------------------------
v_data VARCHAR2(255);
v_retval RAW(255);
BEGIN
IF P_SUPPLIER_BANK_ACCOUNT_NUMBER IS NOT NULL THEN
--p_key := utl_raw.cast_to_raw('GD6GTT56HKY4HGF6FH3JG9J5F62FT1');
v_data := RPAD(P_SUPPLIER_BANK_ACCOUNT_NUMBER, CEIL(LENGTH(P_SUPPLIER_BANK_ACCOUNT_NUMBER) / 8) * 8, CHR(0));
dbms_obfuscation_toolkit.DES3Encrypt(input => utl_raw.cast_to_raw(v_data),
key => G_KEY,
which => 1,
encrypted_data => v_retval);
RETURN v_retval;
ELSE
RETURN NULL;
END IF;
END;

PL/SQL procedure output stored in file and then email in Oracle 11g

I have a procedure where I am checking whether there are new codes. If there are then insert them into a table. And also save the new data into a csv or txt file and email them to me.
I can't get the logic for how to redirect the new data to file or just even put the data as simple text in the email. Thanks
create or replace
PROCEDURE new_codes_test( today_date IN VARCHAR2 DEFAULT NULL, v_proc_return OUT NUMBER) AS
sql_str VARCHAR2(4000);
.....
BEGIN
v_start_time := SYSDATE;
v_proc_return := 0;
....
INSERT INTO NEW_CODES_test
SELECT DISTINCT Sy_ID ,P_CODE, SYSDATE
FROM X.B
WHERE NOT EXISTS (SELECT DISTINCT Sy_ID, P_CODE
FROM X.C
WHERE today = today_date) ;
COMMIT;
--SELECT ___ into ___ from X.B;
sql_str := 'UTL_MAIL.send(sender => ''
,recipients => ''
,cc => ''
,subject => 'New codes'
,MESSAGE => '' )';
--EXECUTE IMMEDIATE sql_str;
p_proc_return := v_proc_return;
EXCEPTIONS
....
END;
To write to a file the UTL_FILE package will come in handy. To write an email you'll need to put the text to be sent into some sort of string before passing it to the MESSAGE argument of UTL_MAIL.SEND. You'll also need to be sure UTL_MAIL is installed and set up on your server; see this FAQ.
So something like the following may be useful:
CREATE OR REPLACE FUNCTION NEW_CODES_TEST(today_date IN VARCHAR2)
RETURN NUMBER
AS
strMessage VARCHAR2(32767);
nRows NUMBER := 0;
fHandle UTL_FILE.FILE_TYPE;
BEGIN
v_start_time := SYSDATE;
fHandle := UTL_FILE.FOPEN(someDirectory, someFilename, 'w');
FOR aRow IN (SELECT DISTINCT SY_ID ,P_CODE
FROM X.B
WHERE NOT EXISTS (SELECT DISTINCT Sy_ID, P_CODE
FROM X.C
WHERE today = today_date)
LOOP
INSERT INTO NEW_CODES_test
VALUES (aRow.SY_ID, aRow.P_CODE, SYSDATE);
UTL_FILE.PUT_LINE(fHandle, aRow.SY_ID || ', ' || aRow.P_CODE);
strMessage := strMessage || 'Added ' || aRow.SY_ID || ', ' ||
aRow.P_CODE || CHR(10);
nRows := nRows + 1;
END LOOP;
COMMIT;
UTL_FILE.FCLOSE(fHandle);
UTL_MAIL.SEND(sender => 'me#mycompany.com',
recipients => 'you#someplaceelse.net',
subject => 'New codes',
message => strMessage);
RETURN 0;
EXCEPTIONS
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
RETURN SQLCODE;
END NEW_CODES_TEST;
Share and enjoy.

Resources