How to put empty string in JSON_OBJECT_T object? - json

I am trying to add an empty string to a JSON_OBJECT_T using the following code but I am getting null in value and not empty string.
DECLARE
V_OBJ JSON_OBJECT_T;
BEGIN
V_OBJ := JSON_OBJECT_T();
V_OBJ.PUT('customerAccRef','');
DBMS_OUTPUT.PUT_LINE(V_OBJ.stringify);
END;
When I do this, I am getting the following json
{"customerAccRef":null}
and I want output as below
{"customerAccRef":""}
Can someone suggest what change I need to do to pass an empty string?

The reason it is happening is due to the fact that Oracle internally changes empty string to NULL values. It is due to some legacy reason and you may read this answer to know the history.
I couldn't find anywhere in the JSON documentation with an option to bypass this particular problem myself, although I'd be glad if someone could find it.
As a workaround to your problem, you could use TRIM function to convert a single space to blank string.
V_OBJ.PUT('customerAccRef' , TRIM(' '));
which gives
{"customerAccRef":""}
This seems to work both in Oracle 12.2 version; I tested in my local machine and in Oracle
18c : DEMO, as well as in 19c (LiveSQL online)
A point to also note here is that a simple select TRIM(' ') from dual always returns NULL, which is surprising and luckily for you, it works as expected with JSONs

In 19c, using put(..., '') you get an empty string.
Using put_null, or put(..., to_char(null)), put(..., <unitializedString>) you get the "real" null value.
DECLARE
V_OBJ JSON_OBJECT_T;
V_STRING VARCHAR2(20);
BEGIN
V_OBJ := JSON_OBJECT_T();
V_OBJ.PUT('doubleSingleQuotes','');
V_OBJ.PUT('toCharNull',to_char(null));
V_OBJ.PUT('uninitializedStringVariable',v_string);
V_OBJ.PUT_null('null');
V_OBJ.PUT('trimSpace', trim(' '));
DBMS_OUTPUT.PUT_LINE(V_OBJ.stringify);
END;
... gives :
{"doubleSingleQuotes":"","toCharNull":null,"uninitializedStringVariable":null,"null":null,"trimSpace":""}

Related

How to fetch data from API in Oracle APEX without web source module

I'm just new with APEX, PL/SQL and API/JSON so please bear with me.
I need to create a search page where the data will be coming from the API.
I tried to do it with web source but unfortunately I'm having an error, checked already with the dba team, etc. the error still there, thinking its about the version issue or something, so i remove this idea, though this will really help me a lot.
So the workaround is that the PL/SQL will connect to the API.
So it goes like this:
In APEX, I will input some data on the textbox and when I click the search button it will fetch the data from API to the interactive report.
**UPDATED
This is what I have and I believe there's a conversion of JSON thing that I also need to do.
declare
v_url varchar2(1000);
v_wallet_path varchar2(120) :='<walletvalue>';
v_body clob := '{<json body>}';
l_response clob;
begin
apex_web_service.g_request_headers.delete;
apex_web_service.g_request_headers(1).name := 'Ocp-Apim-Subscription-Key';
apex_web_service.g_request_headers(1).value := '<key value>';
v_url := '<url>';
l_response := apex_web_service.make_rest_request(
p_url => v_url,
p_http_method => 'POST',
p_wallet_path => v_wallet_path,
p_wallet_pwd =>'<password>',
p_body => v_body);
if apex_web_service.g_status_code = 200 then --OK
--dbms_output.put_line(l_response);
else --ERROR?
dbms_output.put_line('ERROR');
End If;
End;
Can someone please help me, I've been thinking about this for weeks. I don’t know where to start. What are the things I need to have, to know and the steps on how to create the page.
I know this is a lot but I will really appreciate your help! Thanks in advance also!
This is a very broad question, so my answer is also pretty vague.
I don't think you want to create a function - before the Web Source module was introduced, this kind of thing was often done in an on-submit page process. In your process you'd need to:
Call the web API, pass in your search term, and get back a response. The old way to do this was with UTL_HTTP, but the newer APEX_WEB_SERVICE package made it much easier.
Using APEX_COLLECTION, create/truncate a collection and save the response clob into the collection's p_clob001 field.
Edit: here's a code snippet for that
l_clob := apex_web_service.make_rest_request(....);
APEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTION(p_collection_name => 'API_RESPONSE');
APEX_COLLECTION.ADD_MEMBER(
p_collection_name => 'API_RESPONSE'
p_clob001 => l_clob);
Then create an interactive report. The source will be a SQL query which will take the collection's clob, parse it as JSON, and convert into a tabular format (rows and columns) using JSON_TABLE.
Edit: add example
SELECT jt.id, jt.name
FROM APEX_collections c
cross join JSON_TABLE(
clob001, -- the following lines depend on your JSON structure
'$[*]',
columns(
id number path '$.id',
name varchar2(10) path '$.name')
) jt
WHERE collection_name = 'API_RESPONSE'
Alternately, you could parse the clob using JSON_TABLE as part of your page process, save the output table into a collection using APEX_COLLECTION.CREATE_COLLECTION_FROM_QUERY, and then just query that collection for your interactive report.
Edit: I'm not sure if this would work, but something like:
APEX_COLLECTION.CREATE_COLLECTION_FROM_QUERY (
p_collection_name => 'API_RESPONSE',
p_query => 'SELECT t.id, t.name
FROM JSON_TABLE(
l_clob,
''$[*]'',
columns(
id number path ''$.id'',
name varchar2(10) path ''$.name'')
) t');
Side note: as a very different option, you could also call the web service using JavaScript/jQuery/AJAX. I think this would be more complicated, but it's technically possible.

Unknown column in field list

I'm trying to insert some information to MySQL with Pascal, but when I run the program I get the error
unknown column 'mohsen' in field list
This is my code
procedure TForm1.Button1Click(Sender: TObject);
var
aSQLText: string;
aSQLCommand: string;
namee:string;
family:string;
begin
namee:='mohsen';
family:='dolatshah';
aSQLText:= 'INSERT INTO b_tbl(Name,Family) VALUES (%s,%s)';
aSQLCommand := Format(aSQLText, [namee, family]);
SQLConnector1.ExecuteDirect(aSQLCommand);
SQLTransaction1.Commit;
end;
How can I solve this problem?
It's because your
VALUES (%s,%s)
isn't surrounding the namee and family variable contents by quotes. Therefore, your back-end Sql engine thinks your mohsen is a column name, not a value.
Instead, use, e.g.
VALUES (''%s'',''%s'')
as in
Namee := 'mohsen';
Family := 'dolatshah';
aSQLText:= 'INSERT INTO b_tbl(Name,Family) VALUES (''%s'',''%s'')';
aSQLCommand := Format(aSQLText,[namee,family]);
In the original version of my answer, I explained how to fix your problem by "doubling up" single quotes in the Sql you were trying to build, because it seemed to me that you were having difficulty seeing (literally) what was wrong with what you were doing.
An alternative (and better) way to avoid your problem (and the one I always use in real life) is to use the QuotedStr() function. The same code would then become
aSQLText := 'INSERT INTO b_tbl (Name, Family) VALUES (%s, %s)';
aSQLCommand := Format(aSQLText, [QuotedStr(namee), QuotedStr(family)]);
According to the Online Help:
Use QuotedStr to convert the string S to a quoted string. A single quote character (') >is inserted at the beginning and end of S, and each single quote character in the string is >repeated.
What it means by "repeated" is what I've referred to as "doubling up". Why that's important, and the main reason I use QuotedStr is to avoid the Sql db-engine throwing an error when the value you want to send contains a single quote character as in O'Reilly.
Try adding a row containing that name to your table using MySql Workbench and you'll see what I mean.
So, not only does using QuotedStr make constructing SQL statements as strings in Delphi code less error-prone, but it also avoid problems at the back-end, too.
Just in case this will help anybody else I had the same error when I was parsing a python variable with a sql statement and it had an if statement in i.e.
sql="select bob,steve, if(steve>50,'y','n') from table;"
try as I might it coming up with this "unknown column y" - so I tried everything and then I was about to get rid of it and give it up as a bad job until I thought I would swap the " for ' and ' for "..... Hoooraaahh it works!
This is the statement that worked
sql='select bob,steve, if(steve>50,"y","n") from table;'
Hope it helps...
To avoid this sort of problem and SQL injection you should really look into using SQL parameters for this, not the Pascal format statement.

Delphi XE7 MultiDevice SQL Error TIMESTAMP Ret. Field

I've an SQLTIMESTAMP binded (SQL output of query from datamodule)
(On live bindings) -> BindVIsually TTMFSMXGRID.
When I open the query, the field is correctly filled in the grid (EX: 06/05/2016, etc)
But when I try to take the Cell[x,y] as StringToSqltimestamp it come back in a different format (WRONG FORMAT). And I am trying to use field for a query so it make me an error (EOF) because don't find anything in that date.
dm1.UpdMsg.ParamByName('data').AsSQLTimeStamp:=StrToSQLTimeStamp(GrRec.Cells[1,GrRec.FocusedCell.Row]);
Any idea about how to solve it?
Like other date/time formatting functions, StrToSqlTimeStamp() by default uses formatting criteria based on the current system locale. If that does not match what you need (for example, maybe the day and month are swapped), you can use the overloaded version that accepts a TFormatSettings as input so you can customize it.
var
Fmt: TFormatSettings;
begin
// Get default settings first...
Fmt := TFormatSettings.Create;
// customize Fmt as needed...
Fmt.ShortDateFormat := 'dd/mm/yyyy';
Fmt.DateSeparator := '/';
// not convert...
dm1.UpdMsg.ParamByName('data').AsSQLTimeStamp := StrToSQLTimeStamp(GrRec.Cells[1,GrRec.FocusedCell.Row], Fmt);
end;

HTML Entity decoding to Special characters

I want to display special symbols in my output.
For eg: My text may contain entity codes like <, > etc.
I want to display this as <, > in my output. I need to do this in SQL.
I googled about this and got a function,
select dbms_xmlgen.convert('ABC <; ',0) from dual
This does the reverse process, it generates the output as 'ABC <'
I tried with decoding but it does not work. I even changed the sql command as,
select dbms_xmlgen.convert('ABC <; ',1) from dual, where 1 is for entity_decode, but I don't get the desired output.
Instead of using DBMS_XMLGEN.convert, I used the function UTL_I18N.UNESCAPE_REFERENCE:
SELECT UTL_I18N.UNESCAPE_REFERENCE('ABC < ') FROM DUAL;
result:
ABC <
More information on the Oracle doc: http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/u_i18n.htm#i998992
Try something like:
SELECT DBMS_XMLGEN.CONVERT('ABC < ', DBMS_XMLGEN.ENTITY_DECODE) FROM DUAL
Also, see the Oracle docs for that.
EDIT:
Ok, so apparently this is a bug in some Oracle versions (9.2.0.1 and 10.1.0.2, as it seems). Somebody solved it by wrapping the function. I don't know how that's supposed to solve it, but it my be worth trying. Create a function like this:
CREATE OR REPLACE FUNCTION
xml_decode(
i_xml_string IN VARCHAR2
)
RETURN VARCHAR2
IS
BEGIN
RETURN
DBMS_XMLGEN.convert(
i_xml_string,
DBMS_XMLGEN.ENTITY_DECODE
);
END;
And use it instead:
SELECT xml_decode('ABC < ') FROM DUAL;
Let us know if that works.

Sanitisation of input for a stored MySQL database procedure

I am using a database someone else produced (and I am not really authorised to change it). However, as I was looking into the stored procedures within the database I noticed the following procedure:
DELIMITER $$
CREATE PROCEDURE `logIn`(userName varChar(50), userPass varChar(50))
BEGIN
declare userID int;
SELECT
u.userID INTO userID
FROM
users u
WHERE
u.userName=userName
AND u.userPassword=MD5(userPass);
IF (IFNULL(uID,-1) > 0) THEN
select 1 as outMsg;
ELSE
select 0 as outMsg;
END IF;
END$$
with the corresponding table users having three columns: userID INT, userName VARCHAR(50) and userPassword VARCHAR(50).
As I am not very good at this, could someone let me know whether the input for such a function needs to be sanitised as to not allow any SQL injections and if not - why? A general rule of thumb would be very much appreciated.
P.S. This function will be called from a JS script on a form submit.
There are a few rules of thumb here that depend on the underlying datatype and how it's inserted into the database.
First, Parameterized queries are always best for SQL Injection protection.. but.. if you can't change that..
String type:
Remove any single quotes
OR
Replace any single quotes with the single quote twice.
Replace any of the following characters with their encoded alternative;
>
<
"
;
(chr 34)
)
(
For example.. ) is replaced with & #x29;
-(the space in the above example is so you'll see the code, remove it to get ")")
For a datatype other then string, check that the datatype is sane and remove any character that shouldn't be in the datatype. If it's an integer, make sure the string that you're passing in is an integer. This can commonly be done by casting to the type in code. The cast will either work.. or cause an error. It's also good to check that the datatype min and maxes have not been exceeded. For example.. If I was checking for an integer, I might use code similar to this:
var myInt = parseInt(param);
Then I might check it's bounds to be sure it's less then the maximum integer value and greater then the minimum integer value.
That should be good enough to prevent a SQL Injection attack...
And.. since you have not posted the code that actually interfaces with the database... As an added precaution.. you may also want to remove --,`,%,",", "".
You only want 'sane' values getting to the database call.. so an integer like, $309 wouldn't make sense, you'd want to remove the $.. . probably by using a regex replace for any non numeric characters a comma and a period.
[^[0-9,.]]
Be extra cautious.
Yes, the input must be sanitized before trying to run the procedure.
You might want to share the actual calling point for the procedure to get more help here, since there is no way that the procedure is called directly from JS on form submit. You probably have a Servlet, PHP page or some HTTP friendly intermediary to make the database call somehow.