I need to execute the stored procedure ctx_ddl.sync_index on an Oracle database. In a query program this works;
EXECUTE ctx_ddl.sync_index('MyIndex');
When I try to run in dapper with the following;
using var connect = new OracleConnection(conString);
var results = connect.Execute("ctx_ddl.sync_index", "MyIndex", commandType: CommandType.StoredProcedure);
It gives me the following error:
ORA-06550: line 1, column 7: PLS-00306: wrong number or types of
arguments in call to 'SYNC_INDEX'
What am I doing wrong?
Instead of passing "MyIndex" as a string, try this:
using var connect = new OracleConnection(conString);
var p = new OracleDynamicParameters();
p.Add("THENAMEOFYOURPARAMETER", dbType: OracleDbType.THETYPEOFYOURPARAMETER, direction: ParameterDirection.Input);
var results = connect.Execute("ctx_ddl.sync_index", param:p, commandType: CommandType.StoredProcedure);
Related
I have an application that was calling an Oracle stored procedure by passing a string with the parameters in the string, i.e.
string Query = "CALL MySP(" + Variable1 + ", " + Variable2 + ")";
This worked, or at least the last time I tried it it worked.
I need to rewrite the query using DynamicParameters.
However when I do an Execute(Query, Parms, commandType: CommandType.StoredProcedure), I get errors.
Here is an example of what I tried:
using(OracleConnection conn = new OracleConnection(myConnectionString)
{
string Query = "CALL MySP(:Var1, :Var2)";
DynamicParameters parms = new DynamicParameters();
parms.Add("Var1", Variable1, DbType.String, ParameterDirection.Input);
parms.Add("Var2", Variable2, DbType.String, ParameterDirection.Input);
var results = connection.Execute(Query, parms, commandType: CommandType.StoredProcedure);
}
When I run it with the above query, I get this error:
ORA-06550: line 1, column 12:
PLS-00103: Encountered the symbol "MySP" when expecting one of the following:
:= . ( # % ;
The symbol ":=" was substituted for "MySP" to continue.
If I remove the CALL statement:
Query = "MySP(:Var1, :Var2);
then I get this error message:
ORA-01008: not all variables bound
Can anyone see what I am doing wrong?
The problem is that I keep looking on the C# and Dapper side of the equation. Our Oracle DBA set the permissions on the Stored Procedure and it ran.
However I could not tell Dapper that the CommandType was Stored Procedure and my string to call the stored procedure had to look like:
string Query = "CALL MySP(:Var1, :Var2)";
If I removed "CALL" I got an error. If I added "commandType: CommandType.StoredProcedure" to the Dapper Execute function I got a binding error.
Our DBA said he was going to look more into it to see what he can find out on his end so that we can call the stored procedure using the CommandType.StoreProcedure parameter in the Execute function.
I am trying to pass a parameter to a stored procedure using the Oracle.EntityFrameworkCore package like this:
DataTable table = new DataTable();
table.Columns.Add("keyColumn", typeof(string));
table.Columns.Add("valueColumn", typeof(string));
var row = table.NewRow();
row.ItemArray = new object[]
{
entry.KeyColumn,
entry.ValueColumn
};
table.Rows.Add(row);
var parameter = new OracleParameter("entries",table);
parameter.UdtTypeName = "entry_type_list";
return context.Database.ExecuteSqlCommandAsync(
new RawSqlString( #"EXEC set_entry_list (:entries)" ),
parameter);
The stored procedure and type are defined like this:
CREATE OR REPLACE TYPE entry_type AS OBJECT
(
"keyColumn" NVARCHAR2(3),
"valueColumn" NVARCHAR2(3)
);
CREATE OR REPLACE TYPE entry_type_list AS TABLE OF entry_type;
CREATE OR REPLACE PROCEDURE set_entry_list (entries entry_type_list) AS
BEGIN
REM Doing stuff
END;
But I get an error:
System.ArgumentException: Value does not fall within the expected range.
at Oracle.ManagedDataAccess.Client.OracleParameter..ctor(String parameterName, Object obj)
The only sources for this is an answer how to do this with SQL Server, but no answer for Oracle with EFCore. The issue here seems to be that Oracle only accepts an OracleParameter whereas others use SqlParameter.
If I use the SqlParameter type like this:
var parameter = new SqlParameter("entries", SqlDbType.Structured);
parameter.TypeName = "entry_type_list";
parameter.Value = table;
I get this error:
System.InvalidCastException: Unable to cast object of type'System.Data.SqlClient.SqlParameter' to type 'Oracle.ManagedDataAccess.Client.OracleParameter'.
I also did try setting parameter.OracleDbType to different values like Blob, RefCursor, Clob or XmlType, setting parameter.DbType to Object or setting CollectionType to PLSQLAssociativeArray with no success. Also passing a list or an array of objects instead of a table did not succeed.
I currently have no idea what else I could try.
Any method to pass a big amount of entities to a stored procedure in a performant way would help. I use them with the merge-command so I need to be able to convert those parameters to a table.
I now found a solution using a temporary table and using this one as my input parameter.
As I can't pass a complete table, but an array of simple objects I have to fill this table by passing one array for each column:
var keyColumn = new OracleParameter( "keyColumn", OracleDbType.Decimal );
keyColumn.Value = values.Select( c => c.KeyColumn).ToArray();
var valueColumn = new OracleParameter( "valueColumn", OracleDbType.Decimal );
valueColumn = values.Select( c => c.ValueColumn).ToArray();
using ( var transaction = this.dbContext.Database.BeginTransaction( IsolationLevel.ReadCommitted) )
{
var connection = this.dbContext.Database.GetDbConnection() as OracleConnection;
OracleCommand cmd = connection.CreateCommand();
cmd.CommandText = #"
INSERT INTO TMP_TABLE
(
""keyColumn"",
""valueColumn""
)
VALUES (
:keyColumn,
:valueColumn)";
cmd.Parameters.Add( keyColumn );
cmd.Parameters.Add( valueColumn );
cmd.ArrayBindCount = values.Length;
var insertCount = await cmd.ExecuteNonQueryAsync();
cmd = connection.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.stored_procedure";
var result = await cmd.ExecuteNonQueryAsync();
transaction.Commit();
}
I created the temp table like this:
CREATE
GLOBAL TEMPORARY TABLE "dbo"."TMP_TABLE"
ON COMMIT DELETE ROWS
AS SELECT * FROM "dbo"."REAL_TABLE" WHERE 0=1;
And changed my stored procedure to use it:
CREATE OR REPLACE PROCEDURE stored_procedure AS
BEGIN
REM use the "dbo"."TMP_TABLE"
END;
This answer helped me with the approach of bulk inserting with one array per column. The thread also contains some further discussion about the topic and a more generic approach.
I've got a table in my Google Cloud SQL database with an auto-incrementing column.
How do I execute an INSERT query via google-apps-script/JDBC and get back the value for the newly incremented column?
For example, my column is named ticket_id. I want to INSERT and have the new ticket_id value be returned in the result set.
In other words, if I have the following structure, what would I need to modify or how, so that I can do something like rs = stmt.getGeneratedKeys();
var conn = Jdbc.getCloudSqlConnection("jdbc:google:rdbms:.......
var stmt = conn.createStatement();
//build my INSERT sql statement
var sql = "insert into ......
var rs = stmt.executeUpdate(sql);
I see that there is a JDBC statement class with a member called RETURN_GENERATED_KEYS but I have so far not been smart enough to figure out how to properly manipulate that and get what I need. Is RETURN_GENERATED_KEYS a constant, is it an attribute, or how can I make use of it?
It seems like the documentation with the Apps Script JDBC service is a bit lacking. I've created an internal task item for that. Thankfully, Apps Script JDBC API follows the Java JDBC API pretty closely. The key is to get the result set back using the stmt.getGeneratedKeys() call.
I built a sample table using the animals example from the MySQL docs and this sample below works nicely against that and logs the next incremented ID.
function foo() {
var conn = Jdbc.getCloudSqlConnection("jdbc:google:rdbms://<instance>/<db>");
var stmt = conn.createStatement();
var sql = "INSERT INTO animals (name) VALUES ('dog')";
var count = stmt.executeUpdate(sql,1)//pass in any int for auto inc IDs back
var rs = stmt.getGeneratedKeys();
//if you are only expecting one row back, no need for while loop
// just do rs.next();
while(rs.next()) {
Logger.log(rs.getString(1));
}
rs.close();
stmt.close();
conn.close();
}
I'm using a product that provides a database API based on Oracle functions and I'm able to call functions via ODP.NET in general. However, I can't figure out, how to call a function that includes a Ref Cursor as Out-parameter. All the samples I found so far either call a procedure with Out-parameter or a function with the Ref Cursor as return value. I tried to define the parameters similiarly, but keep getting the error that the wrong number or type of parameters is supplied.
Here is the function header (obviously obfuscated):
FUNCTION GetXYZ(
uniqueId IN somepackage.Number_Type,
resultItems OUT somepackage.Ref_Type)
RETURN somepackage.Error_Type;
These are the type definitions in "somepackage":
SUBTYPE Number_Type IS NUMBER(13);
TYPE Ref_Type IS REF CURSOR;
SUBTYPE Error_Type IS NUMBER;
And this is the code that I have tried:
string sql = "otherpackage.GetXYZ";
var getXYZCmd = OracleCommand oracleConnection.CreateCommand(sql);
getXYZCmd.CommandType = CommandType.StoredProcedure;
getXYZCmd.Parameters.Add("uniqueId", OracleDbType.Int32).Value = uniqueExplosionId;
getXYZCmd.Parameters.Add("resultItems", OracleDbType.RefCursor).Direction = ParameterDirection.Output;
getXYZCmd.Parameters.Add("return_value", OracleDbType.Int32).Direction = ParameterDirection.ReturnValue;
The I tried the following different ways to call the function (of course only one at a time):
var result = getXYZCmd.ExecuteNonQuery();
var reader = getXYZCmd.ExecuteReader();
var scalarResult = getXYZCmd.ExecuteScalar();
But each of them fails with the error message:
Oracle.DataAccess.Client.OracleException: ORA-06550: line 1, column 15:
PLS-00306: wrong number or types of arguments in call to 'GETXYZ'
ORA-06550: line 1, column 15:
PLS-00306: wrong number or types of arguments in call to 'GETXYZ'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored.
So is it generally possible to call a function with a Ref Cursor as Out-parameter from C# with ODP.NET? I can call a function with the same structure with a Varchar2-Out-parameter instead of the Ref Cursor without problems...
Btw, I'm using ODP.NET version 2.112.2.0 from C#.NET 3.5 in Visual Studio 2008.
Thanks in advance for your help!
You sure can. There are a few gotchas to be wary of but here is a test case
create or replace function testodpRefCursor(
uniqueId IN NUMBER
,resultItems OUT NOCOPY SYS_REFCURSOR) RETURN NUMBER
IS
BEGIN
OPEN resultItems for select level from dual connect by level < uniqueId ;
return 1;
END testodpRefCursor;
I have found that
functions likes to have the
ReturnValue as THE FIRST param
in the collection
BindByName is by default FALSE, so it defaults to BIND BY POSITION
Otherwise it is quite straight forward:
OracleCommand cmd = new OracleCommand("TESTODPREFCURSOR", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.BindByName = true;
// Bind
OracleParameter oparam = cmd.Parameters.Add("ReturnValue", OracleDbType.Int64);
oparam.Direction = ParameterDirection.ReturnValue ;
OracleParameter oparam0 = cmd.Parameters.Add("uniqueId", OracleDbType.Int64);
oparam0.Value = 5 ;
oparam0.Direction = ParameterDirection.Input;
OracleParameter oparam1 = cmd.Parameters.Add("resultItems", OracleDbType.RefCursor);
oparam1.Direction = ParameterDirection.Output;
// Execute command
OracleDataReader reader;
try
{
reader = cmd.ExecuteReader();
while(reader.Read() ){
Console.WriteLine("level: {0}", reader.GetDecimal(0));
}
} ...
Now for more samples go to your Oracle Home directory and look # the Ref cursor samples in ODP.NET
for instance:
%oracle client home%\odp.net\samples\4\RefCursor
hth
I'm trying to map OleDb parameters to an Oracle Function. I was able to do this using the System.Data.Oracle namespace but then found that this is depricated, so I thought i would re-write it as OldDb to avoid installing the Oracle Provider.
I have defined the following oracle function as an example:
create function GetImagePath (AIRSNumber in number)
return varchar2
is
begin
return '\\aiimg524\images\Ofndrtrk\2010\01\0kvrv1p000lcs74j';
end;
and I'm calling it using the following code:
using (var command = new OleDbCommand())
{
command.Connection = con;
command.CommandText = ConfigurationManager.AppSettings[OTRAK_PHOTO_FUNC];
command.CommandType = CommandType.StoredProcedure;
string parm = ConfigurationManager.AppSettings[OTRAK_PHOTO_PARM];
command.Parameters.Add(parm, OleDbType.Decimal); // maps to oracle Number
command.Parameters[parm].Direction = ParameterDirection.Input;
command.Parameters[parm].Value = airsNumber;
command.Parameters.Add(RETURN_VALUE, OleDbType.Variant); // maps to Oracle varchar2
command.Parameters[RETURN_VALUE].Direction = ParameterDirection.ReturnValue;
try
{
con.Open();
command.ExecuteNonQuery();
path = command.Parameters[RETURN_VALUE].Value.ToString();
}
I tried a bunch of different OleDB types for the parameter and the return value. the current attempt is from a mapping table i found on the web that said number = decimal and varchar2 = variant. I'm about to try every permutation of types in the enum and wanted to ask for help. the not so useful error message i get is:
[System.Data.OleDb.OleDbException] = {"ORA-06550: line 1, column 7:\nPLS-00306: wrong number or types of arguments in call to 'GETIMAGEPATH'\nORA-06550: line 1, column 7:\nPL/SQL: Statement ignored"}
This actually had nothing to do with the type of the parameters but the order. Using the OleDb provider for Oracle does not respect the names of the parameters in the parameter collection but rather the order that the parameters are added. Wwhen calling an oracle function, the return value is a free parameter that must be declared first. by adding my return value parameter and then the actual function parameter things started working.
using the command.Parameters.AddWithValue(parm, value) also simplifies things a bit.