MariaDB, Syntax error when executing stored procedure in c++ program - c++

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!

Related

OCI 19c - Parsing a PL/SQL statement that contains SELECT-FROM-INTO statements

I have a need to parse queries using OCI. For the most part - it is working. However I ran into a problem today that I am not able to figure out. For the time being - it looks like it's either a bug in OCI (currently using 19c 32bit) - or for some reason these kinds of statements are being ignored.
Whenever a SQL query contains SELECT-FROM-INTO statements - the entire query is ignored when OCIStmtExecute is invoked with OCI_PARSE_ONLY. It doesn't matter if there are invalid entity names / syntax in the query. I tried to find some additional info online regarding this particular situation - but I found nothing of use.
The query in question is below (the query is intentionally using garbage entity names to illustrate the strange behavior)
BEGIN
SELECT ColumnThatDoesntExist FROM DerpyTable INTO ThisVariableDoesntExist WHERE YeahRight = :BindName;
END
The statement preparation code / execution code is below
...
...
if (OCIStmtPrepare2(connectionHandle
, &statementHandle
, errorHandle
, command
, commandLength
, nullptr
, 0
, OCI_NTV_SYNTAX
, OCI_DEFAULT))
ThrowLastError(__FUNCTIONW__, __FILE__, __LINE__);
...
...
if (OCIStmtExecute(connectionHandle
, statementHandle
, errorHandle
, 0
, 0
, 0, 0, OCI_PARSE_ONLY))
ThrowLastError(__FUNCTIONW__, __FILE__, __LINE__);
In this particular test - OCIStmtExecute should be failing - however OCI_SUCCESS (aka 0) is returned when OCIStmtExecute is invoked. Are there additional flags I should be using for these kind of statements or should I be looking at another approach?
I tried prepending the query with EXPLAIN PLAN FOR and invoke OCIStmtExecute with the default flag - but that just results in a missing-keyword error.
It is not a bug in the OCI the return code OCI_SUCCESS (aka 0) is correct. It sent your statement to to the data base.
Your statement is an anonymous block, indicated by BEGIN, and contains errors. You can do what you want, but you have to define the variable you select into. You have not done that so that would be 1 error. Also the INTO clause comes before the FROM so the statement should be "select...INTO...from", that would be error 2. Finally if it is true the column you select actually does not that will also generate a "compile" time error, that would be error 3. Also I believe EXPLAIN PLAN for an anonymous block, is also invalid, but it been a long time since I ran into it.
But you can try the following:
declare
ThisVariableDoesntExist varchar2(50), -- or the correct data type
begin
select columnthatdoesntexist
into thisvariabledoesntexist
from derpytable
where yeahright = :bindname;
end;
Note: As an anonymous block the above should run (if the column actually exists). However, you will not get output. You may wish to add
dbms_output.put_line('Value=' || ThisVariableDoesntExist);
Oracle object names are not case sensitive and echoed back form Oracle will be folded to upper case, so it bast to avoid CamelCase and instead use snake_case.

Calling PLsql script with an anonymous PL SQL block from SOCI

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?

Need to call tcl proc from another file in c++

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...

C++ and ADODB : Com error 0x800a0e78

I have a C++ library that calls a stored procedure in MSSQL database using ADODB. Everything works fine on development database. But on test database, I am getting Com error 0x800a0e78: This option is not allowed if an object is closed.
The code looks like
ADODB::_CommandPtr cmd = NULL;
ADODB::_RecordsetPtr rs = NULL;
CHECKHR(cmd.CreateInstance( __uuidof( ADODB::Command) ));
cmd->ActiveConnection = m_connexion;
cmd->CommandText = "my_stored_procedure";
cmd->CommandType = ADODB::adCmdStoredProc;
CHECKHR(cmd->Parameters->Refresh());
//input param
cmd->Parameters->GetItem(paramIndex)->put_Value(paramValue);
// output param
cmd->Parameters->GetItem(paramIndex)->PutDirection(ADODB::adParamOutput);
rs = cmd->Execute( NULL, NULL, ADODB::adCmdStoredProc);
while(! rs->ADO_EOF ) { ...
rs->ADO_EOF is where it crashes the program if I use the test database.
note: The stored procedure is same in both the databases and returns same data.
There is one more flow where another SP is called. It works well with the test database. The problem appears only with this particular SP.
I tend to think that it is not a code issue because it works with development database. But I can consistently reproduce the problem with test database.
Please suggest of next actions I should take to resolve this issue
UPDATE
Due to some miracle this C++ exception has gone away after 1 day. But it is so very slow that the execution almost always times out. I do not know how to justify this behavior
UPDATE: 2013-07-18
After so much time, the error has appeared again. This time with development DB with the same SP
[Com error 0x800a0e78 : Operation is not allowed when the object is closed.
on the same line
while(! rs->ADO_EOF ) {
in rs I can see a memory address pointing to ADODB recordset object. But rs->ADO_EOF is generating the said error

Tcl_Eval to return call stacks when error

When using Tcl C++ API Tcl_Eval, if it returns TCL_ERROR, the error message can be retrieved from Tcl_GetStringResult(interp). However, when executing a bunch of tcl script, the error message doesn't indicate which line the script fails.
Eg:
can't find package foobar
while executing
"package require foobar"
(file "./test.tn" line 5)
Tcl_GetStringResult(interp) doesn't provide this information: (file "./test.tn" line 5). Is there a way to print out the call stack like in tcl interpreter so that I know which line the script fails?
The information you are looking for, the error info (i.e., stack trace), is in the global errorInfo variable. This information may be retrieved with Tcl_GetVar or one of its related functions. One of the best to choose is Tcl_GetVar2Ex (the name being a product of a slowly evolving API) which is highly efficient:
Tcl_Obj *infoObj = Tcl_GetVar2Ex(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY);
Then you use Tcl_GetString to extract the human-readable part as a char * (treat as const).
I use the following code to pull back and report on the errorinfo that is available from the interpreter.
Tcl_Obj *top_interpInfoName ;
Tcl_Obj *top_interpInfo ;
top_interpInfoName = Tcl_NewStringObj("errorInfo", -1) ;
Tcl_IncrRefCount(top_interpInfoName) ;
top_interpInfo = Tcl_ObjGetVar2(tcl_interp,
top_interpInfoName,
NULL,
TCL_LEAVE_ERR_MSG) ;
Tcl_IncrRefCount(top_interpInfo) ;
ERR_REP2("ERROR: %s", Tcl_GetString(top_interpInfo)) ;
Tcl_DecrRefCount(top_interpInfoName) ;
Tcl_DecrRefCount(top_interpInfo) ;
ERR_REP2 is my error reporting macro you need to replace it with your own.