I have the following code that doesn't work. It compiles, but when called with sysdate as the parm_value parameter it throws PLS-00307: too many declarations of 'P_UPSERT_SDE_DATA' match this call If I comment out the varchar2 entry, the overload works as expected with just date and number datatypes.
What is the best way to go about what I'm trying to do, which is accept parameters differing only in parm_values datatype(specifically date and varchar2)?
PROCEDURE P_Upsert_SDE_Data(parm_table_name GORSDAV.GORSDAV_TABLE_NAME%TYPE,
parm_attr_name GORSDAV.GORSDAV_ATTR_NAME%TYPE,
parm_key GORSDAV.GORSDAV_PK_PARENTTAB%TYPE,
parm_user_id GORSDAV.GORSDAV_USER_ID%TYPE,
parm_value VARCHAR2);
--
PROCEDURE P_Upsert_SDE_Data(parm_table_name GORSDAV.GORSDAV_TABLE_NAME%TYPE,
parm_attr_name GORSDAV.GORSDAV_ATTR_NAME%TYPE,
parm_key GORSDAV.GORSDAV_PK_PARENTTAB%TYPE,
parm_user_id GORSDAV.GORSDAV_USER_ID%TYPE,
parm_value NUMBER);
PROCEDURE P_Upsert_SDE_Data(parm_table_name GORSDAV.GORSDAV_TABLE_NAME%TYPE,
parm_attr_name GORSDAV.GORSDAV_ATTR_NAME%TYPE,
parm_key GORSDAV.GORSDAV_PK_PARENTTAB%TYPE,
parm_user_id GORSDAV.GORSDAV_USER_ID%TYPE,
parm_value DATE);
You can see this if one of the others arguments you pass is being implicitly converted; from the call you posted I suspect var_FRBGRNT_CODE is a different type and is being converted; e.g. that variable is a number and GORSDAV.GORSDAV_PK_PARENTTAB is a string.
From the documentation:
When trying to determine which subprogram was invoked, if the PL/SQL compiler implicitly converts one parameter to a matching type, then the compiler looks for other parameters that it can implicitly convert to matching types. If there is more than one match, then compile-time error PLS-00307 occurs, as in Example 8-34.
Implicit conversion of one of the other arguments makes it look for potential conversions of the others; it's only then that it sees the date and varchar2 versions, which can be implicitly converted to each other. If all of the arguments are of the same type as the table columns used for the formal parameter declarations then it won't look for implicit conversions, and won't be confused by the date/varchar2 versions.
Whenever I get this error, it seems to be that there's a mismatch between the spec declaration and the body declaration somewhere. Make sure each of your spec declarations match your body declarations exactly.
Related
It's said in database PL/SQL language reference in topic 10.1 What is a package? that:
If the public items include cursors or subprograms, then the package must also have a body. The body must define queries for public cursors and code for public subprograms.
I've tested cursor package spec without body and it worked fine (in DB version 19c):
create or replace package some_pak is
cursor c is select * from employees where employee_id <102;
end;
begin
for i in some_pak.c
loop
dbms_output.put_line(i.employee_id|| ' '||i.first_name||' '|| i.salary);
end loop;
end;
Result:
100 Steven 24000
101 Neena 17000
What am I doing or understanding wrong?
I am preparing for 1z0-149 exam and want to know accurate information.
It is a bit misleading, but if you take the previous paragraph from the documentation into account:
A package always has a specification, which declares the public items that can be referenced from outside the package.
... and stress the declares in that, then it sort of makes sense; if you only declare a cursor, rather than declaring and defining as your example does, then you do need a body too. For example, your specification could do:
create or replace package some_pak is
cursor c return dual%rowtype;
end;
/
But if you tried to reference that cursor you'd get "ORA-04067: not executed, package body ... does not exist".
You would then need to define the cursor in the package body, e.g.:
create or replace package body some_pak is
cursor c return dual%rowtype is
select * from dual;
end;
/
Notice that you need to declare the return type, in both the specification and body. In your original version that isn't necessary, but here the declaration still has to tell callers the structure of the data the cursor will return. That is the API contract, if you like.
Declaring and defining the cursor separately means you can redefine the cursor by recompiling just the package body (as long as the projection remains the same, of course), avoiding invalidating anything that refers to it - as would happen if you recompiled the specification.
db<>fiddle
Splitting the declaration and definition would also allow you to hide (albeit not very securely) the actual cursor query by wrapping the package body, without wrapping the specification. Whether that would ever be useful is another matter, but thought it might be worth mentioning anyway.
Consider the following example:
create or replace function f(n integer) return integer as
begin
return n;
end;
/
begin
dbms_output.put_line(f(3.8));
end;
/
3.8
PL/SQL procedure successfully completed.
This makes no sense to me. Obviously, PL/SQL simply ignores the integer specification, both on entering the function and on exiting it. Is this simply a bug? Is it a design choice, made deliberately by the language developers?
Here is why I find this confusing. Compare to the following example:
declare
x integer;
begin
x := 3.8;
dbms_output.put_line(x);
end;
/
4
PL/SQL procedure successfully completed.
In this example, the data type specification is complied with. PL/SQL doesn't throw an error, but at least it performs an implicit coercion and it does not violate the data type declared for x - the variable stores the value 4, an integer, not 3.8.
So, how does PL/SQL do the function call thing in the first example? As far as I understand (never having been trained formally in computing), whenever the compiler or interpreter finds a function call it creates a stack frame, with variables for the arguments passed to the function and for the return value to come back from the function. Aren't these variables, when the stack frame is created, supposed to be the same data type as specified in the function declaration? If the stack frame has a field of integer data type for the argument 3.8, how come that is not coerced to 4 before it is even stored in the corresponding variable? And the same thing for the return value: if the function returns 3.8 but the caller expects an integer (and therefore the corresponding variable in the stack frame should be integer), how is it able to accept the return value 3.8?
And, most disturbing - why is this behavior different from the behavior when explicitly declared variables are involved (as in my second example)?
Thank you for sharing your thoughts!
The answer is found in the documentation for Oracle Database (to which your question has absolutely no relation whatsoever).
Firstly, INTEGER in the said database is a synonym for NUMBER(38). Upon assignment to a NUMBER(38) variable x, as in your second example, according to the assignment rules the NUMBER (with arbitrary precision) literal 3.8 is rounded.
In your first example though no assignment happens because IN parameters to PL/SQL subprograms are passed by reference and the same reference (to the NUMBER value 3.8) is returned.
I'm trying to understand the definition of the keyword TYPE in pascal. I understand that typedef in C just gives a new name to the type (alasing). But as I understand TYPE in Pascal does not work that way. It will create a new unique type.
I was trying to search and create a simple example which shows the mechanism of TYPE. I tried to create an example which creates some types and a function. After that, it pass each time one of the types to that function. It should fail because the function should get only one type, which proves that those types are not just aliasing. Due to my lack of knowledge of Pascal syntax, I failed each time.
Could you share a simple short program which proves the power of TYPE?
EDIT:
I have created the following example:
program Check;
TYPE
Meters = Real; Seconds = Real;
VAR
m: Meters; s: Seconds;
Procedure PRINT_SEC(s: Seconds);
Begin
WriteLn(s, ' sec');
end;
Begin
PRINT_SEC(s);
PRINT_SEC(m);
end.
Output:
0.0000000000000000E+000 sec
0.0000000000000000E+000 sec
But why it does not fail? I passed m which has type Meters no? Also, How can I initialize those variables?
First a minor point, in Pascal, the keyword TYPE does not create types. The keyword TYPE must occur before type definitions, but it is the type definitions which MAY create types. Not all type definitions create types.
The Pascal Standard says the following:
A type-definition shall introduce an identifier to denote a type.
which means a type definition introduces (i.e. creates or redefines) an identifier which denotes (i.e. is an alias for) a type.
The Pascal Standard defines a type definition as:
type-definition = identifier '=' type-denoter
type-denoter = type-identifier | new-type
new-type = new-ordinal-type | new-structured-type | new-pointer-type
Which means that a type definition is a identifier, followed by the equal side, followed by a type denoter. A type denoter is either a type identifier or a new type.
So a type identifier introduces an identifier that denotes (i.e. is an alias for) either another type identifier or a new-type. A type is created only in the case where the type denoter is a new type.
So in your example:
TYPE
Meters = Real; Seconds = Real;
The type denoter in both type definitions is the type identifier Real, so Meters and Seconds are both aliases for Real.
Yes, in Pascal, Real is not a Type, it is a built-in type identifier for the real type.
The Pascal Standard says
The required type identifier real shall denote the real-type.
So real is actually a type identifier and not a type. It is as if, there is an invisible type definition.
TYPE
Real = real-type;
where real-type is the actual real type.
Variables like m and s are defined by a type. In this case both types origins from a real type. That is called a type alias. They are compatible, both as a type and by assignment.
If you want a distinct type (in Freepascal and delphi), define:
type Seconds = type real;
That would have made the print procedure to only accept the Seconds type argument. Note that variables of Seconds and Meters declared as distinct types still are assignment compatible.
To initialize variables, just assign a value:
s := 42.0;
Note: most types are named starting with a T. Like TSeconds. Just to distinct them from variables. It is a common convention (in pascal).
I've looked up questions here as well as looking online and watching videos but I'm still confused exactly what IN, OUT is. The reason I'm asking is because I'm writing a procedure that will log an error based on the IN parameters in other procedures,
Cheers!
The Oracle documentation here does a good job of explaining:
The mode of a parameter indicates whether the parameter passes data to a procedure (IN), returns data from a procedure (OUT), or can do both (IN OUT).
And about OUT parameters specifically:
... you cannot use it to pass a value to the procedure. Nor can you read its value inside the procedure, even after a value has been assigned to it.
EDIT
Actually, though the information provided above is valid, I linked to a poor resource (SQL*Module for Ada Programmer's Guide).
A much better and more complete resource to better understand the 3 modes can be found here: Table 8-1 PL/SQL Subprogram Parameter Modes.
IN mode:
Default mode
Passes a value to the subprogram.
Formal parameter acts like a constant: When the subprogram begins, its value is that of either its actual parameter or default value, and the subprogram cannot change this value.
Actual parameter can be a constant, initialized variable, literal, or expression.
Actual parameter is passed by reference.
OUT mode:
Must be specified.
Returns a value to the invoker.
Formal parameter is initialized to the default value of its type. The default value of the type is NULL except for a record type with a non-NULL default value.
When the subprogram begins, the formal parameter has its initial value regardless of the value of its actual parameter. Oracle recommends that the subprogram assign a value to the formal parameter.
If the default value of the formal parameter type is NULL, then the actual parameter must be a variable whose data type is not defined as NOT NULL.
By default, actual parameter is passed by value; if you specify NOCOPY, it might be passed by reference.
IN OUT mode:
Must be specified.
Passes an initial value to the subprogram and returns an updated value to the invoker.
Formal parameter acts like an initialized variable: When the subprogram begins, its value is that of its actual parameter. Oracle recommends that the subprogram update its value.
Actual parameter must be a variable (typically, it is a string buffer or numeric accumulator).
By default, actual parameter is passed by value (in both directions); if you specify NOCOPY, it might be passed by reference.
I have two procedures in oracle which define a parameter differently, even though the underlying data is the same. In code we represent it as an integer.
procedure GetByNum(..., vRegionID in number, ...)
procedure GetByInt(...., vRegionID in integer, ...)
In java we always define the field as an Integer (and in db too)
public Integer getRegionID() {
return 100;
}
Is there a way to pass this (in java) integer to both procedures using the same OracleTypes.*? We use a custom base wrapper around org.springframework.jdbc.object.StoredProcedure to call both of them. Modifying the stored procedures is not allowed for right now, so all that is left is fixing up the java.
Can you pass getRegionID as OracleTypes.NUMBER to the getByInt proc?
Can you pass it as OracleTypes.INTEGER to getByNum, which expects in number?
Another, magical way?
This might seem simple, but I assume i'm not allowed to mangle parameters like this. I hope thats not true!
Edit:
Using OracleTypes.INTEGER to pass to procedure ... (param in number) worked
Don't know if this is just environment specific though
Hoping for a definitive answer, so I can have them start using the new wrapper without worrying about an unforeseen issue.
INTEGER is a ANSI standard type, which Oracle describes as a subtype of NUMBER as NUMBER(38). Therefore, anywhere that takes an INTEGER as a parameter should also accept a NUMBER so long as there are no fractional parts.