I'm really stuck in how to pass an argument to a second level of an Eiffel agent call. Hope my example is enough self explaining:
main_call
do
b (agent d(?, ?))
end
b (a_proc: PROCEDURE[STRING])
local
l_something_else: INTEGER
do
l_something_else := 1
-- z("a_line", a_proc(l_something_else)) -- How can I pass at this stade l_something_else to the d procedure!!!
end
d (line: STRING; something_else: INTEGER)
do
-- do_some_stuff_with_line (line, something_else)
do_nothing
end
z (a_line: STRING; a_proc: PROCEDURE[STRING])
do
a_proc.call([a_line])
end
I would have liked to be able to do something like
z("a_line", a_proc(?, something_else))
But its not possible, as I try with the agent keyword, the a_proc argument is not recognized!
So what would be the syntax? I even tried to add an argument to a_proc with a_proc.set_operands but am lost with the OPEN_ARGS class
Implementation case
If you need a goal... just imagine I'd like to have 2 different implementations of the d function, and in my case its using UT_CSV_HANDLER that I'd like to have 2 different functions for each line of a CVS
The following code gives a Non-compatible actual argument in feature call
Feature: import_from_csv_impl
Called feature: import_from_csv_impl (a_rest_request: REST_REQUEST; a_procedure: PROCEDURE [DS_ARRAYED_LIST [STRING_8], INTEGER_64, STRING_8]): [detachable like items] detachable SIT_LINKED_LIST [MEASURING_POINT] from MEASURING_POI...
Argument name: ia_name
Argument position: 2
Formal argument type: STRING_8
Actual argument type: INTEGER_64
Line: 258
ia_procedure.call (ia_measuring_point_id, ia_name)
-> end (?, l_measuring_point_id, l_s)
l_csv_handler.read_file (l_is, l_partially_closed)
The complete example:
-- Main call
import_from_abb_csv (a_rest_request: REST_REQUEST): detachable like items
do
Result := import_from_csv_impl (a_rest_request, agent impl_for_each_csv_line_import_from_abb_csv)
end
-- Second call
import_from_csv_impl (a_rest_request: REST_REQUEST; a_procedure: PROCEDURE[DS_ARRAYED_LIST [STRING_8], INTEGER_64, STRING]): detachable like items
local
l_csv_handler: UT_CSV_HANDLER
l_is: KL_STRING_INPUT_STREAM
l_measuring_point_id: INTEGER_64
l_s: STRING
l_partially_closed: PROCEDURE[DS_ARRAYED_LIST[STRING]]
do
l_s := "whatever"
l_measuring_point_id := 12
create l_csv_handler.make_with_separator (',')
l_partially_closed := agent (i_al: DS_ARRAYED_LIST[STRING]; ia_measuring_point_id: INTEGER_64; ia_name: STRING; ia_procedure: PROCEDURE[INTEGER_64, STRING])
do
ia_procedure.call (ia_measuring_point_id, ia_name)
end (?, l_measuring_point_id, l_s)
l_csv_handler.read_file (l_is, l_partially_closed)
end
-- end call
impl_for_each_csv_line_import_from_abb_csv (a_csv_line: DS_ARRAYED_LIST [STRING_8]; a_measuring_point_id: INTEGER_64; l_cu_name: STRING)
do
-- do_my_business
end
-- for information signature of read_file is:
-- read_file (a_file: KI_TEXT_INPUT_STREAM; a_action: PROCEDURE [DS_ARRAYED_LIST [STRING]])
agent d or agent d (?,?) (they are both equivalent) produces a PROCEDURE [STRING, INTEGER], with both operands of d still open. Because of tuple covariance, PROCEDURE [STRING, INTEGER] conforms to PROCEDURE [STRING], thus your implementation of a compiles, but attempting to call the agent with only a TUPLE [STRING] instead of a TUPLE [STRING, INTEGER] as operands (which b does) will cause a runtime exception (probably a catcall).
One way to progressively close the operands of an agent is to wrap it in another agent with one less open operand:
b (a_procedure: PROCEDURE [STRING, INTEGER])
local
l_something_else: INTEGER
l_partially_closed: PROCEDURE [STRING]
do
l_something_else := 1
l_partially_closed := agent (ia_operand_1: STRING; ia_operand_2: INTEGER; ia_procedure: PROCEDURE [STRING, INTEGER])
do
ia_procedure.call (ia_operand_1, ia_operand_2)
end (?, l_something_else, a_procedure)
-- Notice how only one operand is left open
z ("a_line", l_partially_closed)
end
Alternatively, l_something_else could be declared inside the inline agent:
b (a_procedure: PROCEDURE [STRING, INTEGER])
local
l_partially_closed: PROCEDURE [STRING]
do
l_partially_closed := agent (ia_operand_1: STRING; ia_procedure: PROCEDURE [STRING, INTEGER])
local
il_something_else: INTEGER
do
il_something_else := 1
ia_procedure.call (ia_operand_1, il_something_else)
end (?, a_procedure)
z ("a_line", l_partially_closed)
end
You could also use {ROUTINE}.set_operands and {ROUTINE}.apply, but it is less flexible and more error-prone in my opinion, as well as not thread-safe.
b (a_procedure: PROCEDURE [STRING, INTEGER])
local
l_something_else: INTEGER
l_partially_closed: PROCEDURE [STRING]
do
l_something_else := 1
a_procedure.set_operands ("some string you will override later", l_something_else)
z ("a_line", a_procedure)
end
z (a_line: STRING; a_proc: PROCEDURE [STRING]) -- or PROCEDURE [STRING, INTEGER]
do
-- You have no guarantee that `a_proc' has any operands set (though you could make it a precondition)
-- This is why it is less safe and less reusable than the previous approach
check attached a_proc.operands as la_operands then
la_operands [1] = a_line
end
a_proc.apply
end
UPDATE
Given your implementation case, see the comments starting with 'HERE':
-- Main call
import_from_abb_csv (a_rest_request: REST_REQUEST): detachable like items
do
Result := import_from_csv_impl (a_rest_request, agent impl_for_each_csv_line_import_from_abb_csv)
end
-- Second call
import_from_csv_impl (a_rest_request: REST_REQUEST;
a_procedure: PROCEDURE[DS_ARRAYED_LIST [STRING_8], INTEGER_64, STRING]): detachable like items
local
l_csv_handler: UT_CSV_HANDLER
l_is: KL_STRING_INPUT_STREAM
l_measuring_point_id: INTEGER_64
l_s: STRING
l_partially_closed: PROCEDURE[DS_ARRAYED_LIST[STRING]]
do
l_s := "whatever"
l_measuring_point_id := 12
create l_csv_handler.make_with_separator (',')
l_partially_closed := agent (i_al: DS_ARRAYED_LIST[STRING];
ia_measuring_point_id: INTEGER_64;
ia_name: STRING;
ia_procedure: PROCEDURE[DS_ARRAYED_LIST [STRING_8], INTEGER_64, STRING]) -- HERE, change the declared type of `ia_procedure' to match `a_procedure'
do
ia_procedure.call (i_al, ia_measuring_point_id, ia_name) -- HERE, add `i_al' to `call'
end (?, l_measuring_point_id, l_s, a_procedure) -- HERE, add `a_procedure'
l_csv_handler.read_file (l_is, l_partially_closed)
end
-- end call
impl_for_each_csv_line_import_from_abb_csv (a_csv_line: DS_ARRAYED_LIST [STRING_8]; a_measuring_point_id: INTEGER_64; l_cu_name: STRING)
do
-- do_my_business
end
-- for information signature of read_file is:
-- read_file (a_file: KI_TEXT_INPUT_STREAM; a_action: PROCEDURE [DS_ARRAYED_LIST [STRING]])
Related
I am trying to create a program that prints 11 buttons so I wanted to use an array. The only change with these buttons is the name.
When I try to compile, I get the error "illegal qualifier" at my first array assignment.
type
buttonName = array[0..11] of String;
procedure PopulateButton(const buttonName);
begin
buttonName[0] := 'Sequence';
buttonName[1] := 'Repetition';
buttonName[2]:= 'Modularisation';
buttonName[3]:= 'Function';
buttonName[4]:= 'Variable';
buttonName[5]:= 'Type';
buttonName[6]:= 'Program';
buttonName[7]:= 'If and case';
buttonName[8]:= 'Procedure';
buttonName[9]:= 'Constant';
buttonName[10]:= 'Array';
buttonName[11]:= 'For, while, repeat';
end;
and in main I am trying to use this for loop
for i:=0 to High(buttonName) do
begin
DrawButton(x, y, buttonName[i]);
y:= y+70;
end;
Please know, I am very new to this and am not too confident of my knowledge in arrays, parameters/calling by constant and such.
Thank you
The parameter definition of PopulateButton() is wrong.
Try this:
type
TButtonNames = array[0..11] of String;
procedure PopulateButtons(var AButtonNames: TButtonNames);
begin
AButtonNames[0] := 'Sequence';
...
end;
...
var lButtonNames: TButtonNames;
PopulateButtons(lButtonNames);
for i := Low(lButtonNames) to High(lButtonNames) do
begin
DrawButton(x, y, lButtonNames[i]);
y:= y+70;
end;
Also pay attention to the naming conventions. Types normally begin with a T and function parameters start with an A.
ALL
I have a postgresql function,so this:
CREATE OR REPLACE FUNCTION query_callouts(
INOUT io_cursor_ref refcursor,
INOUT opstatus integer,
INOUT errtext character varying)
RETURNS record AS
$BODY$
DECLARE
BEGIN
OPEN $1 FOR
SELECT tablename FROM pg_tables limit 10;
--SELECT * from call_out_numbers;
RETURN;
Exception
When Others Then
GET STACKED DIAGNOSTICS opstatus = RETURNED_SQLSTATE,
errText = MESSAGE_TEXT;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION query_callouts(refcursor, integer, character varying)
OWNER TO postgres;
I want to known ,how to use libpq in my c codes to visit the function - query_callouts and get the param io_cursor_ref and opstatus and errtext?
You can call the function just like you are executing any query:
select * from query_callouts('mycur', 0, '');
io_cursor_ref | opstatus | errtext
---------------+----------+---------
mycur | 0 |
(1 row)
opstatus and errtext would be set to the appropriate values if an exception occurs.
io_cursor_ref contains the name you have passed to the function.
Internally, a refcursor value is simply the string name of a so-called
portal containing the active query for the cursor. This name can be
passed around, assigned to other refcursor variables, and so on,
without disturbing the portal.
Note, you can use a refcursor only within a transaction.
All portals are implicitly closed at transaction end. Therefore a
refcursor value is usable to reference an open cursor only until the
end of the transaction.
You can use explicit transaction:
begin;
select * from query_callouts('mycur', 0, '');
fetch all in mycur;
-- save or show the query result
-- and finally
commit;
or use mycur inside a function.
The quotes are from the documentation.
It's just like any other query.
SELECT * FROM query_callouts('cursorname', 4, 'msg')
I suspect some if not all of your parameters should be OUT parameters not INOUT parameters, though. You don't ever use the input value of errtext at least.
I am passing an array to a PL/SQL package function. I am doing this to use this array in a query inside the function which has IN clause.
My declaration of package looks like :
create or replace
PACKAGE selected_pkg IS
TYPE NUM_ARRAY IS TABLE OF NUMBER;
FUNCTION get_selected_kml(
in_layer IN NUMBER,
in_id IN NUMBER,
in_feature_ids IN selected_pkg.NUM_ARRAY,
in_lx IN NUMBER,
in_ly IN NUMBER,
in_ux IN NUMBER,
in_uy IN NUMBER
)
RETURN CLOB;
END selected_pkg;
In my PL/SQL function I am firing a query like following
select a.id, a.geom from Table_FIELD a where a.id in (select * from table (in_feature_ids)) and sdo_filter(A.GEOM,mdsys.sdo_geometry(2003,4326,NULL,mdsys.sdo_elem_info_array(1,1003,3), mdsys.sdo_ordinate_array(0,57,2.8,59)),'querytype= window') ='TRUE'
The same query runs fine if I run it from anonymous block like
CREATE TYPE num_arr1 IS TABLE OF NUMBER;
declare
myarray num_arr1 := num_arr1(23466,13396,14596);
BEGIN
FOR i IN (select a.id, a.geom from Table_FIELD a where a.id in (select * from table (myarray)) and sdo_filter(A.GEOM,mdsys.sdo_geometry(2003,4326,NULL,mdsys.sdo_elem_info_array(1,1003,3), mdsys.sdo_ordinate_array(0,57,2.8,59)),'querytype= window') ='TRUE'
loop
dbms_output.put_line(i.id);
end loop;
end;
If I try to run it by calling function as below
--Running function from passing array for IDs
declare
result CLOB;
myarray selected_pkg.num_array := selected_pkg.num_array(23466,13396,14596);
begin
result:=SELECTED_PKG.get_selected_kml(3, 19, myarray, 0.0,57.0,2.8,59);
end;
I am getting error
ORA-00904: "IN_FEATURE_IDS": invalid identifier
Could someone please help me understand the cause of it?
Thanks,
Alan
You cannot query a type declared in plsql in a sql query, as the sql engine doesn't recognise it.
Your first example works because you have declared the type numarr1 in the database, whereas the type selected_pkg.num_array is declared in a package.
Good summary here
I can't quite recreate the error you're getting; the anonymous block doesn't refer to in_feature_ids, and the package ought to only report that if it doesn't recognise it on compilation rather than at runtime - unless you're using dynamic SQL. Without being able to see the function body I'm not sure how that's happening.
But you can't use a PL/SQL-defined type in an SQL statement. At some point the table(in_feature_ids) will error; I'm getting an ORA-21700 when I tried it, which is a new one for me, I'd expect ORA-22905. Whatever the error, you have to use a type defined at schema level, not within the package, so this will work (skipping the spatial stuff for brevity):
CREATE TYPE num_array IS TABLE OF NUMBER;
/
CREATE OR REPLACE PACKAGE selected_pkg IS
FUNCTION get_selected_kml(
in_layer IN NUMBER,
in_id IN NUMBER,
in_feature_ids IN NUM_ARRAY,
in_lx IN NUMBER,
in_ly IN NUMBER,
in_ux IN NUMBER,
in_uy IN NUMBER
) RETURN CLOB;
END selected_pkg;
/
CREATE OR REPLACE PACKAGE BODY selected_pkg IS
FUNCTION get_selected_kml(
in_layer IN NUMBER,
in_id IN NUMBER,
in_feature_ids IN NUM_ARRAY,
in_lx IN NUMBER,
in_ly IN NUMBER,
in_ux IN NUMBER,
in_uy IN NUMBER
) RETURN CLOB IS
BEGIN
FOR i IN (select * from table(in_feature_ids)) LOOP
DBMS_OUTPUT.PUT_LINE(i.column_value);
END LOOP;
RETURN null;
END get_selected_kml;
END selected_pkg;
/
... and calling that also using the schema-level type:
set serveroutput on
declare
result CLOB;
myarray num_array := num_array(23466,13396,14596);
begin
result:=SELECTED_PKG.get_selected_kml(3, 19, myarray, 0.0,57.0,2.8,59);
end;
/
23466
13396
14596
PL/SQL procedure successfully completed.
Also note that you have to use exactly the same type, not just one that looks the same, as discussed in a recent question. You wouldn't be able to call your function with a variable of num_arr1 type, for example; they look the same on the surface but to Oracle they are different and incompatible.
function classes(o:integer): String;
var allclasses : array[1..7] of String;
begin
allclasses[1]:= 'class1';
allclasses[2]:= 'class2';
allclasses[3]:= 'class3';
allclasses[4]:= 'class4';
allclasses[5]:= 'class5';
allclasses[6]:= 'class6';
allclasses[7]:= 'class7';
classes := allclasses[o];
end;
Above you can see a function, which should receive an integer and give a result of string that was stored in array.
procedure loadthis(chosen : string);
var f: text;
i : integer;
begin
Assign(f, 'files\'+chosen+'.txt');
Reset(f);
ReadLn(f, i);
MyChar.clas := classes[i];
end;
When this procedure is called, it calls a "classes" function. Pleae note that Mychar ir a global variable.
begin
loadthis(FileName);
ReadLn;
Readln
end.
Ant this is the main program, which calls "loadthis" procedure.
I Have no idea whats wrong, but I am getting these errors:
Wrong amount of parameters specified
Illegal qualifier
Both errors come from this line:
MyChar.clas := classes[i];. I have really no idea what is wrong, maybe I can not call a function from a procedure ? Please help.
You're trying to access it as an array index, but it needs to be a function call:
MyChar.clas := classes(i); { note () instead of [] }
You should probably add some range checking, too. What happens if someone puts 20 in the text file? Your array only has items at indexes 1 through 7, so you'll get a runtime error when you call classes(20) with the out of range value.
(You could probably use a constant array for allclasses to lessen your code as well, but your instructor probably haven't gotten that far yet.)
Given your comment about not having an instructor, here's a suggestion about a better way to handle the function:
function classes(o:integer): String;
const
allclasses: array[1..7] of string = ('class1',
'class2',
'class3',
'class4',
'class5',
'class6',
'class7');
begin
{
Low() returns the lowest index of the array, and
High() returns the highest. The if statement makes sure
that o is between them. It is the range check I mentioned.
}
if (o >= Low(allclasses)) and (o <= High(allclasses)) then
classes := allclasses[o]
else
classes := '';
end;
I'm using PostgreSQL 9.1.3 and the following functions:
CREATE OR REPLACE FUNCTION cad(INOUT args text[], OUT retval int4) AS $cad$
BEGIN
retval := 0;
RAISE NOTICE 'cad: %', args;
END;
$cad$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION dodo(in_args text[]) RETURNS text[] AS $dodo$
DECLARE
_res text[];
_rv int4;
BEGIN
_res := in_args;
EXECUTE 'SELECT cad($1)' USING _res INTO _res, _rv;
RETURN _res;
END;
$dodo$ LANGUAGE plpgsql;
When I call cad directly, I get expected output:
psql$ select cad(ARRAY['Quiz']);
NOTICE: cad: {Quiz}
-[ RECORD 1 ]---
cad | ({Quiz},0)
Time: 0,319 ms
My expected result for the dodo(ARRAY['Quiz']) call is the input array without changes. But instead I receive the following error:
psql$ select dodo(ARRAY['Quiz']);
NOTICE: cad: {Quiz}CONTEXT: SQL statement "SELECT cad($1)"
PL/pgSQL function "dodo" line 8 at EXECUTE statement
ERROR: array value must start with "{" or dimension information
CONTEXT: PL/pgSQL function "dodo" line 8 at EXECUTE statement
What is wrong here?
P.S.: I have to use EXECUTE as function to call will vary, code simplified for the purpose of question.
You want something like:
EXECUTE 'SELECT * FROM cad($1)' USING _res INTO _res, _rv;
The return type isn't two columns of text[],int it's a record of (text[],int) which needs unwrapping.