DECLARE
l_string NVARCHAR2(600) := '123456';
checksum NVARCHAR2(600);
BEGIN
DBMS_OUTPUT.DISABLE;
DBMS_OUTPUT.ENABLE(1000000);
DBMS_OBFUSCATION_TOOLKIT.md5 (input_string => l_string, checksum_string => checksum);
DBMS_OUTPUT.PUT_LINE(RAWTONHEX(utl_raw.cast_to_raw(checksum)));
END;
Expected value: e10adc3949ba59abbe56e057f20f883e
But it returns: FFFD00390049FFFD0059FFFDFFFD0056FFFD000FFFFD003E
Note I want to maintain nvarchar2 datatype. The value from checksum variable gets stored in column that is of type nvarchar2.
I am aware that md5 accepts and returns data in varchar2. But if someone can help me figure this out using nvarchar2 data type, that would be great.
The NLS_CHARACTERSET = AL32UTF8
The following should work via dbms_crypto using hash()
declare
l_src nvarchar2(100) := '123456';
l_raw_hash raw(100);
begin
l_raw_hash := dbms_crypto.hash(to_clob(l_src), dbms_crypto.HASH_MD5);
dbms_output.put_line(l_raw_hash);
end;
Result: E10ADC3949BA59ABBE56E057F20F883E
l_raw_hash will be in raw format. You can use UTL_RAW to convert it to another datat type. Just make sure your display is showing the proper character set or thing will look interesting.
Related
I want to leave some fields empty (i.e. Null) when I insert values into table. I don't see why would I want to have a DB full of empty strings in fields.
I use Delphi 10, FireDAC and local SQLite DB.
Edit: Provided code is just an example. In my application values are provided by user input and functions, any many of them are optional. If value is empty, I would like to keep it at Null or default value. Creating multiple variants of ExecSQL and nesting If statements isn't an option too - there are too many optional fields (18, to be exact).
Test table:
CREATE TABLE "Clients" (
"Name" TEXT,
"Notes" TEXT
);
This is how I tried it:
var someName,someNote: string;
begin
{...}
someName:='Vasya';
someNote:='';
FDConnection1.ExecSQL('INSERT OR REPLACE INTO Clients(Name,Notes) VALUES (:nameval,:notesval)',
[someName, IfThen(someNote.isEmpty, Null, somenote)]);
This raises an exception:
could not convert variant of type (Null) into type (OleStr)
I've tried to overload it and specify [ftString,ftString] and it didn't help.
Currently I have to do it like this and I hate this messy code:
FDConnection1.ExecSQL('INSERT OR REPLACE INTO Clients(Name,Notes) VALUES ('+
IfThen(someName.isEmpty,'NULL','"'+Sanitize(someName)+'"')+','+
IfThen(someNote.isEmpty,'NULL','"'+Sanitize(someNote)+'"')+');');
Any recommendations?
Edit2: Currently I see an option of creating new row with "INSERT OR REPLACE" and then use multiple UPDATEs in a row for each non-empty value. But this looks direly ineffective. Like this:
FDConnection1.ExecSQL('INSERT OR REPLACE INTO Clients(Name) VALUES (:nameval)',[SomeName]);
id := FDConnection1.ExecSQLScalar('SELECT FROM Clients VALUES id WHERE Name=:nameval',[SomeName]);
if not SomeString.isEmpty then
FDConnection1.ExecSQL('UPDATE Clients SET Notes=:noteval WHERE id=:idval)',[SomeNote,id]);
According to Embarcadero documentation ( here ):
To set the parameter value to Null, specify the parameter data type,
then call the Clear method:
with FDQuery1.ParamByName('name') do begin
DataType := ftString;
Clear;
end;
FDQuery1.ExecSQL;
So, you have to use FDQuery to insert Null values, I suppose. Something like this:
//Assign FDConnection1 to FDQuery1's Connection property
FDQuery1.SQL.Text := 'INSERT OR REPLACE INTO Clients(Name,Notes) VALUES (:nameval,:notesval)';
with FDQuery1.ParamByName('nameval') do
begin
DataType := ftString;
Value := someName;
end;
with FDQuery1.ParamByName('notesval') do
begin
DataType := ftString;
if someNote.IsEmpty then
Clear;
else
Value := someNote;
end;
if not FDConnection1.Connected then
FDConnection.Open;
FDQuery1.ExecSql;
It's not very good idea to execute query as String without parameters because this code is vulnerable to SQL injections.
Some sources tells that it's not enough and you should do something like this:
with FDQuery1.ParamByName('name') do begin
DataType := ftString;
AsString := '';
Clear;
end;
FDQuery1.ExecSQL;
but I can't confirm it. You can try it if main example won't work.
I have a table that is filled with some value, for setting the value I use a stored procedure that also calculate a hash function and save in database.
In case of updating value hash should be recalculated. For recalculating hash I use the following procedure:
DELIMITER $$
CREATE PROCEDURE `sp_UpdateHash`(IN rkey int)
Begin
DECLARE AuthCode VarChar(10);
SET #input = concat('SELECT r_ac into #AuthCode
FROM table_rec
where r_key=',rkey);
PREPARE squery FROM #input;
EXECUTE squery;
SET #hashed = SHA2(#AuthCode,256);
select #hashed;
DEALLOCATE PREPARE squery;
end;
and procedure just for calculating hash:
CREATE PROCEDURE `sp_GetHash`(IN AuthCode VarChar(10))
BEGIN
DECLARE hashed VarChar(64);
SET hashed = SHA2(AuthCode,256);
select hashed as 'Hash';
END
AuthCode identical, but hash is different when I try to process value after select command I get a wrong code. If I compare two hashes with other results, for example from an online generator, the result is similar to the second function: sp_GetHash
Do you have any idea why?
The problem was in one field that has a different coding from the table, and when I use it in the query it has a different size.
I just learned that the Sqlite Manager plug-in for Firefox will go away in November, so I've been trying to recreate its functionality in Delphi: open Sqlite databases, enter SQL queries. I'm running Tokyo.
My problem comes with Sqlite fields defined as 'date.' While Sqlite allows specification of data types, it allows pretty much anything to be put in any field. FireDAC handles bad entries in integer or float fields ('bar' becomes 0, '32foo' becomes 32), but it hiccups on fields described as 'date.'
For example, I have a table:
CREATE TABLE "someTable" ("id" INTEGER, "s" text(10), "d" date)
With this data:
INSERT INTO "someTable" VALUES ("1","good date","2017-09-09");
INSERT INTO "someTable" VALUES ("2","bad date","2017-09-0b");
INSERT INTO "someTable" VALUES ("3","empty d","");
INSERT INTO "someTable" VALUES ("4","null date",null);
Opening FDQuery q1 with SQL = "select * from someTable", a bad date (such as the second record) raises an EConvertError ("Invalid argument to date encode"). I tried to get around it by adding a maprule (q1 is a FDQuery):
with q1.FormatOptions do begin
OwnMapRules := True;
with MapRules.Add do begin
SourceDataType := dtDate;
TargetDataType := dtAnsiString;
sizemin := 10;
sizemax := 256;
PrecMin := -1;
PrecMax := -1;
ScaleMin := -1;
ScaleMax := -1;
end;
end;
However, that raises an EFDException:
[FireDAC][DatS]-32. Variable length column overflow. Value length - [10], column maximum length - [0].
What am I missing?
Why do I get "Invalid argument to date encode" exception when fetching tuple containing invalid DATE type field value?
The problem is that FireDAC expects DATE data type values represented as a string in fixed format YYYY-MM-DD where all members must be integers. Internally used FDStr2Int function doesn't use any detection of invalid input and works directly with ASCII ordinary values shifted by the 0 char, so input like e.g. GHIJ-KL-MN results in year 25676, month 298, day 320 after parsing. And just these misinterpreted values are then passed to the EncodeDate function, which fails for such values with the exception you've mentioned:
Invalid argument to date encode
The above happens inside the GetData method (ParseDate nested function). One possible way for fixing this issue on FireDAC's side could be using safer function TryEncodeDate instead of a direct encoding attempt. Similar problem is with TIME data type string value.
I using SQlite database im my Firemonkey Android application and there is no native DateTime type.
I storing date as text type
insert command:
insert into table (value,date_of_change)
values (:val,date('now'));
it works fine, date is correct stored, order by date works fine but if I want load this date into TDate edit
query:
select id,value,date_of_change
from table
where id = :MyID
code:
FDQuery1.Close;
FDQuery1.ParamByName('MyID').Value:= myid;
FDQuery1.OpenOrExecute;
FDQuery1.First;
NumberBox1.Value:=FDQuery1.FieldByName('suma').AsFloat;
DateEdit1.Date:=FDQuery1.FieldByName('date_of_change').AsDateTime;
I get error 2016-10-16 is not valid date and time but in Date edit I can see correct date !
Do anybody knows correct solution of this problem ?
Since you store the date as a string FireDAC fails to parse the format properly. You need to change the way the string value in the database column date_of_change is parsed using the correct date format.
So, instead of doing this:
DateEdit1.Date:=FDQuery1.FieldByName('date_of_change').AsDateTime;
You should do this:
function ParseDateFromDB(const DateStr: String): TDateTime;
var
FormatSettings: TFormatSettings;
begin
FormatSettings.DateSeparator := '-';
FormatSettings.ShortDateFormat := 'YYYY-MM-DD';
Result := StrToDate(DateStr, FormatSettings);
end;
//[...]
DateEdit1.Date := ParseDateFromDB(FDQuery1.FieldByName('date_of_change').AsString);
FireDAC uses its own mapping to SQLite data types and adds the DATE pseudo data type for you. So as there is the SINGLE pseudo data type that you can use for storing value of that number box.
So you can create your table by FireDAC like this:
FDQuery.SQL.Text := 'CREATE TABLE MyTable (DateField DATE, SingleField SINGLE)';
FDQuery.ExecSQL;
Then you can insert data:
FDQuery.SQL.Text := 'INSERT INTO MyTable (DateField, SingleField) VALUES (:DateField, :SingleField)';
FDQuery.ParamByName('DateField').AsDate := DateEdit.Date;
FDQuery.ParamByName('SingleField').AsSingle := NumberBox.Value;
FDQuery.ExecSQL;
And read them for example this way:
FDQuery.SQL.Text := 'SELECT DateField, SingleField FROM MyTable';
FDQuery.Open;
DateEdit.Date := DateOf(FDQuery.FieldByName('DateField').AsDateTime);
NumberBox.Value := FDQuery.FieldByName('SingleField').AsSingle;
I'm designing a function that is part of a larger package. The function is intended to take a District Code and return a collection of unique IDs for 10-15 stores that are assigned to that district. The function is intended to return a collection that can be queried like a table, i.e., using the TABLE function in a SQL statement.
I've created the following Types:
Schema Level type:
create or replace TYPE HDT_CORE_ORGIDS AS TABLE OF CHAR(20);
and a Type inside the Package
TYPE CORE_ORGIDS IS TABLE OF CHAR(20) INDEX BY BINARY_INTEGER;
Here's the function code:
FUNCTION FindDistrictOrgs(
ParamOrgCode VARCHAR2
)
RETURN HDT_CORE_ORGIDS
AS
ReturnOrgs HDT_CORE_ORGIDS := HDT_CORE_ORGIDS();
FDOTemp HDT_CORE_MAIN.CORE_ORGIDS;
i BINARY_INTEGER := 0;
CURSOR FDOCurr IS
SELECT org.id AS OrgID
FROM tp2.tpt_company org
WHERE LEVEL = 2
START WITH org.name = ParamOrgCode
CONNECT BY PRIOR org.id = org.parent_id;
BEGIN
OPEN FDOCurr;
LOOP
i := i +1;
FETCH FDOCurr INTO FDOTemp(i);
EXIT WHEN FDOCurr%NOTFOUND;
END LOOP;
IF FDOTemp.EXISTS(FDOTemp.FIRST) THEN
ReturnOrgs.EXTEND(FDOTemp.LAST);
FOR x IN FDOTemp.FIRST .. FDOTemp.LAST LOOP
ReturnOrgs(x) := FDOTemp(x).OrgID;
END LOOP;
END IF;
CLOSE FDOCurr;
RETURN ReturnOrgs;
END FindDistrictOrgs ;
I'm getting the PLS-00487:Invalid Reference to variable 'CHAR' at the line:
ReturnOrgs(x) := FDOTemp(x).OrgID;
I've double-checked at the value returned by the SQL (the org.id AS OrgID) is of the CHAR(20 BYTE) datatype.
So...what's causing the error?
Any help is appreciated! :)
OrgID is the alias you gave the column in your cursor, it has no meaning to the collection. Since both collections are of simple types you should just be doing:
ReturnOrgs(x) := FDOTemp(x);
The syntax you're using is implying FDOTemp is a collection of objects and you're trying to reference the OrgID attribute of an object; but since CHAR isn't an object type, this errors. The error message even makes some sense when viewed like that, though it's not terribly helpful if you don't already know what's wrong... and not entirely helpful when you do.
Incidentally, you could use a bulk collect to populate the collection without the cursor or loops, or the extra collection:
SELECT org.id
BULK COLLECT INTO ReturnOrgs
FROM tp2.tpt_company org
WHERE LEVEL = 2
START WITH org.name = ParamOrgCode
CONNECT BY PRIOR org.id = org.parent_id;
RETURN ReturnOrgs;