How Can I Call PL/pgSQL Function From C++ Code - c++

I am trying to call a function which is declared in PostgreSQL with PL/pgSQL. For that I write the code below. My function is working but after that I am taking a "PGRES_FATAL_ERROR". Also when I changed "select removestopwords()" with an sql query like "DELETE * FROM TABLE1" it's working successfully.
I am considering, that error can cause some big problem in future even if now working. How can I call a PL/pgSQL function without taking error?
void removeStopWordsDB(PGconn* conn) {
PGresult *res = PQexec(conn, "select removestopwords()");
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
printf("removestopwords failed");
cout<<PQresultStatus(res);
PQclear(res);
exit_nicely(conn);
}
printf("removestopwords - OK\n");
PQclear(res);
}

If you get PGRES_FATAL_ERROR from PQresultStatus you should use PQresultErrorField to get all the error data from the result set to provide a useful error message. This will allow you to determine what the actual error is here (quite likely an error being sent over from the server).
Consider creating a class to hold PostgreSQL error details that can be constructed from q PQresult pointer, e.g.:
PgError(const PGresult *rs)
{
severity = GetErrorField(rs, PG_DIAG_SEVERITY);
sqlstate = GetErrorField(rs, PG_DIAG_SQLSTATE);
primary = GetErrorField(rs, PG_DIAG_MESSAGE_PRIMARY);
// ...
}
static std::string GetErrorField(const PGresult *rs, int fieldCode)
{
const char *message = PQresultErrorField(rs, fieldCode);
if (message == NULL) return "";
return std::string(message);
}
Then you can, for example, encapsulate dumping out the error to a stream in this object to provide details just like psql and friends do (although strictly speaking, you'd need the input SQL as well for all of that)

PostgreSQL API doesn't support some flag like "ignore all errors". If you would to ignore result, then just don't check result in host environment. But it is bad strategy.

Related

C++ Legacy Driver mongoDB Replicaset in Class of a DLL

I have built a dll which includes a class implementing the mongoDB replicaset operations. Here is a summary of the class.
#include "mongo/client/dbclient.h"
mongoimp::mongoimp() {
mongo::client::initialize();
}
mongoimp::~mongoimp() {
mongo::client::shutdown();
}
int mongoimp::queryTRecords() {
string errmsg;
vector<mongo::HostAndPort> hosts = { mongo::HostAndPort("xx-a0.yyyy.com:xxxxx"), mongo::HostAndPort("xx-a1.yyyy.com:xxxxx") };
static mongo::DBClientReplicaSet con("xx", hosts, 0);
con.connect();
con.auth("dbname", "username", "password", errmsg);
auto_ptr<DBClientCursor> cursor = con.query("dbname.t", BSONObj());
BSONObj response;
con.logout("xx", response);
if (cursor->more()) {
BSONObj recordnm = cursor->nextSafe();
return(recordnm.getIntField("lastid"));
} else return(-1);
}
The above code is working. But here are my questions:
1) With the above setting, I can do normal mongoDB operations with the dll but since my application needs to constantly update mongoDB data (close to real-time, up to hundreds a second), I am getting error (No valid replicaset instance servers found) when updating data.
2) Only the server needs to talk to the mongoDB database. So basically I just need one connection to the database. So I want to declare the mongo::DBClientReplicaSet con as a static global variable and connect to it in the class construct function. But it seemed I cannot do it. My application cannot run at all. With that, I constantly get the following error.
Assertion failed: px != 0, file C:\Boost\include\boost-1_62\boost/smart_ptr/scoped_ptr.hpp, line 105
Does anybody know how to solve the problem?
Below is the code I tried:
static mongo::DBClientReplicaSet con("xx", { mongo::HostAndPort("xx-a0.yyyy.com:xxxxx"), mongo::HostAndPort("xx-a1.yyyy.com:xxxxx") }, 0);
mongoimp::mongoimp() {
mongo::client::initialize();
string errmsg;
con.connect();
con.auth("dbname", "username", "password", errmsg);
}
mongoimp::~mongoimp() {
BSONObj response;
con.logout("xx", response);
mongo::client::shutdown();
}
int mongoimp::queryTRecords() {
auto_ptr<DBClientCursor> cursor = con.query("dbname.t", BSONObj());
if (cursor->more()) {
BSONObj recordnm = cursor->nextSafe();
return(recordnm.getIntField("lastid"));
} else return(-1);
}
3) Last question, I noticed there is mongo/client/dbclient_rs.h" file for replicaset. But it seemed I cannot use it. With that, I am getting error for initialize() and auto_ptr cursor. How can I use the file and take full advantage of replicaset features? How can I initialize the relica set if I can use "dbclient_rs.h"? How do I do query and fetch data in that case?
Thanks a lot in advance!
For question No. 2, I remembered the reason for the error:
You need to call mongo::client::initialize before you construct any driver objects, or BSON for that matter.
But how to make that global definition possible, I still need a solution.

ADO Recordset->EndOfFile giving me _com_error when Empty Recordset

I'm using ADO, and getting a very weird com error.
So I'm simply running a stored proc using ADO CommandPtr, and storing it in a Recordset.
Here is what I'm doing:
_ConnectionPtr Connptr;
//Instantiate ConnectionPtr...
_CommapndPtr CommPtr;
CommPtr.CreateInstance(__uuidof(Command));
CommPtr->CommandType = adCmdText;
CommPtr->ActiveConnection = ConnPtr;
CommPtr->CommandText = "Execute MyDb..MyStoredProc";
_RecordsetPtr RecPtr;
RecPtr.CreateInstance(__uuidof(Recordset));
RecPtr->CursorLocation = adUseClient;
RecPtr->CacheSize = 150;
RecPtr = CommPtr->Execute(NULL, NULL, adOptionUnspecified); //RecPtr = Empty Recordset
while (!RecPtr->EndOfFile) { //ERROR HAPPENS HERE!!!
//Do something
RecPtr->MoveNext();
}
So my stored procedure is supposed to returns an empty recordset (0 rows).
But then , when I check if the recordset has reached the end (which should simply return true if it is empty). I get a com error.
When I caught the com error and printed it out, I got this.
Code = -2147217849
Meaning = IDispatch error #3153
Source = NULL
Which doesn't tell me much.
I don't understand why RecPtr->EndofFile is throwing a com error, since it should simply return true/false.
I highly doubt that the error is caused because I'm doing something wrong when initializing Connection and Command objects. (If so, then I would have gotten the error when Executing the command.)
Any ideas on what might be causing this exception?

sqlite3_step() doesn't seem to work

I have this C++ function which works to check if a given name exists. But every time it returns false even when a given name already exists. Where do I do wrong?
bool Database::hasRepository(std::string repoName)
{
string sql = "SELECT * FROM repository WHERE NAME='";
sql += repoName + "'";
sqlite3_stmt* selectStmt = nullptr;
sqlite3_prepare_v2(connection, sql.c_str(), sql.size(), &selectStmt, NULL);
int results = sqlite3_step(selectStmt);
sqlite3_finalize(selectStmt);
if(results == SQLITE_ROW)
return true;
else
return false;
}
You should check the return value of all functions which might return an error indication. For example, sqlite3_prepare_v2 will return an error code if the statement has a syntax error.
That might have told you that the statement you supplied is incorrect (if it is).
You can use sqlite3_errmsg and sqlite3_errstr to produce more readable error messages. It is never a good idea to just throw away the specific information about the error, for example by just returning false, particularly during debugging.

C++ and sqlite3- sqlite3_prepare_v2 is not working

I'm using sqlite3 with C++ . But the problem is that when I debugged the code I realised that it does not, I mean sqlite3_prepare_v2 does not extract the data from the database.
When I printed the value it extracted, it printed some garbage value. At the same time, I noticed the following warning:
Warning 4 warning LNK4248: unresolved typeref token (01000028) for
'sqlite3_stmt'; image may not run .
I'm working on MS visual studio 2010 windows forms application.
Can anyone help?
I think the linker complaint is ok. See http://social.msdn.microsoft.com/Forums/en-US/8376b2f0-cc36-48c8-9021-f30bda41f410/linker-warning-lnk4248-possible-problem
The following does not take into account that you're programming in MS managed C++, however, I hope it still provides some guidance.
sqlite3_prepare_v2 prepares the statement but does not execute anything.
You need to call sqlite3_step to execute (or partly execute) the SQL.
For an SQL statement that returns nothing (e.g. UPDATE, DELETE, INSERT) you call sqlite3_step once and it should return SQLITE_DONE if it worked.
For a statement that can return multiple rows you call sqlite3_step repeatedly. Each time the return code is SQLITE_ROW you have been returned a row of data that you can access with the
sqlite3_column_* set of statements. When all data has been returned sqlite3_step returns SQLITE_DONE.
Code might look something like this
sqlite3* db;
int rc = sqlite3_open(databasePath.c_str(), &db);
std::string sql = "SELECT Id, Url FROM Url WHERE Page_Download_Reqd <> 0;";
sqlite3_stmt* statementPtr;
const char* tailPtr;
int rc = sqlite3_prepare_v2(db, sql.c_str(), sql.size(), &statementPtr, &tailPtr);
bool finished = false;
do {
int rc = sqlite3_step(statementPtr);
switch (rc) {
case SQLITE_ROW: {
__int64 id = sqlite3_column_int(statementPtr, 0);
std::string url = reinterpret_cast<const char*>(sqlite3_column_text(statementPtr, 1));
// Do something with your data
}
break;
case SQLITE_DONE:
finished = true;
break;
default:
assert(false);
}
} while (!finished);
If you have variables in you SQL, e.g.
SELECT Id, Url FROM Url WHERE Count <> :count;
then you need to use one of the sqlite3_bind_* functions between the sqlite3_prepare_v2 and sqlite3_step functions. For example
__int count = 6;
std::string bindVarName = ":count";
rc = sqlite3_bind_int(
statementPtr,
sqlite3_bind_parameter_index(statementPtr, bindVarName.c_str()),
count);

App crash on access new Database

My application crashes on reading / writing data from the database. I have one database on c: and I copy-pasted and rename with different name. The following process is what I have used for copy...Please guide me if you have any suggestion or solution.
RFs fs;
fs.Connect();
CFileMan* fileMan=CFileMan::NewL(fs);
CleanupStack::PushL(fileMan);
TInt err=fileMan->Copy(anOld,aNew);
CleanupStack::PopAndDestroy(fileMan);
fs.Close();
if(err==KErrNone)
return ETrue;
else
return EFalse;
It crashes on following line when I am trying to insert or get any data from the database.
User::LeaveIfError( iDatabase.Execute( strSQL ) );
db creation:
TBool Open = OpenL();
if (!Open)
{
User::LeaveIfError(iDbSession.Connect());
CleanupClosePushL(iDbSession);
CleanupClosePushL(iDatabase);
User::LeaveIfError(iDatabase.Replace(iDbSession, iDBPath ));
// create table
_LIT(KSQLtest,"CREATE TABLE testtable(id INTEGER,test1 VARCHAR(50),test2 VARCHAR(50))"); User::LeaveIfError(iDatabase.Execute(KSQLtest));
iDatabase.Compact();
iDatabase.Close();
iDbSession.Close();
CleanupStack::PopAndDestroy();
CleanupStack::PopAndDestroy();
Open database:
User::LeaveIfError( iDbSession.Connect() );
CleanupClosePushL( iDbSession );
if ( KErrNone != iDatabase.Open(iDbSession, iDBPath))
{
iDbSession.Close();
CleanupStack::PopAndDestroy();
return EFalse;
}
else
{
CleanupClosePushL( iDatabase );
iIsDatabaseOpened = ETrue;
return ETrue;
}
User:: LeaveIfError() throws an exception when iDatabase.Execute() returns an error code.
You can find the most common Symbian error codes at NewLC
If the crash happens before RDbDatabase::Execute() is actually run, we'll need to see more code to figure out why iDatabase is in a bad state.
You need to explain what "crashes" means - an exception/leave? a panic? If so, what leave code, or what panic category and number?
If it "crashes" here
User::LeaveIfError( iDatabase.Execute( strSQL ) );
you might want to check the return value, i.e.
TInt error = iDatabase.Execute( strSQL );
//Now log/display the error
User::LeaveIfError(error);
A few other points of note:
If you use CleanupClosePushL() on an object, you don't need to call both Close() and CleanupStack::PopAndDestroy(). The latter will call Close() for you.
Your OpenL() function uses a mix of leaving and return code which is considered bad style generally. In addition, functions which leave something on the cleanup stack are generally named xxxxLC(), the trailing 'C' denoting a cleanup item.