From: Jonathan Blitz on
Sorry for posting this here but the database forums are totally dead.
Maybe one or two posts a week or so.

I am using VC++ 2005 and am trying to get to an ODBC database.
This is my code:

SQLHENV henv1;
SQLRETURN returnValue;
SQLINTEGER odbcVersion = SQL_OV_ODBC3;
SQLWCHAR SQLState[5];
SQLINTEGER nativeErrorCode;
SQLWCHAR nativeErrorMessage[1000];
SQLSMALLINT nativeErrorMessageLength;

returnValue = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv1);

returnValue =
SQLSetEnvAttr(henv1,SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3,0);

/* Allocate connection handle */
SQLHDBC hdbc;
SQLHSTMT hstmt;

returnValue = SQLAllocHandle(SQL_HANDLE_DBC, henv1, &hdbc);


returnValue =
SQLConnectA(hdbc,(SQLCHAR*)"PTPGSQL21",9,(SQLCHAR*)"",SQL_NTS,(SQLCHAR*)"",S
QL_NTS);

returnValue = SQLAllocStmt(hdbc,&hstmt);

// Execute the SELECT statement.
SQLUINTEGER NumRowsFetched;
SQLUSMALLINT RowStatusArray[ROW_ARRAY_SIZE];
SQLINTEGER rowArraySize = 10;
typedef struct {SQLUINTEGER gameId; SQLINTEGER gameIdInd;} gameStruct;
gameStruct gamesData[ROW_ARRAY_SIZE];

returnValue = SQLSetStmtAttr(hstmt,
SQL_ATTR_ROW_BIND_TYPE,(void*)sizeof(gameStruct), 0);

returnValue = SQLSetStmtAttr(hstmt,
SQL_ATTR_ROW_ARRAY_SIZE,(void*)rowArraySize, 0);

returnValue = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR,
RowStatusArray,0);

returnValue = SQLSetStmtAttr(hstmt,
SQL_ATTR_ROWS_FETCHED_PTR,&NumRowsFetched, 0);

returnValue = SQLBindCol(hstmt, 1, SQL_C_ULONG, &gamesData[0].gameId,
0,&gamesData[0].gameIdInd);

returnValue = SQLExecDirect(hstmt,(SQLWCHAR *)"SELECT game_id FROM
game",SQL_NTS);

switch (returnValue)
{
case SQL_SUCCESS:
break;
case SQL_SUCCESS_WITH_INFO:
break;
case SQL_ERROR:
SQLError
(henv1,hdbc,hstmt,SQLState,&nativeErrorCode,nativeErrorMessage,1000,&nativeE
rrorMessageLength);
break;
case SQL_INVALID_HANDLE:
SQLGetDiagRec
(SQL_HANDLE_DBC,henv1,1,SQLState,&nativeErrorCode,nativeErrorMessage,1000,&n
ativeErrorMessageLength);
break;
};


All works ok until the SQLExecDirect command. It returns an error: "The # of
binded parameters < the # of parameter markers".
From what I can see I have the parameters. I copied this from an example in
the doumentation so I can't think of anything I left out.


What have I done wrong?


Jonathan Blitz



From: muchan on
Jonathan Blitz wrote:
> Sorry for posting this here but the database forums are totally dead.
> Maybe one or two posts a week or so.
>
> I am using VC++ 2005 and am trying to get to an ODBC database.
> This is my code:
>
> (snip)
> returnValue = SQLAllocStmt(hdbc,&hstmt);
>
> // Execute the SELECT statement.
> SQLUINTEGER NumRowsFetched;
> SQLUSMALLINT RowStatusArray[ROW_ARRAY_SIZE];
> SQLINTEGER rowArraySize = 10;
> typedef struct {SQLUINTEGER gameId; SQLINTEGER gameIdInd;} gameStruct;
> gameStruct gamesData[ROW_ARRAY_SIZE];
>
> returnValue = SQLSetStmtAttr(hstmt,
> SQL_ATTR_ROW_BIND_TYPE,(void*)sizeof(gameStruct), 0);
>
> returnValue = SQLSetStmtAttr(hstmt,
> SQL_ATTR_ROW_ARRAY_SIZE,(void*)rowArraySize, 0);
>
> returnValue = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR,
> RowStatusArray,0);
>
> returnValue = SQLSetStmtAttr(hstmt,
> SQL_ATTR_ROWS_FETCHED_PTR,&NumRowsFetched, 0);
>
> returnValue = SQLBindCol(hstmt, 1, SQL_C_ULONG, &gamesData[0].gameId,
> 0,&gamesData[0].gameIdInd);
>
> returnValue = SQLExecDirect(hstmt,(SQLWCHAR *)"SELECT game_id FROM
> game",SQL_NTS);
>
> (snip)
>
> All works ok until the SQLExecDirect command. It returns an error: "The # of
> binded parameters < the # of parameter markers".
> From what I can see I have the parameters. I copied this from an example in
> the doumentation so I can't think of anything I left out.
>
>
> What have I done wrong?
>


I can't tell what is wrong in your code, (I din't spent enough time
inspecting all the details), but I can tell the way I my code work.

After connection and get hstmt,

//=== in function SomeObjectSQL::Open() ===
if (SQLPrepare(hstmt, select_sql_statement, SQL_NTS) != SQL_SUCCESS) {
// ... error handling
}
if (SQLExecute(hstmt) != SQL_SUCCESS) {
// ... error handling
}

//SQLBindCol(hstmt, ...); // I bind the colam to a SQL object's members
// in your case:
SQLBindCol(hstmt, 1, SQL_C_ULONG, &gamesData[0].gameId, 0,&gamesData[0].gameIdInd);

//=== in another function SomeObjectSQL::Fetch(), for fetching multiple record... ===

RETCODE rc;

rc = SQLFetch(hstmt); // get the data to the binded members

if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO ) {
// here it's OK. do some work like trimming the string, etc.
} else {
// ... error handling
}

//=== after the loop of calling Fetch(), in function SomeObjectSQL::Close()

SQLTransact(link->henv, link->hdbc, SQL_COMMIT); // may not be neccessary
// esp, in nested fetching
SQLFreeStmt(hstmt, SQL_DROP);

//=================================================================

SomeObjectSQL obj; // Typically I have one more layer of POD object
// that uses this SQL object as implementation.
// but here I show direct usage of SQL object

if (obj.Open(where, order_by)) { // select statement is static inside, "where" and "order by"
while (obj.Fetch()) { // are often added dynamically
// .. do something with obj's data.
}
}
obj.Close();

Hope it helps

muchan
From: muchan on
Jonathan Blitz wrote:
>
> returnValue = SQLBindCol(hstmt, 1, SQL_C_ULONG, &gamesData[0].gameId,
> 0,&gamesData[0].gameIdInd);
>
> returnValue = SQLExecDirect(hstmt,(SQLWCHAR *)"SELECT game_id FROM
> game",SQL_NTS);
>
>
> What have I done wrong?
>
> Jonathan Blitz
>

Shorter answer:
Microsoft's help gives a example of SQLBindCol :

#define NAME_LEN 50
#define PHONE_LEN 10

SQLCHAR szName[NAME_LEN], szPhone[PHONE_LEN];
SQLINTEGER sCustID, cbName, cbCustID, cbPhone;
SQLHSTMT hstmt;
SQLRETURN retcode;

retcode = SQLExecDirect(hstmt,
"SELECT CUSTID, NAME, PHONE FROM CUSTOMERS ORDER BY 2, 1, 3",
SQL_NTS);

if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {

/* Bind columns 1, 2, and 3 */

SQLBindCol(hstmt, 1, SQL_C_ULONG, &sCustID, 0, &cbCustID);
SQLBindCol(hstmt, 2, SQL_C_CHAR, szName, NAME_LEN, &cbName);
SQLBindCol(hstmt, 3, SQL_C_CHAR, szPhone, PHONE_LEN, &cbPhone);

/* Fetch and print each row of data. On */
/* an error, display a message and exit. */

while (TRUE) {
retcode = SQLFetch(hstmt);
if (retcode == SQL_ERROR || retcode == SQL_SUCCESS_WITH_INFO) {
show_error();
}
if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO){
fprintf(out, "%-*s %-5d %*s", NAME_LEN-1, szName,
sCustID, PHONE_LEN-1, szPhone);
} else {
break;
}
}
}

So, you should call SQLBindCol after SQLExecDirect, and use SQLFetch to
retrieve the data.

muchan

 | 
Pages: 1
Prev: ModifyMenu issue
Next: CComBSTR vs string