Decode base64 image into BLOB with PL/SQL - json

I'm using the script below in order to fetch JSON file from MongoDB, parse it and then insert it into Oracle table.
The script works fine in a sense that it inserts all values correctly into Oracle table. That includes the value Photo which is an image of base64 formate and it is much larger than 32KB.
The column Photo in the table Appery_Photos is of the type CLOB while column DecodedPhoto is of the type BLOB.
The problem lies in the line blobOriginal := base64decode1(Photo); which I used to decode the CLOB into BLOB. The function base64decode1 has been replaced with several functions (i.e. decode_base64 , base64DecodeClobAsBlob_plsql, base64decode , from_base64 & finally JSON_EXT.DECODE).
The result was the same for all of them. That is, the resultant BLOB object cannot be openned as an image in any of images editors (I'm using Oracle SQL Developer to download it).
I checked CLOB, and I could not find any newlines \n, nor could I find any spaces (only + signs found). Furthermore, I inserted CLOB value into the base64-image-converter and it displays the image correctly. In addition, I tried to encode the resultant BLOB in base64 back in order to further validate (using the opposite functions provided in the links above), the resultant base64 is not the same at all.
BEGIN
l_http_request := UTL_HTTP.begin_request('https://api.appery.io/rest/1/db/collections/Photos?where=%7B%22Oracle_Flag%22%3A%22Y%22%7D' , 'GET' , 'HTTP/1.1');
-- ...set header's attributes
UTL_HTTP.set_header(l_http_request, 'X-Appery-Database-Id', '53f2dac5e4b02cca64021dbe');
l_http_response := UTL_HTTP.get_response(l_http_request);
BEGIN
LOOP
UTL_HTTP.read_text(l_http_response, buf);
l_response_text := l_response_text || buf;
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
NULL;
END;
l_list := json_list(l_response_text);
FOR i IN 1..l_list.count
LOOP
A_id := json_ext.get_string(json(l_list.get(i)),'_id');
l_val := json_ext.get_json_value(json(l_list.get(i)),'Photo');
dbms_lob.createtemporary(Photo, true, 2);
json_value.get_string(l_val, Photo);
dbms_output.put_line(dbms_lob.getlength(Photo));
dbms_output.put_line(dbms_lob.substr(Photo, 20, 1));
blobOriginal := base64decode1(Photo);
A_Name := json_ext.get_string(json(l_list.get(i)),'Name');
Remarks := json_ext.get_string(json(l_list.get(i)),'Remarks');
Status := json_ext.get_string(json(l_list.get(i)),'Status');
UserId := json_ext.get_string(json(l_list.get(i)),'UserId');
A_Date := json_ext.get_string(json(l_list.get(i)),'Date');
A_Time := json_ext.get_string(json(l_list.get(i)),'Time');
MSG_status := json_ext.get_string(json(l_list.get(i)),'MSG_status');
Oracle_Flag := json_ext.get_string(json(l_list.get(i)),'Oracle_Flag');
acl := json_ext.get_string(json(l_list.get(i)),'acl');
INSERT
INTO Appery_Photos
(
A_id,
Photo,
DecodedPhoto,
A_Name,
Remarks,
Status,
UserId,
A_Date,
A_Time,
MSG_status ,
Oracle_Flag,
acl
)
VALUES
(
A_id,
Photo,
blobOriginal,
A_Name,
Remarks,
Status,
UserId,
A_Date,
A_Time,
MSG_status ,
Oracle_Flag,
acl
);
dbms_lob.freetemporary(Photo);
END LOOP;
-- finalizing
UTL_HTTP.end_response(l_http_response);
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
UTL_HTTP.end_response(l_http_response);
END;
Any help is deeply appreciated.

I found that is not in the function I used in the base64 decoding. Instead, the value I have is not base64 encoded strings, but base64 encode dataURi's, something like
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAA
So I have to use something like: clobbase642blob( substr( Photo, instr( Photo, ',' ) + 1 ) )
The following script is inspired by Oracle Community answer
DECLARE
l_param_list VARCHAR2(512);
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
l_response_text CLOB;
--l_response_text VARCHAR2(32767);
buf VARCHAR2(32767);
l_list json_list;
l_val json_value;
A_id VARCHAR2(100);
Photo CLOB;
A_Name VARCHAR2(100);
Remarks VARCHAR2(100);
Status VARCHAR2(100);
UserId VARCHAR2(100);
A_Date VARCHAR2(100);
A_Time VARCHAR2(100);
MSG_status VARCHAR2(100);
Oracle_Flag VARCHAR2(100);
acl VARCHAR2(100);
obj json_list;
blobOriginal BLOB := empty_blob();
clobInBase64 CLOB;
substring VARCHAR2(2000);
tmp BLOB;
n pls_integer := 0;
substring_length pls_integer := 2000;
------------------------------------------------------
FUNCTION clobbase642blob(
p_clob CLOB )
RETURN BLOB
IS
t_blob BLOB;
t_buffer VARCHAR2(32767);
t_pos NUMBER := 1;
t_size NUMBER := nls_charset_decl_len( 32764, nls_charset_id( 'char_cs' ) );
t_len NUMBER;
t_tmp raw(32767);
BEGIN
dbms_lob.createtemporary( t_blob, true );
t_len := LENGTH( p_clob );
LOOP
EXIT
WHEN t_pos > t_len;
t_buffer := REPLACE( REPLACE( SUBSTR( p_clob, t_pos, t_size ), chr(10) ), chr(13) );
t_pos := t_pos + t_size;
WHILE t_pos 0
LOOP
t_buffer := t_buffer || REPLACE( REPLACE( SUBSTR( p_clob, t_pos, 1 ), chr(10) ), chr(13) );
t_pos := t_pos + 1;
END LOOP;
t_tmp := utl_encode.base64_decode( utl_raw.cast_to_raw( t_buffer ) );
dbms_lob.writeappend( t_blob, utl_raw.length( t_tmp ), t_tmp );
END LOOP;
RETURN t_blob;
END;
------------------------------------------------------
BEGIN
-- service's input parameters
-- preparing Request...
l_http_request := UTL_HTTP.begin_request('https://api.appery.io/rest/1/db/collections/Photos?where=%7B%22Oracle_Flag%22%3A%22Y%22%7D' , 'GET' , 'HTTP/1.1');
-- ...set header's attributes
UTL_HTTP.set_header(l_http_request, 'X-Appery-Database-Id', '53f2dac5e4b02cca64021dbe');
l_http_response := UTL_HTTP.get_response(l_http_request);
BEGIN
LOOP
UTL_HTTP.read_text(l_http_response, buf);
l_response_text := l_response_text || buf;
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
NULL;
END;
l_list := json_list(l_response_text);
FOR i IN 1..l_list.count
LOOP
A_id := json_ext.get_string(json(l_list.get(i)),'_id');
--deal with base64 URI photo >32KB
l_val := json_ext.get_json_value(json(l_list.get(i)),'Photo');
dbms_lob.createtemporary(Photo, true, 2);
json_value.get_string(l_val, Photo);
--dbms_output.put_line(dbms_lob.getlength(Photo));
--dbms_output.put_line(dbms_lob.substr(Photo, 20, 1));
blobOriginal := clobbase642blob( SUBSTR( Photo, 24 ) );
A_Name := json_ext.get_string(json(l_list.get(i)),'Name');
Remarks := json_ext.get_string(json(l_list.get(i)),'Remarks');
Status := json_ext.get_string(json(l_list.get(i)),'Status');
UserId := json_ext.get_string(json(l_list.get(i)),'UserId');
A_Date := json_ext.get_string(json(l_list.get(i)),'Date');
A_Time := json_ext.get_string(json(l_list.get(i)),'Time');
MSG_status := json_ext.get_string(json(l_list.get(i)),'MSG_status');
Oracle_Flag := json_ext.get_string(json(l_list.get(i)),'Oracle_Flag');
acl := json_ext.get_string(json(l_list.get(i)),'acl');
INSERT
INTO Appery_Photos
(
A_id,
Photo,
DecodedPhoto,
A_Name,
Remarks,
Status,
UserId,
A_Date,
A_Time,
MSG_status ,
Oracle_Flag,
acl
)
VALUES
(
A_id,
Photo,
blobOriginal,
A_Name,
Remarks,
Status,
UserId,
A_Date,
A_Time,
MSG_status ,
Oracle_Flag,
acl
);
dbms_lob.freetemporary(Photo);
END LOOP;
-- finalizing
UTL_HTTP.end_response(l_http_response);
EXCEPTION
WHEN UTL_HTTP.end_of_body THEN
UTL_HTTP.end_response(l_http_response);
END;
/

It is Base64 or HexBinary...
This works for HexBinary
function DESERIALIZE_HEX_BLOB(P_SERIALIZATION CLOB)
return BLOB
is
V_BLOB BLOB;
V_OFFSET INTEGER := 1;
V_AMOUNT INTEGER := 32000;
V_INPUT_LENGTH NUMBER := DBMS_LOB.GETLENGTH(P_SERIALIZATION);
V_HEXBINARY_DATA VARCHAR2(32000);
begin
if (P_SERIALIZATION is NULL) then return NULL; end if;
DBMS_LOB.CREATETEMPORARY(V_BLOB,TRUE,DBMS_LOB.CALL);
while (V_OFFSET <= V_INPUT_LENGTH) loop
V_AMOUNT := 32000;
DBMS_LOB.READ(P_SERIALIZATION,V_AMOUNT,V_OFFSET,V_HEXBINARY_DATA);
V_OFFSET := V_OFFSET + V_AMOUNT;
DBMS_LOB.APPEND(V_BLOB,TO_BLOB(HEXTORAW(V_HEXBINARY_DATA)));
end loop;
return V_BLOB;
end;
--
And could probably be modified to handle Base64 without too much trouble.

Related

Oracle utl_file throwing error when the input buffer size exceeds more than 1000 character

I'm tyring to write files for every cursor execution as shown in below sql. The data_payload column will have more than 1000 characters.
There is an exception at utl_file.put line.
If I use SUBRTR of 1000 characters, then the file writes successfully.
Is there any other alternate function to handle this?
SET SERVEROUTPUT ON
DECLARE
l_file_name VARCHAR2(100);
l_chr_payload VARCHAR2(4000);
fhandle utl_file.file_type;
CURSOR payload_cur IS
SELECT data_payload,
request_reference
FROM temp_tbl
WHERE message_type = 'ORDERCREATE'
AND ROWNUM < 2;
TYPE payload_typ IS
TABLE OF payload_cur%rowtype INDEX BY BINARY_INTEGER;
l_col_payload payload_typ;
BEGIN
OPEN payload_cur;
l_col_payload.DELETE;
FETCH payload_cur
BULK COLLECT INTO l_col_payload;
CLOSE payload_cur;
FOR i IN 1..l_col_payload.count LOOP
l_file_name := l_col_payload(i).request_reference
|| '_'
|| i
|| '.json';
dbms_output.put_line('l_file_name' || l_file_name);
fhandle := utl_file.fopen(
'TMP_DIR' -- File location
,
l_file_name -- File name
,
'w' -- Open mode: w = write.
);
l_chr_payload := substr(
l_col_payload(i).data_payload,
1,
1000
);
utl_file.put(
fhandle,
l_chr_payload
);
utl_file.fclose(fhandle);
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
dbms_output.put_line('ERROR: '
|| sqlcode
|| ' - '
|| sqlerrm);
RAISE;
END;
/
Assuming that temp_tbl.data_payload is a CLOB data type then you want to loop through the payload reading substring chunks (without needing to store the substrings in a temporary variable).
If you want to write more than 1024 characters at one time then you need to specify the 4th argument to UTL_FILE.FOPEN(location, filename, openmode, max_linesize) as the default is 1024.
Something like this (untested as I don't have your tables or a directory to write to):
DECLARE
l_index PLS_INTEGER := 0;
CURSOR payload_cur IS
SELECT data_payload,
request_reference
FROM temp_tbl
WHERE message_type = 'ORDERCREATE'
AND ROWNUM < 2;
TYPE payload_typ IS TABLE OF payload_cur%rowtype;
l_col_payload payload_typ;
BEGIN
OPEN payload_cur;
LOOP
-- In general, you should limit the number of rows you process at one time.
-- In your case this is not necessary due to the `ROWNUM < 2` filter.
FETCH payload_cur BULK COLLECT INTO l_col_payload LIMIT 10;
EXIT WHEN payload_cur%NOTFOUND;
FOR i IN 1..l_col_payload.count LOOP
l_index := l_index + 1;
DECLARE
l_file_name VARCHAR2(100);
c_amt CONSTANT PLS_INTEGER := 32000;
l_length PLS_INTEGER := COALESCE(dbms_lob.getlength(l_col_payload(i).data_payload),0);
l_offset PLS_INTEGER := 1;
l_fhandle utl_file.file_type;
BEGIN
l_file_name := l_col_payload(i).request_reference || '_' || l_index || '.json';
dbms_output.put_line('l_file_name' || l_file_name);
l_fhandle := utl_file.fopen('TMP_DIR', l_file_name, 'w', 32760);
WHILE ( l_offset <= l_length ) LOOP
utl_file.put(
l_fhandle,
dbms_lob.substr(l_col_payload(i).data_payload,c_amt,l_offset)
);
utl_file.fflush(l_fhandle);
l_offset := l_offset + c_amt;
END LOOP;
utl_file.fclose(l_fhandle);
END;
END LOOP;
END LOOP;
CLOSE payload_cur;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERROR: ' || sqlcode || ' - ' || sqlerrm);
RAISE;
END;
/

PL SQL Function is not raising User defined exception

I have defined a user-defined exception but my function is not raising the exception .
****Spec*****
FUNCTION get_property_id (
p_owner IN app.bc_entities.entity_id%TYPE,
p_object_type_id IN app.bc_custom_attribute_masters.object_type_id%TYPE,
p_custom_attribute_master_id IN
app.bc_custom_attribute_masters.custom_attribute_master_id%TYPE,
p_non_rev_controlled_p IN NUMBER DEFAULT 0
)
RETURN NUMBER;
****BODY*****
FUNCTION get_custom_attribute_master_id (
p_owner IN app.bc_entities.entity_id%TYPE,
p_object_type_id IN app.bc_custom_attribute_masters.object_type_id%TYPE,
p_property_id IN NUMBER,
p_non_rev_controlled_p IN NUMBER DEFAULT 0
)
RETURN app.bc_custom_attribute_masters.custom_attribute_master_id%TYPE
IS
l_custom_attribute_master_id
APP.BC_CUSTOM_ATTRIBUTE_MASTERS.CUSTOM_ATTRIBUTE_MASTER_ID%TYPE;
l_object_property_id NUMBER := 0;
v_sql varchar2(32767);
r_error bc_errors%rowtype;
TYPE templatecurtype IS REF CURSOR;
c_result templatecurtype;
l_column_name VARCHAR2(50);
invalid_property_id EXCEPTION ;
BEGIN
r_error.code_unit := 'Get_Custom_Attribute_Master_Id';
r_error.error_context := 'Calling Get_Custom_Attribute_Master_Id ';
r_error.error_data := ' Get_Custom_Attribute_Master_Id :Property Id :'||to_char(p_property_id)||', OBJECT_ID:'||to_char(p_object_type_id);
FOR V_ROW IN
(SELECT * FROM app.CUSTOM_PROPERTY_TABLES_DEFS WHERE object_type_id =
p_object_type_id AND
(
(p_object_type_id = 20 AND REV_CONTROLLED_P = p_non_rev_controlled_p) OR (p_object_type_id <> 20 AND NVL(REV_CONTROLLED_P,0) = NVL(p_non_rev_controlled_p,0) ) )
)
LOOP
IF v_row.FIRST_PROPERTY_COLUMN_NAME IS NOT NULL THEN
l_column_name := v_row.FIRST_PROPERTY_COLUMN_NAME;
ELSE
l_column_name := v_row.PROPERTY_COLUMN_NAME;
END IF;
v_sql := ' SELECT civ.'||l_column_name || ' FROM '||v_row.table_name ||' civ WHERE '||
v_row.property_column_name ||' = :property_id ';
dbms_output.put_line ( '1-v_sql '||v_sql);
r_error.error_data := v_sql;
OPEN c_result FOR v_sql
USING p_property_id ;
FETCH c_result INTO l_object_property_id ;
CLOSE c_result ;
IF l_object_property_id IS NULL THEN
RAISE invalid_property_id;
END IF;
v_sql :='Select civ.custom_attribute_master_id FROM ' || v_row.table_name||' civ WHERE civ.' || l_column_name || '= :property_id and civ.custom_attribute_master_id is not null
and rownum = 1';
dbms_output.put_line ( '2-v_sql '||v_sql);
OPEN c_result FOR v_sql USING l_object_property_id ;
FETCH c_result INTO l_custom_attribute_master_id;
CLOSE c_result ;
RETURN l_custom_attribute_master_id ;
END LOOP;
EXCEPTION
WHEN invalid_property_id THEN
r_error.error_stack := dbms_utility.format_call_stack()||chr(10)||dbms_utility.format_error_backtrace();
r_error.message := ' p_property_id ( '||p_property_id||' ) is invalid ';
r_error.error_code := SQLCODE;
r_error.notified_p := null;
PKG_ERROR.LOG(p_code_unit=>r_error.code_unit
,p_message=>r_error.message
,p_application=>V_APPLICATION
,p_database_name=>V_DB_NAME
,p_error_owner=>p_owner
,p_error_user=>p_owner
,p_error_module=>V_MODULE
,p_error_object=>V_APP_OBJECT
,p_error_type=>app.pkg_error.error
,p_error_code=>r_error.error_code
,p_error_stack=>r_error.error_stack
,p_error_context=>r_error.error_context
,p_error_data=>r_error.error_data
,p_notified_p=>r_error.notified_p);
RAISE_APPLICATION_ERROR ( -20098, r_error.message ) ;
WHEN OTHERS THEN
r_error.error_stack := dbms_utility.format_call_stack()||chr(10)||dbms_utility.format_error_backtrace();
r_error.message := SQLERRM;
r_error.error_code := SQLCODE;
r_error.notified_p := null;
PKG_ERROR.LOG(p_code_unit=>r_error.code_unit
,p_message=>r_error.message
,p_application=>V_APPLICATION
,p_database_name=>V_DB_NAME
,p_error_owner=>p_owner
,p_error_user=>p_owner
,p_error_module=>V_MODULE
,p_error_object=>V_APP_OBJECT
,p_error_type=>app.pkg_error.error
,p_error_code=>r_error.error_code
,p_error_stack=>r_error.error_stack
,p_error_context=>r_error.error_context
,p_error_data=>r_error.error_data
,p_notified_p=>r_error.notified_p);
RAISE;
END get_custom_attribute_master_id;

Scrape data from Apex which created with PL/SQL dynamic content?

I have used PL/SQL dynamic content for creating a report. I need to add functionality to scrape (take data) from this HTML. Moreover, I have SQL which returns data.
How can I do this with APEX region PL/SQL dynamic content?
PL/SQL code for creating HTML tags with data:
declare
l_sql varchar2(2000) := test.function(92500, 2017, 2, 114); --return which select when we dynamical execute this select it will return data
l_columns varchar2(2000) := test.test_function_dynamic_columns_name(92500, 2017, 2, 114);
l_vc_arr2 APEX_APPLICATION_GLOBAL.VC_ARR2;
v_tableclob clob := '<table summary="" class="a-IRR-table" id="2212903112260009" role="presentation">';
v_rowclob clob;
v_rowclob_1 clob;
begin
l_vc_arr2 := APEX_UTIL.STRING_TO_TABLE(l_columns);
v_tableclob := v_tableclob||'<tr>';
v_tableclob := v_tableclob ||'<th class="a-IRR-header">';
v_tableclob := v_tableclob ||REPLACE(REPLACE(l_columns,'"',' '),':','</th><th class="a-IRR-header">');
v_tableclob := v_tableclob ||'</th></tr>';
FOR z IN 1..l_vc_arr2.count LOOP
if l_vc_arr2(z) = '"SUM"' then
v_rowclob := v_rowclob ||'<td class="summation">''||t.'||l_vc_arr2(z)||'||''</td>';
v_rowclob_1 := v_rowclob_1 || '<td id="total_sum"></td>';
else
v_rowclob := v_rowclob ||'<td>''||t.'||l_vc_arr2(z)||'||''</td>';
v_rowclob_1 := v_rowclob_1 || '<td></td>';
end if;
end loop;
execute immediate
'
declare
begin
htp.p('''||v_tableclob||''');
for t in ('||l_sql||') loop
htp.p(''<tr>'||v_rowclob||'</tr>'');
end loop;
htp.p(''<tr>'');
htp.p('''|| v_rowclob_1 ||''');
htp.p(''</tr>'');
htp.p(''</table>'');
end;';
end;

Extracting very long string from JSON to CLOB

I'm trying to extract a very long string into clob from json_object_t and got some weird database behaviour (12.2c) with json_object_t.get_clob(key) method.
There is a sample code than does following:
DECLARE
l_data CLOB := '{"text": "very long string about 1M chars"}';
l_json json_object_t;
l_text CLOB := EMPTY_CLOB();
BEGIN
l_json := json_object_t.parse(l_data);
l_text := l_json.get_clob('text');
dbms_output.put_line('got ' || dbms_lob.getlength(l_text) || ' chars');
END;
When string length in a 'text' key is less than 32k chars, get_clob method works just fine and shows appropriate result, but with longer strings it produces an empty clob with zero length, just like get_string, but without 'character string buffer too small' exception.
I've tried to get same data via json_table query, but it cannot extract data to clob column at all, only varchar/number is allowed.
Is that a bug or am I doing something wrong? Is there any other ways to extract long strings from JSON keys?
I work with Oracle Database JSON Store group and would be happy to assist you with this issue you're facing. Could you try the alternate get_Clob procedure instead of this function and tell us what the behavior is?
Signature:
MEMBER PROCEDURE get_Clob(key VARCHAR2, c IN OUT CLOB)
Please try this:
DECLARE
content_json CLOB := '{"value":"';
content_json_end CLOB := '"}';
content_tmp CLOB := 'ab';
l_json json_object_t;
l_text CLOB := EMPTY_CLOB();
tmp clob;
BEGIN
-- 13 gives 16K
-- 14 gives 32K
FOR count IN 1 .. 14
loop
dbms_lob.append(content_tmp, content_tmp); -- a bad append for now
END loop;
dbms_lob.append(content_json, content_tmp);
dbms_lob.append(content_json, content_json_end);
l_json := json_object_t.parse(content_json);
l_json.get_clob('value', l_text); -- !!! TRY THIS PROC get_Clob
--l_text := l_json.get_clob('value');
dbms_output.put_line('Lob size in Kb: ');
dbms_output.put_line(dbms_lob.getLength(l_text) / 1024);
END;
/
Looking forward to your findings..
This works as well. Instead using the the get_clob method, use c:
DECLARE
CURSOR crsrJSON IS
SELECT
json_object( 'employee_id' VALUE employee_id,
'first_name' VALUE first_name,
'last_name' VALUE last_name,
'email' VALUE email,
'phone_number' VALUE phone_number,
'hire_date' VALUE to_char(hire_date,'MM/DD/YYYY'),
'job_id' VALUE job_id,
'salary' VALUE nvl(salary,0),
'commission_pct' VALUE nvl(commission_pct,0),
'manager_id' VALUE NVL(manager_id,0),
'department_id' VALUE NVL(department_id,0),
'department_name' VALUE (select department_name from departments x where x.department_id = hr.department_id),
'job_title' VALUE (select job_title from jobs x where x.job_id = hr.job_id)) emp_data
FROM
employees hr;
js_array JSON_ARRAY_T := new JSON_ARRAY_T;
json_obj JSON_OBJECT_T := JSON_OBJECT_T();
json_clob CLOB := EMPTY_CLOB();
BEGIN
FOR data_rec IN crsrJSON LOOP
js_array.append(JSON_ELEMENT_T.parse(data_rec.emp_data));
END LOOP;
json_obj.put('data',js_array);
IF json_obj.has('data') THEN
json_clob := json_obj.to_clob;
DBMS_OUTPUT.PUT_LINE(json_clob);
ELSE
DBMS_OUTPUT.PUT_LINE('Nope');
END IF;
END;
with data as
( select
xmlelement(e,regexp_replace('{"name":"'||colname||'"}', '[[:cntrl:]]', ''),',') col1
from tblname
)
select
rtrim(replace(replace(replace(xmlagg(col1).getclobval(),'&'||'quot;','"'),'<E>',''),'</E>',''),',')
as very_long_json
from data;

delphi dxExpress MySQL: invalid LAST_INSERT_ID value

I am developing an application in Delphi and want to insert records to Mysql's table.
An then I want to know inserted record's identity value. So I write bellow code.
On run time , insert is done and record added but returned value for identity is zero!!
what is my mistake ?!!
-- MySql table create
CREATE TABLE Sample_Table (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
mobile_number varchar(20) DEFAULT NULL,
message_body text,
PRIMARY KEY (id)
);
--- Delphi code
SQLCon := TSQLConnection.Create(self);
with SQLCon do begin
Close;
DriverName := 'MySQL';
GetDriverFunc := 'getSQLDriverMYSQL';
LibraryName := 'dbxmys.dll';
VendorLib := 'LIBMYSQL.dll';
LoginPrompt := false;
Params.Values['HostName'] := '127.0.0.1';
Params.Values['Database'] := 'sms_test';
Params.Values['User_Name'] := 'root';
Params.Values['Password'] := 'root';
Open;
end;
SQLQry := TSQLQuery.Create(self);
with SQLQry do begin
Close;
SQLConnection := SQLCon;
SQL.Clear;
SQL.Add('INSERT INTO Sample_Table ');
SQL.Add('(mobile_number, message_body) VALUES');
SQL.Add(format('(%s, %s);',[QuotedStr('989121011689'), QuotedStr('Text1')]));
ExecSQL();
Close;
SQL.Clear;
SQL.Add('SELECT LAST_INSERT_ID() EngineRefNo;');
Open;
First;
ListBox1.items.Add(FieldByName('EngineRefNo').AsString);
Close;
end;
SQLCon.Close;
any advise?
You should get the ID in one go
SQLQry := TSQLQuery.Create(self);
with SQLQry do begin
SQLConnection := SQLCon;
SQL.Add('INSERT INTO Sample_Table ');
SQL.Add('(mobile_number, message_body) VALUES');
SQL.Add(format('(%s, %s);',[QuotedStr('989121011689'), QuotedStr('Text1')]));
SQL.Add('SELECT LAST_INSERT_ID() EngineRefNo;');
Open;
ListBox1.items.Add(FieldByName('EngineRefNo').AsString);
Close;
end;
SQLCon.Close;
and you should think about using parameters to prevent sql injection
SQLQry := TSQLQuery.Create(self);
with SQLQry do begin
SQLConnection := SQLCon;
SQL.Add('INSERT INTO Sample_Table ');
SQL.Add('( mobile_number, message_body ) VALUES');
SQL.Add('( :mobile_number, :message_body );');
SQL.Add('SELECT LAST_INSERT_ID() EngineRefNo;');
ParamByName( 'mobile_number' ).Value := '989121011689';
ParamByName( 'message_body' ).Value := 'Text1';
Open;
ListBox1.items.Add(FieldByName('EngineRefNo').AsString);
Close;
end;
SQLCon.Close;
Apparently this is a problem known with delphi .
quoting the above link:
Certain ODBC applications (including Delphi and Access) may have
trouble obtaining the auto-increment value using the previous
examples. In this case, try the following statement as an alternative:
SELECT * FROM tbl WHERE auto IS NULL;
This alternative method requires that sql_auto_is_null variable is not
set to 0. See Server System Variables
So in your case (untested):
SQLCon := TSQLConnection.Create(self);
with SQLCon do begin
Close;
DriverName := 'MySQL';
GetDriverFunc := 'getSQLDriverMYSQL';
LibraryName := 'dbxmys.dll';
VendorLib := 'LIBMYSQL.dll';
LoginPrompt := false;
Params.Values['HostName'] := '127.0.0.1';
Params.Values['Database'] := 'sms_test';
Params.Values['User_Name'] := 'root';
Params.Values['Password'] := 'root';
Open;
end;
SQLQry := TSQLQuery.Create(self);
with SQLQry do begin
Close;
SQLConnection := SQLCon;
SQL.Clear;
SQL.Add('INSERT INTO Sample_Table ');
SQL.Add('(mobile_number, message_body) VALUES');
SQL.Add(format('(%s, %s);',[QuotedStr('989121011689'), QuotedStr('Text1')]));
ExecSQL();
Close;
SQL.Clear;
SQL.Add('SELECT id FROM Sample_Table WHERE id IS NULL');
Open;
First;
ListBox1.items.Add(FieldByName('Id').AsString);
Close;
end;
SQLCon.Close;