Oracle AQ dequeuing an array - oracle

Good day,
respective all!
Environment: Oracle 18XE 64-bit for Windows.
I have a question about dequeueing an array of messages from persistent queue.
It’s a simple point-to-point messaging. Queue is “single_consumer”, without propagation.
I registered my PL/SQL callback function.
I need to know an exact size of array of messages to dequeue
in every call of my callback function from Oracle AQ internal job.
And I found the only legal way how to have done it. And this way is to register callback
with qosflags parameter of sys.aq$reg_info equal to dbms_aq.NTFN_QOS_PAYLOAD.
Here is the registration PL/SQL block:
declare
v_qosflags number := dbms_aq.NTFN_QOS_PAYLOAD;
r_info SYS.AQ$_REG_INFO;
begin
r_info := SYS.AQ$_REG_INFO(
'STERN.FOUNDERS_QUEUE',
DBMS_AQ.NAMESPACE_AQ,
'plsql://stern.dosomecalc',
HEXTORAW('FF')
);
r_info.qosflags := v_qosflags;
r_info.ntfn_grouping_class := dbms_aq.NTFN_GROUPING_CLASS_TIME ;
r_info.ntfn_grouping_value := 60;
r_info.ntfn_grouping_type := dbms_aq.NTFN_GROUPING_TYPE_SUMMARY ;
DBMS_AQ.REGISTER (
SYS.AQ$_REG_INFO_LIST(
r_info
),
1
);
end;
Here is the declaration of callback procedure. It is a standard declaration:
create or replace procedure dosomecalc
(context RAW
,reginfo SYS.AQ$_REG_INFO
,descr SYS.AQ$_DESCRIPTOR
,payload raw
,payloadl NUMBER)
Now, thankfully to qosflags parameter initialized with dbms_aq.NTFN_QOS_PAYLOAD ,my callback function is registered in such a way that I always can see real size
of messages to dequeue in callback session.
This size is evaluated as counting size of descr.msgid_array part of descr parameter.
Without setting of qosflags during registration to some value - this part of descr parameter always comes empty to callback procedure call.
Once I know the real size of messages array , I can use it in
Dbms_aq.dequeue_array(…, array_size => descr.msgid_array.count,…)
dequeuing call inside my callback function.
Than, after investigating contents of descr parameter, I found in it an ntfnsRecdInGrp element,
and decided that ntfnsRecdInGrp is always equal to descr.msgid_array.count,
and just made for programmer’s convenience, just for duplicate descr.msgid_array.count.
AQ documentation says:
msgid_array - Group notification message ID list
ntfnsRecdInGrp - Notifications received in group
That was why I decided that they are equal by value.
It was a my mistake. When I use callback with array size equal to descr.msgid_array.count –
everything is OK. With ntfnsRecdInGrp – no. Sometimes descr.msgid_array.count
and ntfnsRecdInGrp equal to each other, sometimes not.
Now the question is:
What is the meaning of ntfnsRecdInGrp part of descr parameter?
Why it is not the same as msgid_array.count?
Thanks in advance.

Related

Delphi TTreeView OnCustomDrawItem event slows it down

I have an outliner application (in Delphi 10.2 Tokyo) that I use for taking notes (I'll call it NoteApp). I have another application that I use for editing plain text (TextApp). Since I switch between these applications a lot, I decided to integrate note-taking abilities within TextApp.
I copy/pasted the code from NoteApp to TextApp, and I put the components (one TTreeView, one TRichEdit, and one TActionToolbar) on TextApp.Form_Main .
OnCustomDrawItem event of the TTreeView is set to change FontStyle of each Node based on the NoteType of the corresponding note item which is an array of simple records:
type
///
/// Note types
///
TNoteType = (ntNote, ntTodo, ntDone, ntNext, ntTitle) ;
///
///
///
TNote = Record
Text ,
Attachment ,
Properties ,
CloseDate : String ;
NoteType : TNoteType ;
End;
Our array:
var
Notes: Array of TNote ;
And the event:
procedure TForm_Main.TreeView_NotesCustomDrawItem(Sender: TCustomTreeView;
Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
///
/// First check to see if the application allows us to change visuals. If the
/// application is in processing mode, visual updates are not allowed.
///
if ChangeAllowed AND Node.IsVisible then
begin
///
/// Check NoteType of the corresponding note:
///
case Notes[Node.AbsoluteIndex].NoteType of
ntTitle:
begin
TreeView_Notes.Canvas.Font.Style := [fsBold] ;
end;
//ntNote:
// begin
// end;
ntTodo:
begin
TreeView_Notes.Canvas.Font.Style := [fsBold] ;
end;
ntNext:
begin
TreeView_Notes.Canvas.Font.Style := [fsUnderline] ;
end;
ntDone:
begin
TreeView_Notes.Canvas.Font.Style := [fsStrikeOut] ;
end;
end;
end;
end;
When I open a note file in NoteApp, it works perfectly. When I open the same file in TextApp, TTreeView refreshes slowly. The top items in the TTreeView are alright, but the lower you go, the lower the refreshing rate.
The properties of all of the components are identically set.
I suspect I have made a mistake somewhere. I set the visibility of all of the other components on TextApp to false, but the TTreeView is still abysmally slow.
If I remove the code above, it becomes fast again. I don't use runtime themes in TextApp.
Okay, I found the answer to the question.
The answer was hidden in the code above, and what I posted was enough to answer the question. Turns out the code that I have posted was MCVE after all. I am posting the answer in case it happens to someone else.
Answer:
Turns out Node.AbsoluteIndex is incredibly slow. It shouldn't be used as an index.
Solution 1:
I used Node.Data as an index, and now it is very fast.
Solution 2:
An alternative solution that I tried and worked:
TTreeNodeNote = class(TTreeNode)
public
Note: TNote;
end;
procedure TForm_Main.TreeView_NotesCreateNodeClass(Sender: TCustomTreeView;
var NodeClass: TTreeNodeClass);
begin
NodeClass := TTreeNodeNote;
end;
Then we store the data in the Note property of each Node, instead of a separate array. Works like a charm.

PLSQL - Check if an object exists in a table of objects - type has no map

I have a procedure that has got this variables of type table of objects:
lsa_final_filter_ports t_modifylink_multicolumnlist;
lsa_initial_filter_ports t_modifylink_multicolumnlist;
Definition of t_modifylink_multicolumnlist is:
CREATE OR REPLACE TYPE "T_MODIFYLINK_MULTICOLUMNLIST" IS TABLE OF o_modifylink_multicolumnlist;
TYPE o_modifylink_multicolumnlist AS OBJECT(some properties);
lsa_final_filter_ports variable is populated like this
SELECT b.name INTO ls_bandwidth_name FROM bandwidth b
WHERE b.bandwidthid = lna_compatible_port_bw(i);
SELECT CAST(MULTISET(SELECT *
FROM TABLE(piosa_all_ports)
WHERE ITEMNAME4 = ls_bandwidth_name)
AS t_modifylink_multicolumnlist)
INTO lsa_final_filter_ports
FROM dual;
Where piosa_all_ports is I/O parameter having same type t_modifylink_multicolumnlist
Second parameter is initialized at the beginig of procedure with a second I/O parameter that the procedure has
lsa_initial_filter_ports := piosa_filtered_ports;
What I want to achieve is to check if an object from lsa_final_filter_ports exists in lsa_initial_filter_ports and if so then skip adding that object in lsa_initial_filter_ports which will be an output parameter used by the outside calling procedure.
What I've tried is iterating through lsa_final_filter_ports objects and checking if that object already exists like this:
FOR i in 1..lsa_final_filter_ports.COUNT LOOP
IF lsa_final_filter_ports(i) MEMBER OF lsa_initial_filter_ports THEN
CONTINUE;
END IF;
lsa_initial_filter_ports.EXTEND();
lsa_initial_filter_ports(lsa_initial_filter_ports.COUNT) := lsa_final_filter_ports(i);
END LOOP;
But with this code I'm getting the following error:
Error: PLS-00801: internal error [*** ASSERT at file pdw4.c, line
2181; Type 0x0x7f991127aef8 has no MAP method.;
NR_WIZARDVALIDATIONS__CUSTOMISATIONS__B__166833[33]
I'm not sure if this kind of comparation can be made, maybe someone can clarify this.
Thank you
Unfortunately, if you have objects in PlSql, which only exist in RAM (that means those, that are NOT stored persistently in a table), assigning them to another variables always causes a (deep) copy of the origin object.
(which is really a problem, if you often assign objects with nested objects to working-variables and back to the collection ...)
Therefore, you´ll never will be able to compare two (copies) of the same object on object-pointer level.
Only thing you can do, is comparing some unique attributes of the objects.
Let´s say, you´ll have an ID in your piosa_all_ports, then you might modify your loop above with the comparison in something similar to this:
v_exists integer;
FOR i in 1..lsa_final_filter_ports.COUNT LOOP
select nvl(max(1),0)
from table(lsa_initial_filter_ports) x
where x.id = lsa_final_filter_ports(i).id;
if (v_exists = 0) then
lsa_initial_filter_ports.EXTEND();
lsa_initial_filter_ports(lsa_initial_filter_ports.COUNT) := lsa_final_filter_ports(i);
end if;
END LOOP;
(rem.: i know, that switching PlSql- and SQL-Context in sample above is time-consuming, but otherwise, you have to write a function that tests the existance of an ID by iterating the whole list)

TEdit property variable delphi

I have a form in Delphi including several TEdit boxes. I would like a way to check the validation all of the boxes kind of, currently I would have to have a boolean variable assigned to each TEdit. e.g.
Var
TEdit1Valid:boolean;
TEdit2Valid:boolean;
etc, etc for every TEdit on the form.
For the first method it would have to look something like this:
If TEdit1Valid then
ShowMessage('Your', Edit1.Name, 'is invalid');
etc, etc for each TEdit box.
This creates many variables and I was just wondering if there is a better way to do it. One idea I had is to assign a property called Valid to a TEdit, but I don't know how to do this. With this method I could then write an array of TEdits for each TEdit and execute the following
for I := 1 to High(EditList) do
begin
if EditList[I].Valid = false then
ShowMessage('Your ', EditList[I].Name, 'is invalid');
end
This is just an example bit of rough code that I just wrote and could use along with other things I could do iteratively like this.
So could someone either suggest a good method of doing this or show me how to do the afore mentioned method.
How about using the OnExit event handler on the TEdit controls? For example:
procedure TForm1.Edit1Exit(Sender: TObject);
begin
with Sender as TEdit do
begin
if not IsValidEdit(Text) then
ShowMessage('Your ' + Name + ' is invalid');
end;
end;
Then apply that same OnExit event to all your TEdit controls. This example "IsValidEdit" function that I used above can validate the Text values of your TEdit, or you could just put your validation code within the OnExit event (but it would be "cleaner", in my opinion, to use a specific function to validate).
function IsValidEdit(const AValue: string): boolean;
begin
if Length(AValue) < 5 then // not valid if length less than 5
Result := False
else
Result := True;
end;
You can use the Tag property. It an integer rather than a boolean. According to the Embarcadero help -
Tag has no predefined meaning. The Tag property is provided for the
convenience of developers. It can be used for storing an additional
integer value or it can be typecast to any 32-bit value such as a
component reference or a pointer.
OnExit is good for individual field validations, and is useful when you don't want the user to leave the field empty or with an improper value. It's not advisable for overall form validation, although it works.
For form validation, you're better off creating a method (eg., Validate) that's called in the OnClose handler, and if it fails, show a message, position the cursor on the field in question, then set Action := caNone, which will prevent the form from closing.

How can I call an Oracle function from Delphi?

I created a function in Oracle that inserts records in specific tables and return an output according to what occurs within the function. e.g (ins_rec return number)
How do I call this function and see its output in Delphi?
I got a reply (with all my thanks) for sql plus but I need to know how can I do this in Delphi
Just pass the user defined function as column name in the query and it will work.
Example:
Var
RetValue: Integer;
begin
Query1.Clear;
Query1.Sql.Text := 'Select MyFunction(Param1) FunRetValue from dual';
Query1.Open;
if not Query1.Eof then
begin
RetValue := Query1.FieldByName('FunRetValue').AsInteger;
end;
end;
How to accomplish it may depend on what DB access library you use (BDE? dbExpress? ADO? others), some may offer a "stored procedure" component that may work with functions as well.
A general approach it to use an anonymous PL/SQL block to call the function (and a parameter to read the return value), PL/SQL resembles Pascal a lot...:
Qry.SQL.Clear;
Qry.SQL.Add('BEGIN');
Qry.SQL.Add(' :Rez := ins_rec;');
Qry.SQL.Add('END;');
// Set the parameter type here...
...
Qry.ExecSQL;
...
ReturnValue := Qry.ParamByName('Rez').Value;
I would not have used a function, though, but a stored procedure with an OUT value. Moreover, Oracle offers packages that are a very nice way to organize procedure and functions, and they also offer useful features like session variables and initialization/finalization sections... very much alike a Delphi unit.
we run an Oracle stored procedure using this code that utilizes the BDE (I know please don't bash because we used the BDE!)
Try
DMod.Database1.Connected:= False;
DMod.Database1.Connected:= True;
with DMod.StoredProc1 do
begin
Active:= False;
ParamByName('V_CASE_IN').AsString:= Trim(strCaseNum);
ParamByName('V_WARRANT_IN').AsString:= strWarrantNum;
ParamByName('V_METRO_COMMENTS').AsString:= strComment;
Prepare;
ExecProc;
Result:= ParamByName('Result').AsString;
end;
Except

Detecting the number of ORACLE rows updated by a OCI OCIStmtExecute call

I have an ORACLE update statement that I call using the OCIStmtExecute function call.
Using an OCI function call I would like to know how many rows have been updated by the action, e.g. zero, one or more.
How do I do this ?
Use the OCIAttrGet function call on your OCIStmt statement handle with the attribute type set to OCI_ATTR_ROW_COUNT
So add the folllowing code to your program :
ub4 row_count;
rc = OCIAttrGet ( stmthp, OCI_HTYPE_STMT, &row_count, 0, OCI_ATTR_ROW_COUNT,
errhp );
where:
stmthp is the OCIStmt statement handle
errhp is the OCIError error handle
rc is the defined return code (sword)
The number of rows updated (or deleted and inserted if that is your operation) is written into the passed row_count variable
Invoke OCIAttrGet(OCI_ATTR_ROW_COUNT) on the statement handle.

Resources