I'm searching for a way to call an anonymous PLsql block through SOCI. The data transfer takes place through a refcursor that was previously created as a variable in the script:
variable rc refcursor
declare
v_obj_id number(4,0) := 1;
v_obj_def varchar(30);
v_obj_type number := 1;
begin
open :rc for
select v_obj_id, OBJ_DEF_ID
from MY_OBJECT_DEFS
where OBJECT_TYPE = v_obj_type;
end;
I need to read the refcursor from my application to retrieve the data. I tried to execute the above through a soci::statement but it gives me the error: ORA-24333: zero iteration count. The PLsql script works fine when executed in SqlPlus.
How can I make the connection between the statement and the
refcursor rc? Should I use some other SOCI construct (other than statement) for this purpose?
I understand there are two instructions in the above
script; (i. the refcursor creation, ii. the anonymous PLsql block
itself). I'm not sure whether its possible to call multiple
instructions in a single SOCI statement. Can this be confirmed?
Following is the what I tried. The sSQL contains the above PLsql script:
dbConn.open("...");
int iObjId;
std::string iObjDefId;
soci::indicator ind_iObjId = soci::i_ok,
ind_iObjDefId = soci::i_ok;
soci::statement stmt(dbConn);
stmt.alloc();
stmt.prepare(sSQL);
stmt.exchange(soci::into(iObjId, ind_iObjId));
stmt.exchange(soci::into(iObjDefId, ind_iObjDefId));
stmt.define_and_bind();
stmt.execute(false);
while (stmt.fetch())
{
if (soci::i_ok == ind_iObjId)
std::cout << "Obj ID: " << iObjId << std::endl;
if (soci::i_ok == ind_iObjDefId)
std::cout << "Obj Def ID: " << iObjDefId << std::endl;
}
EDIT: I'm using Oracle 11g
The statement variable rc refcursor is neither SQL nor PL/SQL but part of Oracle's SQL*Plus command-line utility and compatible third party products. I don't know C++ but presumably you would need to define a ref cursor object in the host program.
If this is not feasible, and you are on Oracle 12.1 or later, it's possible that you could use an implicit result set construction, along the lines of
declare
rc sys_refcursor;
begin
open rc for select * from dual;
dbms_sql.return_result(rc);
end;
as discussed in Is it possible to output a SELECT state from a PL/SQL block?
Related
I'm writing a simple application to correct a certain byte in a binary blob that's stored in a database. I use OTL to connect my C++ program to the database.
Reading the data and manipulation works fine, but when I want to write it back to the database, the program dies.
The code:
try
{
std::string updateQuery = "UPDATE tbtptemplates "
" SET tptemplate=:tp<BLOB> "
"WHERE afisid=:aid<INT>";
otl_long_string blob(&stp(), theSizeOfBlob);
blob.set_len(theSizeOfBlob);
otl_stream str(1, updateQuery.c_str(), theDbConnection);
str.set_lob_stream_mode(true);
str.set_commit(0);
otl_lob_stream lob;
lob.set_len(theSizeOfBlob);
str << lob;
str << theCurrentAfisId;
lob << blob;
lob.close();
}
catch (const otl_exception &oe)
{
std::cerr << oe.msg;
throw std::runtime_error("could not write to database");
}
I spied at Sourceforge for an example.
I create an otl_long_string that takes the data.
Then I create an otl_stream and bring together the SQL statement and the database connection.
I use an otl_lob_stream to provide the binary data to the otl_stream.
Streaming of the blob data to the otl_stream works fine. But when I hand over the id to the stream, the process dies.
This is the killer instruction:
str << theCurrentAfisId;
it is a simple int-variable that I hand over.
The effect is very funny: the process doesn't die completely, only the call stack below the current method is gone.
This is the call stack before the statement:
Thread #1 [fix_stp] 10307 [core: 7] (Suspended : Breakpoint)
Drm::TPFixer::DatabaseConnector::writeDB() at DatabaseConnector.cpp:224 0x40b565
Drm::TPFixer::DatabaseConnector::fixImpressiontype() at DatabaseConnector.cpp:139 0x40b516
Drm::TPFixer::SuperTemplateManager::fixImpressiontype() at SuperTemplateManager.cpp:67 0x4074e3
main() at main.cpp:51 0x40477b
and this is the complete stack in the very moment after the instruction:
Thread #1 [fix_stp] 10307 [core: 7] (Running : Step)
Drm::TPFixer::DatabaseConnector::writeDB() at DatabaseConnector.cpp:240 0x40b6b2
I tried to debug the otl. It seems that the otl finds that it's got all required parameters and starts to run. In that moment it crashes.
The environment that I use:
- OTL 4.0
- CentOS Linux, kernel 3.10.0-327.22.2.el7.x86_64
- gcc-Version 4.8.5
- Oracle 11g
Does anybody have an idea?
First of all, thank you for read this question and apologize for my poor english.
I'm now converting my DB from SQL-Server to MariaDB. I installed MySQL ODBC driver and added 'system DSN'. ( C:\Windows\SysWOW64\odbcad32.exe )
The problem occured at executing a stored procedure.
When I created an procedure in SQLyog, There was no error and execution also runs fine. But When I execute stored procedure in my c++ application syntax error occurs.
Database [MySQL][ODBC 5.3(w) Driver][mysqld-5.5.5-10.0.20-MariaDB]You
have an error in your SQL syntax; check the manual that corresponds to
your MariaDB server version for the right syntax to use near
'get_bookProperty ?' at line 1
My C++ code is...
bool LoadbookProperty::OnExecute(db::IDbProcesser* dbProcesser)
{
const char* bookName = m_bookName.c_str();
dbProcesser->BindParams(bookName);
if (!dbProcesser->Execute("get_bookProperty"))
return false;
char type[PROPERTY_NAME_LEN];
char value[PROPERTY_VALUE_LEN];
dbProcesser->BindCols(type, value);
dbProcesser->FetchWith([this, &type, &value]()
{
m_properties.push_back(std::make_pair(type, value));
});
return true;
}
And my procedure is...
USE bookInfoDB;
-- GetbookProperty
DELIMITER ;;
CREATE PROCEDURE get_bookProperty (
IN pi_bookName VARCHAR(32)
)
this_proc:BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
END;
SELECT bookName, bookPrice FROM bookProperty WHERE bookName = pi_bookName;
END ;;
DELIMITER ;
I really don't know what's the matter. Please help me.
Solved it! The cause is EXECUTION function. When my team used SQL Server, EXECUTION function's string combination was EXEC and no bracket.
(As you all know, SQL Server execute procedure by 'EXEC sp_name arg1 arg2 ...')
But MySQL(and also MariaDB)'s procedure execution syntax is 'CALL sp_name (arg1, arg2 ...). Our programmer have changed EXECTION function's string combination. And? It works perfectly!
Ich have to shuffle a lot of data data from an application into an ORACLE 11g database. To execute SQL from within C++ I use to employ the following scheme, using bare SQL together with the Poco::Data framework and an ODBC connection to my database:
// get session from ODBC session pool
Session session = moc::SessionPoolMOC::get();
// prepare a statement as QString
QString q = ("INSERT INTO MOC_DATA_CONCENTRATOR"
"("
"EQUIPMENT_INSTANCE_ID, "
"STATION_NAME, "
"DATA_SRC, "
"ORG_ID)"
"values (%1, '%2', '%3', %4);"
);
/*
there would be also prepared statements, but I think that is not the
topic now...
*/
// set parameters within string from passed object 'info'
q = q.arg(info.equipment_instance_id); /* 1 */
q = q.arg(info.station_name.toUtf8().constData()); /* 2 */
q = q.arg(info.data_src.toUtf8().constData()); /* 3 */
q = q.arg(info.org_id); /* 4 */
// prepare statement
Statement query(session);
query << q.toUtf8().constData();
try
{
// execute query
query.execute();
}
catch (...)
{
...
}
I'm aware, that this is a very low level approach, but it worked really fine for all situations I encountered so far...
The problem is, that I have now a lot of data (about 400,000 records) to fill into one table. Data is available as different C++ objects, stored in a Qt-List. My naive approach is to call this code sequence for each object to insert. This works fine, but turns out to be quite slow (taking about 15 minutes or so). I asked around, and was told to better use a so called "bulk insert".
It seems, however, that this is more related to PLSQL - at least I have no Idea how to use that from the same context or in a similar way as shown in my example. I'm neither ORACLE expert nor administrator and usage of the database is only a side problem within my project. What would you recommend?
Many thanks,
Michael
I am using the SOCI library to programmatically interact with a SQL Server DB via ODBC. I am having trouble getting values via output parameters in stored procedures. Some code (modeled after SOCI documentation)...
/*
* create procedure
* Dan.soci_proc #id int, #name varchar(32) output
* as begin
* set nocount on;
* select #name = name from Dan.soci_test where id = #id
* end
*/
std::string sql = "Dan.soci_proc :id, :name";
std::string name;
int proc_ndx = 1;
soci::procedure proc = (my_soci.get_session().prepare << sql, soci::use(proc_ndx),
soci::use(name));
std::cout << "\nAttempting to execute stored procedure " << size << " times."
<< std::endl;
for (; proc_ndx < adj_size; ++proc_ndx) {
try {
proc.execute();
while (proc.fetch())
std::cout << "Fetched: " << name << std::endl;
} catch (const soci::odbc_soci_error& e) {
std::cerr << "Error executing stored procedure." << std::endl;
std::cerr << e.what() << std::endl;
std::cerr << e.odbc_error_message() << std::endl;
return;
}
}
My code does not throw any errors or exceptions, but nothing is fetched, either. I have tried calling it many different ways (normal exec syntax, ODBC call syntax, etc.), but nothing seems to work. I'm wondering if the error goes back to this from here...
If an input/output parameter is omitted, or if a literal is supplied
for the parameter, the driver discards the output value.
Unfortunately, SOCI doesn't really seem to support parameter markers, at least as far as I can tell.
I have made the code work by using this kind of syntax...
std::string sql = "declare #name varchar(32); "
"exec Dan.soci_proc :id, #name = #name output; select #name";
...
soci::procedure proc = (my_soci.get_session().prepare << sql, soci::use(proc_ndx),
soci::into(name));
But that isn't ideal, for reasons that I think are obvious.
Has anyone out there used SOCI and have some input into what I need to do differently to get this to work?
EDIT: I received the following response from the soci-users forum/mailing list...
I haven't used SOCI for a couple of years but I did successfully get
stored procedures to work with ODBC on SQL Server, end of 2011, soci
version 2 (I think).
I remember that to do it I had to use statement rather than procedure.
I had to amend the backend to call SQLMoreResults in a loop.
Output parameters I think were not supported in SOCI and I handled
them direct with ODBC over which I also had a C++ layer.
However, since I'm a recent college grad (just started my first job this month!) and relatively inexperienced with C++ and database applications, I would really appreciate as much help as people are able to offer! For instance, regarding the above response - the last two points are not exactly clear to me.
The problems is that I was using the SOCI ODBC, which apparently does not support stored procedures.
SOCI Existing Backends
SOCI ODBC Backend - Stored Procedures
In the months since I initially asked this question, I have implemented my own SOCI backend, for which stored procedures work perfectly!
I am writing a c++ code to eval a tcl proc. i have two TCL files. any of two can have the tcl proc
say file1.tcl has tcl proc mytest.
file2.tcl is sourcing file1.tcl so if i do [info procs mytest] in file2.tcl , i am able to get the tcl proc name mytest.
But in the c++ when I try to eval the proc, it says invalid command mytest.
However if I write proc mytest in file2.tcl ,it works.
file1.tcl is user input and file2.tcl is default input. only in case proc is missing in file1.tcl, I will call file2.tcl proc.
Please suggest how can i get it work.
Thanks
Ruchi
Your question isn't entirely clear, but it seems that you have two files, one of which you control (and which provides default implementations of some Tcl commands?) and the other of which is controlled by your users (so they can override things?). You then want to load these into a Tcl interpreter context so that you can call things from C++? I'll assume that's what's going on.
Firstly, from the Tcl perspective you do this by getting the context (the Tcl_Interp*) to source the file with the default implementations. (There are other ways to do it, but that's definitely easiest.) Only after that is done would you source the file with the user definitions, and you would only do the call in to kick things off after everything has finished sourcing in correctly. So that's what we're aiming for.
From C++, the main things to note are that Tcl_EvalFile() is the equivalent of source in Tcl (just as Tcl_Eval() is the equivalent of eval), and that you have to take care with checking for errors; Tcl doesn't map its exception system to C++ exceptions at all, so check those return codes.
Tcl_Interp *interp = Tcl_CreateInterp();
if (Tcl_EvalFile(interp, "file2.tcl") != TCL_OK) {
const char *errorMessage = Tcl_GetString(Tcl_GetObjResult(interp));
// Deal with error
cerr << "Problem in library: " << errorMessage << "\n";
exit(1);
}
if (Tcl_EvalFile(interp, "file1.tcl") != TCL_OK) {
const char *errorMessage = Tcl_GetString(Tcl_GetObjResult(interp));
cerr << "Problem in user code: " << errorMessage << "\n";
exit(1);
}
// Everything ready
if (Tcl_Eval(interp, "[the_party get] start") != TCL_OK) {
// Something still went wrong...