Reputation: 1200
I've got a fairly simple c++ application that uses ODBC to connect to a SQL Server instance that uses a stored procedure to populate.
I use the lines of a wstring object to build a query that is then passed through to the stored procedure. Everything works fine if I run it a single time - however, I want to be able to loop through a quite extensive amount of code (3000+ excel rows) and as I do it the cursor error mentioned in the title occurs.
This is the stored procedure:
GO
CREATE PROCEDURE s_addHistorical
@Symbol nchar(10),@Date datetime,
@Open decimal(8,2),@Close decimal(8,2),@MinPrice decimal(8,2),
@MaxPrice decimal(8,2),@Volume int
AS
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
MERGE HistoricalStock WITH (UPDLOCK) AS myTarget
USING (SELECT @Symbol AS Symbol,
@Date AS Date, @Open AS [Open], @Close AS [Close],
@MinPrice AS MinPrice, @MaxPrice AS MaxPrice,@Volume AS Volume) AS mySource
ON mySource.Symbol = myTarget.Symbol AND mySource.Date = myTarget.Date
WHEN MATCHED
THEN UPDATE
SET [Open] = mySource.[Open], [Close] = mySource.[Close],
MinPrice = mySource.MinPrice, MaxPrice = mySource.MaxPrice, Volume = mySource.Volume
WHEN NOT MATCHED
THEN
INSERT(Symbol,Date,[Open],[Close],MinPrice,MaxPrice,Volume)
VALUES(@Symbol,@Date,@Open,@Close,@MinPrice,@MaxPrice,@Volume);
COMMIT
GO
And this is the connector:
#include "stdafx.h"
#include "database_con.h"
////////////////////////////////////////////////////////////////////////
// Show errors from the SQLHANDLE
void database_con::show_error(unsigned int handletype, const SQLHANDLE& handle)
{
SQLWCHAR sqlstate[1024];
SQLWCHAR message[1024];
if (SQL_SUCCESS == SQLGetDiagRec(handletype, handle, 1, sqlstate, NULL, message, 1024, NULL))
wcout << "Message: " << message << "\nSQLSTATE: " << sqlstate << endl;
}
////////////////////////////////////////////////////////////////////////
// Builds the stored procedure query.
std::wstring database_con::buildQuery(vector<std::wstring> input)
{
std::cout << "Building the query" << std::endl;
std::wstringstream builder;
builder << L"EXEC sp_addHistorical " << "@Symbol='" << L"" << input.at(0) << "'," <<
"@Date=" << (wstring)L"" << input.at(1) << "," <<
"@Open=" << (wstring)L"" << input.at(2) << "," <<
"@Close=" << (wstring)L"" << input.at(3) << "," <<
"@MaxPrice=" << (wstring)L"" << input.at(4) << "," <<
"@MinPrice=" << (wstring)L"" << input.at(5) << "," <<
"@Volume=" << (wstring)L"" << input.at(6) << ";";
return builder.str();
}
////////////////////////////////////////////////////////////////////////
// Adds a substring of the string before the delimiter to a vector<wstring> that is returned.
std::vector<wstring> database_con::parseData(wstring line, char delim) {
size_t pos = 0;
std::vector<std::wstring> vOut;
while ((pos = line.find(delim)) != std::string::npos) {
vOut.push_back(line.substr(0, pos));
line.erase(0, pos + 2);
}
vOut.push_back(line.substr(0, pos));
return vOut;
}
std::wstring database_con::StringToWString(const std::string& s)
{
std::wstring temp(s.length(), L' ');
std::copy(s.begin(), s.end(), temp.begin());
return temp;
}
database_con::database_con(std::string historical){
/*
Set up the handlers
*/
/* Allocate an environment handle */
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
/* We want ODBC 3 support */
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
/* Allocate a connection handle */
SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
/* Connect to the DSN */
SQLDriverConnectW(dbc, NULL, L"DRIVER={SQL Server};SERVER=ERA-PC-STUART\\JBK_DB;DATABASE=master;UID=geo;PWD=kalle123;", SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE);
/* Check for success */
if (SQL_SUCCESS != SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt))
{
show_error(SQL_HANDLE_DBC, dbc);
std::cout << "Failed to connect";
}
std::wstringstream stream(StringToWString(historical));
std::wstring line;
while (std::getline(stream, line)) {
vector<wstring> vHistorical = parseData(L"" + line, ',');
std::wstring SQL = buildQuery(vHistorical);
if (SQL_SUCCESS != SQLExecDirectW(stmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS)) {
show_error(SQL_HANDLE_STMT, stmt);
}
}
}
database_con::~database_con() {
}
I've been looking around at google and on SO, but I can't seem to find any questions that are usable in my current question. Most of them seem to be revolving around stored procedures sending back some sort of extracts, whereas my SP is simply inserting / updating.
Any sort of help would be greatly appriciated. :)
Anyone?
Upvotes: 0
Views: 5665
Reputation: 1652
You need to call SQLCloseCursor
to release the Cursor.
Change your code to:
while (std::getline(stream, line)) {
vector<wstring> vHistorical = parseData(L"" + line, ',');
std::wstring SQL = buildQuery(vHistorical);
if (SQL_SUCCESS != SQLExecDirectW(stmt, const_cast<SQLWCHAR*>(SQL.c_str()), SQL_NTS)) {
show_error(SQL_HANDLE_STMT, stmt);
}
// Close Cursor before next iteration starts:
SQLRETURN closeCursRet = SQLCLoseCursor(stmt);
if(!SQL_SUCCEEDED(closeCursRet))
{
// maybe add some handling for the case that closing failed.
}
}
See: https://msdn.microsoft.com/en-us/library/ms709301%28v=vs.85%29.aspx
Upvotes: 2