invalid cursor state while using table valued parameter and ODBC - c++

I would like to create Table Valued Parameter in MSSQL Server using ODBC and c++. Such a SQL Code works fine in SQL Server:
DECLARE #T1 as TABLE
(
PK INT IDENTITY NOT NULL,
Wert CHAR(20),
Name CHAR(20)
)
INSERT INTO #T1(Wert,Name) VALUES ('123','Babio')
INSERT INTO #T1(Wert,Name) VALUES ('214','Martin')
INSERT INTO #T1(Wert,Name) VALUES ('236','Karo')
select * FROM #T1 where Wert=123
I have written this block of code in c++ to send this SQL Code to SQL Server using ODBC:
int main()
{
SQLHENV henv=SQL_NULL_HENV;
SQLHDBC hdbc=SQL_NULL_HDBC;
SQLHSTMT hstmt=SQL_NULL_HSTMT;
SQLRETURN rc;
SQLCHAR Statement[] = "DECLARE #T1 as TABLE (PK INT IDENTITY NOT NULL, Wert CHAR(20), Name CHAR(20)) INSERT INTO #T1(Wert,Name) VALUES ('123','Babak') INSERT INTO #T1(Wert,Name) VALUES ('214','Martin') INSERT INTO #T1(Wert,Name) VALUES ('236','Karo') select Name FROM #T1 Where Wert=123" ;
SQLWCHAR dsn[30] = L"mssqltest"; //Name DNS
SQLWCHAR user[10] = L"di_test";
SQLWCHAR pass[10] = L"di_test";
SQLCHAR retValFName[256];
SQLCHAR retValLName[256];
SQLINTEGER cbLName,cbFName;
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
rc= SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,(void *) SQL_OV_ODBC3,0);
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
rc = SQLConnectW(hdbc, (SQLWCHAR *)dsn, SQL_NTS, (SQLWCHAR *) user, SQL_NTS, (SQLWCHAR *) pass, SQL_NTS);
rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
rc = SQLPrepareA(hstmt, (SQLCHAR *)Statement, SQL_NTS);
rc = SQLExecute(hstmt);
if(rc==SQL_SUCCESS)
{
while(true)
{
rc=SQLFetch(hstmt); // In this line rc=-1
if(rc==SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO)
{
SQLGetData(hstmt,1,SQL_C_CHAR,retValFName,256, &cbLName);
std::cout << retValFName <<" "<< std::endl;
system("pause");
}
else
{
break;
}
}
}
return 0;
}
If I check the error, I see this error which says:
Invalid Cursor state
How could be my code changes to run this code to create and make a query on Table Valued Parameter in SQL?

Related

C++ - Access field in recordset

I am trying to access the field values of recordset. Currently there is a loop to count the recordsets but I don't know how to access specific fields.
rc = SQLDriverConnect(hDbc, NULL, (SQLWCHAR*)L"Driver={Microsoft Access Driver (*.mdb, *.accdb)};DSN='';DBQ=C:\\Temp\\Data.accdb;", SQL_NTS,szConnStrOut, 255, &iConnStrLength2Ptr, SQL_DRIVER_NOPROMPT);
if (SQL_SUCCEEDED(rc))
{
rc2 = SQLAllocStmt(hDbc, &hStmt);
rc2 = SQLPrepare(hStmt, (SQLWCHAR*)L"SELECT UniqueID FROM ObjectCheck", SQL_NTS);
/* Execute the query and create a record set */
rc2 = SQLExecute(hStmt);
/* Loop through the rows in the result set */
rc2 = SQLFetch(hStmt);
while (SQL_SUCCEEDED(rc2))
{
rc2 = SQLFetch(hStmt);
rowCount++;
//cout << UniqueID;
//rc3 = SQLAllocStmt(hDbc, &hStmt);
//rc3 = SQLPrepare(hStmt, (SQLWCHAR*)L"SELECT * FROM DependsOn WHERE UniqueID = " , SQL_NTS);
};
cout << rowCount;
}
Best regards
Steve

C++ ODBC SQL - Insert into table not working

I have been trying to insert a row into my SQL table, i get no syntax error but unfortunately when i check my table in the SQL Server Management Studio, no new entry is added. When debugging, the retCode becomes less than zero starting from the SQLConnect() function.
int main()
{
SQLHANDLE SQLEnvHandle = NULL;
SQLHANDLE SQLConnectionHandle = NULL;
SQLHANDLE SQLStatementHandle = NULL;
SQLRETURN retCode = 0;
// Insert Query
char SQLQuery[] = "insert into crm.dbo.company_name values (22,'01 electronics','#01electronics.net');";
// SQL Server Identifier
char SQLServer[] = "DRIVER={SQL Server}; SERVER=localhost, 8000; DATABASE=xxxx; UID=xxxx_xxxx; PWD=xxxx;";
do
{
// Allocate environment
retCode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &SQLEnvHandle);
// Set ODBC Version
retCode = SQLSetEnvAttr(SQLEnvHandle, SQL_ATTR_ODBC_VERSION,(SQLPOINTER*)SQL_OV_ODBC3, 0);
// Allocate Connection
retCode = SQLAllocHandle(SQL_HANDLE_DBC, SQLEnvHandle, &SQLConnectionHandle);
// Set Login Timeout
retCode = SQLSetConnectAttr(SQLConnectionHandle, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0);
// Set Auto Commit
retCode = SQLSetConnectAttr(SQLConnectionHandle, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)TRUE, 0);
// Connect to DSN
retCode = SQLConnect(SQLConnectionHandle, (SQLCHAR*) SQLServer, SQL_NTS, (SQLCHAR*)NULL, 0, NULL, 0);
// Allocate Statement Handle
retCode = SQLAllocHandle(SQL_HANDLE_STMT, SQLConnectionHandle, &SQLStatementHandle);
// Prepare Statement
retCode = SQLPrepare(SQLStatementHandle, (SQLCHAR*)SQLQuery, SQL_NTS);
// Execute Statement
if (SQLExecute(SQLStatementHandle) == SQL_SUCCESS || SQLExecute(SQLStatementHandle) == SQL_SUCCESS_WITH_INFO)
cout << "SUCCESS";
else
cout << "FAILURE";
} while (FALSE);
// Frees the resources and disconnects
SQLFreeHandle(SQL_HANDLE_STMT, SQLStatementHandle);
SQLDisconnect(SQLConnectionHandle);
SQLFreeHandle(SQL_HANDLE_DBC, SQLConnectionHandle);
SQLFreeHandle(SQL_HANDLE_ENV, SQLEnvHandle);
getchar();
}
When debugging, the retCode becomes less than zero starting from the SQLConnect() function.
From the docs:
When SQLConnect returns SQL_ERROR or SQL_SUCCESS_WITH_INFO, an
associated SQLSTATE value can be obtained by calling SQLGetDiagRec
with a HandleType of SQL_HANDLE_DBC and a Handle of ConnectionHandle

Create database via ODBC

How do you create new database using MFC ODBC API?
In the connection string you should mention the name of the database you want to connect to. What if I have just-installed SQL Server containing no user databases?
Which database name do you specify in the connection string?
E.g. for SQL Server:
CDatabase db;
db.OpenEx(L"Driver={ODBC Driver 11 for SQL Server};Server=myServerAddress;"
L"Database=????????;Uid=myUsername;Pwd=myPassword", CDatabase::noOdbcDialog);
db.ExecuteSQL(L"CREATE DATABASE testdb");
Should I use System Databases (e.g. master, model, etc.)?
Is there some more generic approach?
You can omit Database= in the connection string. Then you create a new database and after that you switch to that database using USE.
Something like this works fine for me:
SQLWCHAR strConnect[256] = L"Driver={SQL Server};Server=.\\MACHINE;Trusted_Connection=yes;";
SQLWCHAR strConnectOut[1024] = { 0 };
SQLSMALLINT nNumOut = 0;
SQLRETURN nResult = SQLDriverConnect(handleDBC, NULL, (SQLWCHAR*)strConnect, SQL_NTS, (SQLWCHAR*)strConnectOut, sizeof(strConnectOut), &nNumOut, SQL_DRIVER_NOPROMPT);
if (!SQL_SUCCEEDED(nResult))
// some error handling
SQLHSTMT handleStatement = SQL_NULL_HSTMT;
nResult = SQLAllocHandle(SQL_HANDLE_STMT, handleDBC, (SQLHANDLE*)&handleStatement);
if (!SQL_SUCCEEDED(nResult))
// some error handling
// Create a new database and use that
nResult = SQLExecDirect(handleStatement, L"CREATE DATABASE Foobar", SQL_NTS);
nResult = SQLExecDirect(handleStatement, L"USE Foobar", SQL_NTS);
// create table Wallet in database Foobar
nResult = SQLExecDirect(handleStatement, L"CREATE TABLE Wallet (WalletID int NOT NULL, Name nvarchar(5) NOT NULL)", SQL_NTS);

Running a query with lots of INSERT statement and one SELECT statement in the wrong order

I would like to INSERT lots of rows in a table. I send my values as an array to a parameterized INSERT Query with ODBC in C++ like this:
INSERT INTO information1(Wert1,Wert2) VALUES(?,?) select wert1 from information1 WHERE Wert2=9"
as you see I would like to run a select statement after INSERTs.
This code is able to insert rows in the table, but the result of select statement is wrong (The rsualt should be d). I have this expectation that something like this runs in the database:
INSERT INTO information1(Wert1,Wert2) VALUES(a,4)
INSERT INTO information1(Wert1,Wert2) VALUES(b,3)
INSERT INTO information1(Wert1,Wert2) VALUES(c,17)
INSERT INTO information1(Wert1,Wert2) VALUES(d,9)
INSERT INTO information1(Wert1,Wert2) VALUES(e,5)
INSERT INTO information1(Wert1,Wert2) VALUES(f,45)
INSERT INTO information1(Wert1,Wert2) VALUES(g,6)
INSERT INTO information1(Wert1,Wert2) VALUES(h,4)
INSERT INTO information1(Wert1,Wert2) VALUES(i,2)
SELECT Wert1 FROM information1 WHERE Wert2=9
but what actually happend is:
INSERT INTO information1(Wert1,Wert2) VALUES(a,4)
SELECT Wert1 FROM information1 WHERE Wert2=9
INSERT INTO information1(Wert1,Wert2) VALUES(b,3)
INSERT INTO information1(Wert1,Wert2) VALUES(c,17)
INSERT INTO information1(Wert1,Wert2) VALUES(d,9)
INSERT INTO information1(Wert1,Wert2) VALUES(e,5)
INSERT INTO information1(Wert1,Wert2) VALUES(f,45)
INSERT INTO information1(Wert1,Wert2) VALUES(g,6)
INSERT INTO information1(Wert1,Wert2) VALUES(h,4)
INSERT INTO information1(Wert1,Wert2) VALUES(i,2)
hence it returns nothing (wrong result for me). Could someone explain me why the wrong order of statements are run in database? and how should I change my code to have the correct order?
Here you can find my whole code:
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include<tchar.h>
#include <sql.h>
#include <sqlext.h>
#include<sqltypes.h>
//#include<odbcss.h>
//#include<odbcinst.h>
#include<exception>
#include<iostream>
#include<stdio.h>
#include <conio.h>
#include "Debug\sqlncli.h";
void HandleDiagnosticRecord (SQLHANDLE hHandle,
SQLSMALLINT hType,
RETCODE RetCode)
{
SQLSMALLINT iRec = 0;
SQLINTEGER iError;
WCHAR wszMessage[1000];
WCHAR wszState[SQL_SQLSTATE_SIZE+1];
if (RetCode == SQL_INVALID_HANDLE)
{
fwprintf(stderr, L"Invalid handle!\n");
return;
}
while (SQLGetDiagRec(hType,
hHandle,
++iRec,
wszState,
&iError,
wszMessage,
(SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
(SQLSMALLINT *)NULL) == SQL_SUCCESS)
{
// Hide data truncated..
if (wcsncmp(wszState, L"01004", 5))
{
fwprintf(stderr, L"[%5.5s] %s (%d)\n", wszState, wszMessage, iError);
}
}
}
#define TRYODBC(h, ht, x) { RETCODE rc = x;\
if (rc != SQL_SUCCESS) \
{ \
HandleDiagnosticRecord (h, ht, rc); \
} \
if (rc == SQL_ERROR) \
{ \
fwprintf(stderr, L"Error in " L#x L"\n"); \
Sleep(30000); \
} \
}
void extract_error(
char *fn,
SQLHANDLE handle,
SQLSMALLINT type)
{
SQLINTEGER i = 0;
SQLINTEGER native;
SQLWCHAR state[ 7 ];
SQLWCHAR text[256];
SQLSMALLINT len;
SQLRETURN ret;
fprintf(stderr,
"\n"
"The driver reported the following diagnostics whilst running "
"%s\n\n",
fn);
do
{
ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
sizeof(text), &len );
if (SQL_SUCCEEDED(ret))
printf("%s:%ld:%ld:%s\n", state, i, native, text);
}
while( ret == SQL_SUCCESS );
}
int main()
{
SQLHENV henv=SQL_NULL_HENV;
SQLHDBC hdbc=SQL_NULL_HDBC;
SQLHSTMT hstmt=SQL_NULL_HSTMT;
RETCODE rc;
SQLTCHAR Statement[] = _T("INSERT INTO information1(Wert1,Wert2) VALUES(?,?) select count(*) as a from information1 ") ;
SQLTCHAR sqlStatementOUT[255];
SQLINTEGER lenStatementOUT=0;
memset(sqlStatementOUT,' ',255);
SQLWCHAR dsn[30] = L"mssqltest"; //Name DNS
SQLWCHAR user[10] = L"di_test";
SQLWCHAR pass[10] = L"di_test";
SQLCHAR retValFName[256];
SQLCHAR retValLName[256];
SQLINTEGER cbLName,cbFName;
SQLLEN cName=0;
SQLCHAR *TVPTableName=(SQLCHAR *) "TableTVP";
#define PARAM_ARRAY_SIZE 9
#define STRING_SIZE 20
SQLCHAR Arrayname1[PARAM_ARRAY_SIZE][STRING_SIZE] = {"4","3","17","9","5","45","6","4","2"};
SQLCHAR Arrayname2[PARAM_ARRAY_SIZE][STRING_SIZE] = {"a","b","c","d","e","f","g","h","i"};
SQLLEN cbTVPTableNAme=10;
SQLLEN cbArraycode[10],cbArrayname[10];
SQLLEN lTVPRowsUsed;
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
rc= SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,(void *) SQL_OV_ODBC3,0);
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
rc = SQLConnectW(hdbc, (SQLWCHAR *)dsn, SQL_NTS, (SQLWCHAR *) user, SQL_NTS, (SQLWCHAR *) pass, SQL_NTS); // from this line rc=-1
rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt);
rc = SQLSetStmtAttr (hstmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, 0);
SQLUSMALLINT Param_status_array[PARAM_ARRAY_SIZE];
SQLULEN ParamsProcessed;
rc=SQLSetStmtAttr(hstmt,SQL_ATTR_PARAMSET_SIZE,(SQLPOINTER*) PARAM_ARRAY_SIZE, 0);
rc=SQLSetStmtAttr(hstmt,SQL_ATTR_PARAM_STATUS_PTR,Param_status_array,0);
rc=SQLSetStmtAttr(hstmt,SQL_ATTR_PARAMS_PROCESSED_PTR,&ParamsProcessed,0);
SQLUSMALLINT newsize=0;
rc=SQLBindParameter(hstmt,1,SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, STRING_SIZE, 0, Arrayname1, STRING_SIZE,0);
rc=SQLBindParameter(hstmt,2,SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, STRING_SIZE, 0, Arrayname2, STRING_SIZE,0);
rc = SQLPrepare(hstmt, (SQLTCHAR *)Statement, SQL_NTS);
rc=SQLGetStmtAttr(hstmt,SQL_ATTR_PARAMSET_SIZE,&newsize,4,NULL);
newsize=0;
rc = SQLExecute(hstmt);
rc=SQLGetStmtAttr(hstmt,SQL_ATTR_PARAMSET_SIZE,&newsize,4,NULL);
if(rc==SQL_SUCCESS)
{
SQLSMALLINT sNumResults;
SQLNumResultCols(hstmt,&sNumResults);
SQLBindCol(hstmt,
1,
SQL_C_TCHAR,
(SQLPOINTER) retValFName,
100,
0);
while(true)
{
rc=SQLFetch(hstmt);
if(rc==SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO)
{
SQLGetData(hstmt,1,SQL_C_CHAR,retValFName,256, &cbLName);
std::cout << retValFName <<" "<< std::endl;
}
else
{
break;
}
}
}
system("pause");
return 0;
}
INSERT INTO information1(Wert1,Wert2) VALUES(?,?) Performs a SINGLE RECORD INSERT. Not a bulk insert.
So when you excecute
INSERT INTO information1(Wert1,Wert2) VALUES(?,?) select count(*) as a from information1
It performs a single insert and then the select.
Separate the two sql commands. Perform the insert for the array, and then separately the select.

Changing odbc timeout

I am trying to run following stored procedure using ODBC:
CREATE PROCEDURE [dbo].[Add]
--WITH ENCRYPTION
AS
DECLARE #LoopVar BIGINT = 0
, #MaxVar BIGINT = 0
, #rows BIGINT = 0
SET #LoopVar = 1
set #rows = 125000
insert into debug values(987654321)
insert into debug values(#LoopVar)
insert into debug values(#rows)
WHILE(#LoopVar <= #rows)
BEGIN
SET #LoopVar = #LoopVar + 1
WAITFOR DELAY '00:00:01'
insert into debug values(#LoopVar)
END
insert into debug values(123456789)
GO
The C++ code for running the stored procedure is:
RETCODE rc = SQL_SUCCESS;
HENV henv = SQL_NULL_HENV;
HDBC hdbc = SQL_NULL_HDBC;
SQLHSTMT hstmt = SQL_NULL_HSTMT;
SQLTCHAR * pszConnection = _T("DRIVER={SQL Server Native Client 10.0};Server=myserver;Trusted_Connection=Yes;Initial Catalog=testdb;");
SQLTCHAR * pszInsertStmt = _T("{call [testdb].[dbo].Add}");
SQLLEN cbParamLength;
SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HENV, &henv);
SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
SQLSetConnectAttr( hdbc, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER>(600), SQL_IS_UINTEGER);
SQLDriverConnect( hdbc, NULL, pszConnection, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
SQLSetStmtAttr(hstmt, SQL_QUERY_TIMEOUT, (SQLPOINTER)12000, SQL_IS_UINTEGER);
SQLSetStmtAttr(hstmt, SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER)12000, SQL_IS_UINTEGER);
SQLINTEGER attr;
rc = SQLGetStmtAttr( hstmt, SQL_ATTR_QUERY_TIMEOUT, &attr, 0, NULL ) ;
rc = SQLGetStmtAttr( hstmt, SQL_QUERY_TIMEOUT, &attr, 0, NULL ) ;
rc = SQLGetConnectAttr(hdbc, SQL_ATTR_CONNECTION_TIMEOUT, &attr, 0, NULL);
rc = SQLExecDirect(hstmt, pszInsertStmt, SQL_NTS);
if (!SUCCESS(rc)) {
if (hstmt)
PrintError(SQL_HANDLE_STMT, hstmt);
if (hdbc)
PrintError(SQL_HANDLE_DBC, hdbc);
if(henv)
PrintError(SQL_HANDLE_ENV, henv);
}
if (hstmt)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
if (hdbc) {
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
if (henv)
SQLFreeHandle(SQL_HANDLE_ENV, henv);
I have set query time in the code above. The connection time out is 0 (which I believe means no timeout). But no matter what I do, the stored procedure times out in 78 seconds. Does any one have any idea as to what I should do so that stored procedure can run indefinitely?
Please note if I run the stored procedure from SQL Server Management Studio directly, it works just fine..
Thanks in advance,
-Neel.
If anyone interested, the solution was to put "SET NOCOUNT ON" as the first line in stored procedure.