I am using GNAT.SHA1 to create a SHA1 hash of a string in Ada. My code base is small, so I'd like to avoid importing any libraries to the project that aren't provided by my compiler, hence I'm using GNAT.SHA1. As far as I'm aware, the only "public" methods for retrieving the hash is in String form via the Digest function. I would like to instead get the 160-bit block that is the H member of the Context type. Unfortunately, the Context record is private. Is there any way I can get at it? Or is their an alternative method provided by GNAT or the Ada standard library?
I don't know of any direct way to get the 160-bit block, but of course you can compute it from the hexadecimal string.
Here's an example I just threw together. (Granted, extracting 32-bit integers by adding "16#" and "#" to the hex substring may not be the most elegant solution.
with GNAT.SHA1;
with Interfaces;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure SHA1_Demo is
C: GNAT.SHA1.Context;
package U32_IO is new Ada.Text_IO.Modular_IO(Interfaces.Unsigned_32);
use U32_IO;
begin
GNAT.SHA1.Update(C, "hello");
declare
SHA1: constant String := GNAT.SHA1.Digest(C);
H: array(0..4) of Interfaces.Unsigned_32;
begin
Put_Line("SHA1(""hello"") = " & GNAT.SHA1.Digest(C));
for I in Integer range 0 .. 4 loop
H(I) := Interfaces.Unsigned_32'Value
("16#" & SHA1(I*8+1 .. I*8+8) & "#");
Put("H(");
Put(I, Width => 0);
Put(") = ");
Put(H(I), Base => 16);
New_Line;
end loop;
end;
end SHA1_Demo;
The output is:
SHA1("hello") = aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
H(0) = 16#AAF4C61D#
H(1) = 16#DCC5E8A2#
H(2) = 16#DABEDE0F#
H(3) = 16#3B482CD9#
H(4) = 16#AEA9434D#
Related
Trying to insert simple xml file with one row in IIB with simple message flow into Oracle XE DB. Message flow works fine and inserts data into database, but data written in db is different from starting data. For example, as I'm trying to insert my name "Dino" I'd get Korean/Japanese/Chinese signs in return.
I've tried changing XML formats thinking there might be problem, but I suppose it has to do with encoding.
Input:
Output in DB:
This is how my compute node looks like:
CREATE COMPUTE MODULE SimpleDB_mf_Compute
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
CALL CopyMessageHeaders();
-- CALL CopyEntireMessage();
INSERT INTO Database.dkralj.emp VALUES(InputRoot.XMLNSC.emp.name);
SET OutputRoot.XMLNSC.DBINSERT.STATUS='SUCCESS';
RETURN TRUE;
END;
CREATE PROCEDURE CopyMessageHeaders() BEGIN
DECLARE I INTEGER 1;
DECLARE J INTEGER;
SET J = CARDINALITY(InputRoot.*[]);
WHILE I < J DO
SET OutputRoot.*[I] = InputRoot.*[I];
SET I = I + 1;
END WHILE;
END;
CREATE PROCEDURE CopyEntireMessage() BEGIN
SET OutputRoot = InputRoot;
END;
END MODULE;
Looking at the IBM documentation for the INSERT statement in ESQL it might be worth trying.
INSERT INTO Database.dkralj(NAME) VALUES(InputRoot.XMLNSC.emp.name);
If weird things are still happening then I'd try a string constant to avoid any issues with character coding in the input message.
INSERT INTO Database.dkralj(NAME) VALUES('TheEmpValue');
Before this statement in your code
SET OutputRoot.XMLNSC.DBINSERT.STATUS='SUCCESS';
You should check for success or otherwise by using the inbuilt SQLSTATE, SQLCODE, SQLERRORTEXT to check the result of your call.
IF NOT ((SQLCODE = 0) OR (SQLSTATE = '01000' AND SQLNATIVEERROR = 8153)) THEN
-- Do something about the error.
-- The check of SQLSTATE and SQLNATIVEERROR covers warnings
-- The 8153 is for Microsoft SQL Server other databases may use a different value
END IF;
Also check the codepages aka CodedCharSetId of the source system data, the message in IIB and the default codepage of the database.
Use mqsicvp MYBROKER -n ODBC_DB_NAME to get other details about the connection you need to use -n to get the details.
Use something like DBeaver to add some data. Have a look at the datatype specified for the field.
As per your comment below and my response here is an example of a PASSTHRU statement. Note the use of the ? to avoid SQL Injection.
PASSTHRU('SELECT RTRIM(A.EMPLID) AS EMPLID,
RTRIM(A.ADDRESS_TYPE) AS ADDRESS_TYPE,
RTRIM(A.ADDR_TYPE_DESCR) AS ADDR_TYPE_DESCR,
CAST(RTRIM(A.EFFDT) AS DATE) AS EFFDT,
RTRIM(A.EFF_STATUS) AS EFF_STATUS,
RTRIM(A.ADDRESS1) AS ADDRESS1,
RTRIM(A.ADDRESS2) AS ADDRESS2,
RTRIM(A.ADDRESS3) AS ADDRESS3,
RTRIM(A.ADDRESS4) AS ADDRESS4,
RTRIM(A.CITY) AS CITY,
RTRIM(A.STATE) AS STATE,
RTRIM(A.POSTAL) AS POSTAL
FROM ADDRESS_VW AS A
WHERE UPPER(A.EMPLID) = ?') VALUES(AggrRef.EmployeeID)
CONCLUSION:
For some reason the flow wouldn't let me convert the incoming message to a BLOB by changing the Message Domain property of the Input Node so I added a Reset Content Descriptor node before the Compute Node with the code from the accepted answer. On the line that parses the XML and creates the XMLNSC Child for the message I was getting a 'CHARACTER:Invalid wire format received' error so I took that line out and added another Reset Content Descriptor node after the Compute Node instead. Now it parses and replaces the Unicode characters with spaces. So now it doesn't crash.
Here is the code for the added Compute Node:
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
DECLARE NonPrintable BLOB X'0001020304050607080B0C0E0F101112131415161718191A1B1C1D1E1F7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF1F2F3F4F5F6F7F8F9FAFBFCFDFEFF';
DECLARE Printable BLOB X'20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020';
DECLARE Fixed BLOB TRANSLATE(InputRoot.BLOB.BLOB, NonPrintable, Printable);
SET OutputRoot = InputRoot;
SET OutputRoot.BLOB.BLOB = Fixed;
RETURN TRUE;
END;
UPDATE:
The message is being parsed as XML using XMLNSC. Thought that would cause a problem, but it does not appear to be.
Now I'm using PHP. I've created a node to plug into the legacy flow. Here's the relevant code:
class fixIncompetence {
function evaluate ($output_assembly,$input_assembly) {
$output_assembly->MRM = $input_assembly->MRM;
$output_assembly->MQMD = $input_assembly->MQMD;
$tmp = htmlentities($input_assembly->MRM->VALUE_TO_FIX, ENT_HTML5|ENT_SUBSTITUTE,'UTF-8');
if (!empty($tmp)) {
$output_assembly->MRM->VALUE_TO_FIX = $tmp;
}
// Ensure there are no null MRM fields. MessageBroker is strict.
foreach ($output_assembly->MRM as $key => $val) {
if (empty($val)) {
$output_assembly->MRM->$key = '';
}
}
}
}
Right now I'm getting a vague error about read only messages, but before that it wasn't working either.
Original Question:
For some reason I am unable to impress upon the senders of our MQ
messages that smart quotes, endashes, emdashes, and such crash our XML
parser.
I managed to make a working solution with SQL queries, but it wasted
too many resources. Here's the last thing I tried, but it didn't work
either:
CREATE FUNCTION CLEAN(IN STR CHAR) RETURNS CHAR BEGIN
SET STR = REPLACE('–',STR,'–');
SET STR = REPLACE('—',STR,'—');
SET STR = REPLACE('·',STR,'·');
SET STR = REPLACE('“',STR,'“');
SET STR = REPLACE('”',STR,'”');
SET STR = REPLACE('‘',STR,'&lsqo;');
SET STR = REPLACE('’',STR,'’');
SET STR = REPLACE('•',STR,'•');
SET STR = REPLACE('°',STR,'°');
RETURN STR;
END;
As you can see I'm not very good at this. I have tried reading about
various ESQL string functions without much success.
So in ESQL you can use the TRANSLATE function.
The following is a snippet I use to clean up a BLOB containing non-ASCII low hex values so that it then be cast into a usable character string.
You should be able to modify it to change your undesired characters into something more benign. Basically each hex value in NonPrintable gets translated into its positional equivalent in Printable, in this case always a full-stop i.e. x'2E' in ASCII. You'll need to make your BLOB's long enough to cover the desired range of hex values.
DECLARE NonPrintable BLOB X'000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F';
DECLARE Printable BLOB X'2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E';
SET WorkBlob = TRANSLATE(WorkBlob, NonPrintable, Printable);
BTW if messages with invalid characters only come in every now and then I'd probably specify BLOB on the input node and then use something similar to the following to invoke the XMLNSC parser.
CREATE LASTCHILD OF OutputRoot DOMAIN 'XMLNSC'
PARSE(InputRoot.BLOB.BLOB CCSID InputRoot.Properties.CodedCharSetId ENCODING InputRoot.Properties.Encoding);
With the exception terminal wired up you can then correct the BLOB's of any messages containing parser breaking invalid characters before attempting to reparse.
Finally my best wishes as I've had a number of battles over the years with being forced to correct invalid message content in the "Integration Layer" after all that's what it's meant to do.
Delphi XE6 - I have a set. I would like a simple way to turn ALL elements off. i.e. instead of Exclude, something like ExcludeALL. I have tried to loop through all elements, but I get an error.
Code
type
TSearchParametersType =
(smDUNSAvailable = 1,
smDUNSHit,
smDUNSMiss,
smDUNSAbsent,
smRegistryAvailable,
smRegistryHit,
smRegistryAbsent,
smRegistryMiss,
smNameAvailable,
smNameHitExact,
smNameHitWords,
smNameMiss
);
// Now create a set type, where we can have a variable that has all the values of TSearchParametersType
type
TSearchParametersSet = set of TSearchParametersType;
...
var
i : Integer;
sSearchStatus: TSearchParametersSet;
begin
for i := smDUNSAvailable to smNameMiss do
Exclude(sSearchStatus, i);
The error I get is "Incompatible Type: 'Integer' and TSearchParametersType. "
Is there a simple way to Exclude ALL, other than MANUALLY going through every element?
Thanks
From the documentation:
Every set type can hold the empty set, denoted by [].
So you can assign the empty set to your variable like this:
sSearchStatus := [];
FWIW, your code fails because smDUNSAvailable and smNameMiss are of type TSearchParametersType and so not compatible with the variable i which is of type Integer. In order to make your code work you would need to change the loop variable to be of type TSearchParametersType.
Let me start by saying that David's answer is the correct one.
I'll just post another one to show how you could do it manually. This code might come in handy some other time:
var
sSearchStatus: TSearchParametersSet;
SearchParametersType : TSearchParametersType;
begin
sSearchStatus := [smDUNSHit, smDUNSMiss, smDUNSAbsent, smRegistryAvailable, smRegistryHit];
for SearchParametersType := low(TSearchParametersType) to high(TSearchParametersType) do
Exclude(sSearchStatus, SearchParametersType);
end;
I have a simple Oracle procedure as below. I am trying to call the procedure using VB6 and extract the output from the procedure.
CREATE OR REPLACE PROCEDURE EXTRACTTXN (reportdate IN DATE, p_recordset OUT SYS_REFCURSOR) AS
BEGIN
OPEN p_recordset FOR
SELECT
TXN_ID,
TXN_ACTION,
TXN_STATUS,
TXN_DATE,
TXN_AMOUNT
FROM TRANSACTIONS
WHERE
TRUNC(TXN_DATE) = TRUNC(reportdate)
END EXTRACTTXN;
The VB Code goes like this;
Sub FetchTransactions(ByVal ReportDate As Date, cnnMine as ADODB.Connection)
On Error GoTo TrapErr
Dim cmdMine As ADODB.Command, rsMine As ADODB.Recordset
cmdMine.ActiveConnection = cnnMine
cmdMine.CommandTimeout = 300
cmdMine.CommandType = adCmdStoredProc
cmdMine.CommandText = "EXTRACTTXN"
cmdMine.Parameters.Append cmdMine.CreateParameter("reportdate", adDate, adParamInput, , Format(ReportDate, "DD-MMM-YYYY"))
cmdMine.Parameters.Append cmdMine.CreateParameter("p_recordset", adVariant, adParamOutput)
Set rsMine = cmdMine.Execute
Do While rsMine.EOF
Debug.Print rsMine!TXN_ID, rsMine!TXN_ACTION, rsMine!TXN_STATUS, rsMine!TXN_DATE, rsMine!TXN_AMOUNT
rsMine.MoveNext
Loop
rsMine.Close
Exit Sub
TrapErr:
MsgBox Err.Number & " - " & Err.Description, vbExclamation, App.ProductName
End Sub
While running the code, I get the following Error:
ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of arguments in call to 'EXTRACTTXN'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
Anything wrong in my code? Appreciate help.
Niz
The problem is that the types of your arguments as specified in your VB code don't match the types of the arguments as specified in your PL/SQL code. The most likely reason for your problem is that the Format function in VB6 returns a Variant type, not a Date type, and that Variant type is set to be a String type. See this for more information on the Format function.
In case you don't know, the way that Variant variables are set up is that they reserve 8 bytes to tell the world what the actual variable type is. So, if you pass your ReportDate in after applying the Format function to it, it will be a Variant that's telling the world it's a string. It's possible that the ADO Parameter object is happy with that (SQL Server is happy to parse properly-formatted strings into Date fields, after all) and Oracle isn't. In my limited experience with Oracle, I've found that it's fussier about that sort of thing than SQL Server.
Try losing the Format function and see if you at least get a different error.
I have managed to get this sorted. It's mainly due to me being new to Oracle and its complexity.
Here are the changes I made;
Stored Procedure Changes. Note that I have changed TRUNC(reportdate, 'DD') on the Where clause.
CREATE OR REPLACE PROCEDURE EXTRACTTXN (reportdate IN DATE, p_recordset OUT SYS_REFCURSOR) AS
BEGIN
OPEN p_recordset FOR
SELECT
TXN_ID,
TXN_ACTION,
TXN_STATUS,
TXN_DATE,
TXN_AMOUNT
FROM TRANSACTIONS
WHERE
TRUNC(TXN_DATE) = TRUNC(reportdate, 'DD')
END EXTRACTTXN;
VB Code Changes (Note that I have change the CommandText within parenthesis with a Call, removed the parameter name, changed the date format to DD/MMM/YYYY and removed the output parameter)
Sub FetchTransactions(ByVal ReportDate As Date, cnnMine as ADODB.Connection)
On Error GoTo TrapErr
Dim cmdMine As ADODB.Command, rsMine As ADODB.Recordset
cmdMine.ActiveConnection = cnnMine
cmdMine.CommandTimeout = 300
cmdMine.CommandType = adCmdStoredProc
cmdMine.CommandText = "{ call EXTRACTTXN}"
cmdMine.Parameters.Append cmdMine.CreateParameter(, adDate, adParamInput, , Format(ReportDate, "DD/MMM/YYYY"))
Set rsMine = cmdMine.Execute
Do While rsMine.EOF
Debug.Print rsMine!TXN_ID, rsMine!TXN_ACTION, rsMine!TXN_STATUS, rsMine!TXN_DATE, rsMine!TXN_AMOUNT
rsMine.MoveNext
Loop
rsMine.Close
Exit Sub
TrapErr:
MsgBox Err.Number & " - " & Err.Description, vbExclamation, App.ProductName
End Sub
The above worked perfectly.
Regards, Niz
I am using the StringOfChar function to create a string of chars like this
S := StringOfChar('b', 5); //s is set to 'bbbbb'
Is there any RTL function that does the same (like StringofString('abc',3) must return 'abcabcabc' ), but accepts a string as parameter instead of char?
I can write a function like that but I'm looking for an existing RTL function.
the function which you are looking for is StrUtils.DupeString
s:=DupeString('abc',3)
will return
'abcabcabc'