Passing in a JSON_OBJECT_T into SODA_DOCUMENT_T - oracle

I have a PL/SQL handler using the SODA package to manipulate a JSON database. I want to:
Read the value for key id in the payload
Write the payload JSON into a new document in the database.
To do step 1, The handler takes :body to be parsed as a JSON_OBJECT_T type, so that I can access the value for key id in the payload.
But for step 2, if I read body again when constructing with SODA_DOCUMENT_T(b_content=> :body), it will simply give me a blank document.
I also can't pass in the JSON_OBJECT_T variable in directly, like SODA_DOCUMENT_T(j_content=> jso), because that function expects a JSON type and not a JSON_OBJECT_T. I can't find the JSON type documentation, but saw in code examples the function JSON('{}') to generate one.
Reading :body_text however gives me other problems - because JSON() function cannot handle line breaks in the payload and gives an error instead.
Currently to work around this I'm using the following:
SODA_DOCUMENT_T(
j_content => JSON(jso.to_string())
)
Which seems very silly because I'm serialising it to a string again before converting it back into JSON type. Is there a proper way to read the value of a key of the payload, and pass it into the SODA_DOCUMENT_T?

Yes, you can't pass in JSON_Object_T instance to SODA_Document_T constructor. However, you may use JSON_QUERY() PL/SQL function that drills into the JSON document given a path expression and it returns a JSON type instance.
Example:
jval := JSON_QUERY(body, '$.id' RETURNING JSON);
In the above example, $.id is the path expression and json_query() returns json value corresponding to the field id starting from the root $
Documentation:
https://docs.oracle.com/en/database/oracle/oracle-database/21/adjsn/function-JSON_QUERY.html
You should now be able to pass this jval instance of type JSON to SODA_Document_T constructor.
Yes, I think you should definitely avoid back and forth conversions.
Usage:
SQL>
CREATE OR REPLACE FUNCTION process_request(body IN VARCHAR2)
RETURN JSON
IS
j JSON;
d SODA_Document_T;
BEGIN
j := JSON_QUERY(body, '$.id' RETURNING JSON);
d := SODA_Document_T(j_Content => j);
-- n := coll.insert_one(d);
RETURN j;
END;
13 /
Function created.
SQL> SELECT process_request('{"id":{"type": "string", "val": "DEADBEEF"}}') FROM dual;
PROCESS_REQUEST('{"ID":{"TYPE":"STRING","VAL":"DEADBEEF"}}')
--------------------------------------------------------------------------------
{"type":"string","val":"DEADBEEF"}
I recommend this approach. Let me know if that helps!
Alternate solution:
BTW, given a DOM, you can directly go to JSON type instance as well, using the following method in JSON_Object_T type:
MEMBER FUNCTION GET_JSON RETURNS JSON
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
KEY VARCHAR2 IN
This method pretty much does the same work as JSON_Query() shown above:
SQL>
CREATE OR REPLACE FUNCTION process_body(data IN VARCHAR2)
RETURN JSON
AS
body JSON_Object_T;
j JSON;
BEGIN
body := JSON_Object_T.parse(data);
j := body.get_Json('id');
return j;
END;
11 /
SQL> SELECT process_body('{"id":{"type": "string", "val": "DEADBEEF"}}') FROM dual;
PROCESS_BODY('{"ID":{"TYPE":"STRING","VAL":"DEADBEEF"}}')
--------------------------------------------------------------------------------
{"type":"string","val":"DEADBEEF"}

Related

Invalid or NULL argument for object_id []

I'm trying to call a function and store the result in a varabile
la_object_names fls_varchar_table := fls_varchar_table();
la_children_EASs FLS_NUMBER_TABLE := FLS_NUMBER_TABLE();
loa_objects_EASs ims_api.typo_ims_objects := ims_api.typo_ims_objects(NULL);
la_object_names := rm.sri_object_utils.get_object_names(pia_object_ids => la_children_EASs);
fls_varchar_table and FLS_NUMBER_TABLE are table of varchar and number declared in a ddl.
get_object_names function returns a fls_varchar_table
The intialization of la_children_EASs
la_children_EASs.extend(loa_objects_EASs.ims_objects.LAST);
I think there is an implicit conversion from fls_number_table to ims_api.typo_ims_objects but not viceversa
I'm getting this error:
Invalid or NULL argument for object_id [] in procedure/function
rm.sri_object_utils.get_object_name
I tried to give and hard coded argument to get_object_names method something like this:
la_object_names := rm.sri_object_utils.get_object_names(pia_object_ids => FLS_NUMBER_TABLE(31231,2313213,231231));
This returns the expected results. Any suggestion is very appreciated.
Seems that you pass parameter of wrong type to this function in first case.
In first case you pass la_children_EASs which is FLS_NUMBER_TABLE(),
in second case you pass fls_varchar_table.

Passing Field Symbol value to Return parameter of method

I have the below code which uses a method. When I try to assign the Field Symbol value [Type ANY] to the return parameter RO_TAB [Type Ref to Data], I am getting an error message OBJECTS_MOVE_NOT SUPPORTED [Conversion of type "l" to type "g" not supported.].
The issue is happening after a BW system upgrade along with which we also moved to ABAP objects. The code executes perfectly in the older version of ABAP.
The dump occurs in the below line:
RO_TAB = <lf_storage>.
I have no idea why.
method GET_LU_STORAGE_FOR_ODS.
* IMPORTS
* IF_ODS TYPE RSODSTECH
* IF_ODS_TABLE_TYPE TYPE ZODS_TAB_TYPE
* RETURNS
* RO_TAB TYPE REF TO DATA
FIELD-SYMBOLS:
<lf_storage> TYPE ANY.
DATA:
lf_index TYPE SY-TABIX,
lf_sindex TYPE STRING,
lf_name TYPE STRING.
lf_index = GET_LU_STORAGE_INDEX(
IF_ODS = IF_ODS
IF_ODS_TABLE_TYPE = IF_ODS_TABLE_TYPE ).
lf_sindex = lf_index.
CONCATENATE
'MO_LU_DATA_'
lf_sindex
INTO lf_name.
ASSIGN lf_name TO <lf_storage>.
RO_TAB = <lf_storage>.
endmethod.
You need to create a data object first, using the CREATE DATA statement. Then you can ASSIGN a field symbol to work with the dynamically created data object. There's an example in the online manual. A field symbol is not a reference, it simply places the variable assigned to it in its position. You're effectively trying to move a string (which is what lf_name is) to a reference variable, and that won't work.
You cannot assign a variable of type STRING to a variable of type REF TO DATA.
The following code snippet shows how it should be done.
DATA: lf_name TYPE string.
DATA: lo_tab TYPE REF TO DATA.
FIELD-SYMBOLS: <lf_name> TYPE string.
lf_name = 'test'.
GET REFERENCE OF lf_name INTO lo_tab.
*lf_name = lo_tab. "this is not allowed
ASSIGN lo_tab->* TO <lf_name>.
So in your case it would be sufficient to define a field symbol.
FIELD-SYMBOLS: <lf_name> TYPE STRING.
then assign the contents referenced by RO_TAB to this field symbol.
ASSIGN ro_tab->* TO <lf_name>.
and finally do the concatenation.
CONCATENATE
'MO_LU_DATA_'
lf_index
INTO <lf_name>.
That's all! No further assignments should be required.
How about just this?
lf_sindex = lf_index.
CONCATENATE
'MO_LU_DATA_'
lf_sindex
INTO RO_TAB.

How to pass in argument of type DATE to a function

I've got a function in an oracle database. I need to call it from delphi. I use the following code:
procedure TForm1.Run;
var
q:TADOQuery;
begin
q:=TADOQuery.Create(nil);
q.Connection:=ADOConnection1;
q.ParamCheck:=false;
q.SQL.Add('BEGIN');
q.SQL.Add(' :RES:=Search(:P_DATE);');
q.SQL.Add('END;');
q.Parameters.AddParameter.Name:='P_DATE';
q.Parameters.ParamByName('P_DATE').Direction:=pdInput;
q.Parameters.ParamByName('P_DATE').DataType:=ftDate;
q.Parameters.ParamByName('P_DATE').Value:=Now;
q.Parameters.AddParameter.Name:='RES';
q.Parameters.ParamByName('RES').DataType:=ftFloat;
q.Parameters.ParamByName('RES').Direction:=pdOutput;
q.Parameters.ParamByName('RES').Value:=1;
q.ExecSQL;
//...
I get ora-06550 error, saying invalid number or type of parameters. If I change the P_DATE parameter to sysdate, i.e. :RES:=Search(sysdate);, it works fine.
So how can I pass an "in" parameter of type DATE to an oracle function from delphi?
Found ORA-06550 when Oracle stored function is called.. However this relates to Kylix Pascal IDE. Am I expected to meet the same behaviour for delphi? Didn't try to use oracle procedure instead of a function. Maybe this issue can be solved some how else...
Oracle doesn't have a date type for bind variables - you need to explicitly convert the bind variable to a date:
:res := search(to_date(:p_date, 'dd/mm/yyyy'));
You should then be able to pass your variable as a string matching the date format you've specified.
Try to send this parameter as a string:
.........
q.SQL.Add('BEGIN');
q.SQL.Add(' :RES:=Search(TO_DATE(:P_DATE,''YYYYMMDD''));');
q.SQL.Add('END;');
q.Parameters.AddParameter.Name:='P_DATE';
q.Parameters.ParamByName('P_DATE').Direction:=pdInput;
q.Parameters.ParamByName('P_DATE').DataType:=ftString;
q.Parameters.ParamByName('P_DATE').Value:=FormatDateTime('yyyymmdd',Now);
..........
You're doing it wrong (and I think you had a previous question deleted as being a duplicate that asked this same question (different function, but same idea) just a couple of days ago).
You're calling the function wrong in the first place.
.........
q.SQL.Add('BEGIN');
q.SQL.Add(' SELECT Search(TO_DATE(:P_DATE,''YYYYMMDD'')) FROM System.Dual;');
q.SQL.Add('END;');
q.Parameters.AddParameter.Name:='P_DATE';
q.Parameters.ParamByName('P_DATE').Direction:=pdInput;
q.Parameters.ParamByName('P_DATE').DataType:=ftDate;
q.Parameters.ParamByName('P_DATE').Value:=Now;
q.Open;
if not q.IsEmpty then // or not q.Eof
Res := q.Fields[0].AsFloat;

load data from database on date selection

I am trying to load database data by using dbplannercalender1.
procedure TForm1.DBPlannerCalendar1DaySelect(Sender: TObject;
SelDate: TDateTime);
begin
with absQuery2 do
begin
absQuery2.Close;
absQuery2.sql.Clear;
ABSQuery2.SQL.Text:='select * from log where date = :a1';
ABSQUERY2.PARAMS.ParamByName('a1').value:= DBPlannerCalendar1.Date;
ABSQuery2.ExecSQL;
end;
end;
I get the error "date string expected"YYYY-MM-DD",but "="found at line 1..."
What am I doing wrong ?
Use trunced value
adoQuery.Parameters.paramByName('DataDal').value := trunc(edDate.Date);
Use AsDateTime instead of Value, to let the driver convert it to the proper format:
AbsQuery2.Params.ParamByName('a1').AsDateTime := DBPlannerCalendar1.Date;
If Absolute doens't support AsDateTime for some reason, format the date yourself:
AbsQuery2.Params.ParamByName('a1').Value :=
FormatDateTime('yyyy-mm-dd`, DBPlannerCalendar1.Date);
The first method is the best choice, because it works more portably between database engines (it works for all of them, because the driverr does the formatting into the proper arrangement for dates). Using the second means that if the DB expects something different than YYYY-MM-DD, you have to change the code everywhere that uses dates.

Getting an Error “ORA-06502: PL/SQL: numeric or value error: character string buffer too small”

This is my query
begin
select ceq_specimens.numero as NUMERO,
analyseEffectuee.DESCRIPTION as analyseEffectuee
into out_rec.NUMERO_SPECIMEN3, out_rec.SPEC3_ANALYSE_EFFECTUE
from CEQ_FORMULAIRES_ANALYSES
inner join ceq_liste_choix analyseEffectuee on analyseEffectuee.ID_LISTE_CHOIX=CEQ_FORMULAIRES_ANALYSES.ID_ANALYSE_EFFECTUE
inner join ceq_specimens on ceq_specimens.ID_SPECIMEN=CEQ_FORMULAIRES_ANALYSES.ID_SPECIMEN and ceq_specimens.ID_SPECIMEN=vintIdSpecimen3
where CEQ_FORMULAIRES_ANALYSES.ID_FORMULAIRE=out_rec.ID_FORMULAIRE;
EXCEPTION
WHEN NO_DATA_FOUND THEN
out_rec.NUMERO_SPECIMEN3 := ' ';
out_rec.SPEC3_ANALYSE_EFFECTUE := ' ';
END;
...
IF analyseEffectuee.DESCRIPTION as analyseEffectuee = Spécimen impossible à analyser: Préciser en commentaire(s)
I get error ''string buffer too small”
IF analyseEffectuee.DESCRIPTION as analyseEffectuee= Non
No problem in this case
Thanks for helping me!
I get Error ''string buffer too small” "
What that means is your variable out_rec.SPEC3_ANALYSE_EFFECTUE is not big enough to hold teh value Spécimen impossible à analyser: Préciser en commentaire(s).
The best way to define PL/SQL variables is to use the %TYPE keyword. This creates a variable which matches the column's definition.
Your code uses something called OUTREC. You haven't given us the definition of this, which makes it harder to correct your specfic problem but perhaps you should be doing something like this. Declare a PL/SQL record type which matches you're desired output, then declare a variable of that type:
type my_rec_t is record (
NUMERO_SPECIMEN1 ceq_specimens.numer%type,
SPEC1_ANALYSE_EFFECTUE analyseEffectuee.DESCRIPTION%type,
NUMERO_SPECIMEN2 ceq_specimens.numer%type,
SPEC2_ANALYSE_EFFECTUE analyseEffectuee.DESCRIPTION%type,
NUMERO_SPECIMEN3 ceq_specimens.numer%type,
SPEC3_ANALYSE_EFFECTUE analyseEffectuee.DESCRIPTION%type
);
out_rec my_rec_t;
ORA-02303: cannot drop or replace a type with type or table dependents
So this means you're using SQL Types, with inheritance. Sorting this out is a bit of a problem. There is some syntax which we can use with the ALTER TYPE command which allows us to handle dependent types. Find out more.

Resources