Question

I have a test table using a Microsoft SQL Server that is defined like this:

CREATE TABLE [dbo].[Table] ( [FirstName] NVARCHAR (255) NULL, [LastName] NVARCHAR (255) NULL );

There's just one row in the table with the values "person" and "man", respectively.

I'm trying to add a function that will update the values of that row but I keep running into this "[Microsoft][ODBC SQL Server Driver]String data, right truncation State code: 22001" error and I cannot figure out what the problem is. I've looked around and people say that it is caused by the data being too long to fit in the column but that's impossible because the string I'm trying to update with is only two characters, and as you can see in the table definition there is plenty of space for it.

I'm using a prepared statement for optimization purposes and the code creating it looks something like this:

const tString query("UPDATE \"" + tableName + "\" SET " + setClause + " WHERE " + whereClause + ";");

SQLHSTMT statement;
SQLAllocHandle(SQL_HANDLE_STMT, fSQLConnection, &statement);

SQLPrepareW(statement, (SQLWCHAR *) query.mWideStr(), SQL_NTS);`

The query string looks like this:

UPDATE "Table" SET "FirstName" = ?, "LastName" = ? WHERE "FirstName" = ? AND "LastName" = ?;

And then I am binding the parameters like this:

// We have our own string class that we use, which is where the mWideStr() and mGetStrSize()
// come from. mWideStr() returns a pointer to a UCS-2 buffer and mGetStrSize() returns the
// size in bytes.
SQLLEN pcbValue(SQL_NTS);
SQLUSMALLINT paramIndex(1);

// Call this for each parameter in the correct order they need to be bound, incrementing the
// index each time.
SQLBindParameter(statement, paramIndex++, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_VARCHAR, 255, 0, (SQLPOINTER) paramValue.mWideStr(), paramValue.mGetStrSize(), &pcbValue);

The first and second bound parameters are the new values which are both just "55", then third would be "person" and fourth would be "man".

Then to execute the statements it's just a call to SQLExecute():

SQLExecute(statement);

The call to SQLExecute() fails and then the error is generated and there is some more code that outputs the error message. As far as I can tell this should all be working perfectly fine. I have another database using Oracle that uses the exact same setup and code and it works without any issues, it's just SQL Server that's barfing for some reason. Is there something obviously wrong here that I'm missing? Does SQL Server have some weird rules that I need to add somewhere?

Was it helpful?

Solution

The SQLLEN pcbValue(SQL_NTS); variable being passed to SQLBindParameter() was going out of scope between binding the parameters and executing the statement, which means that some garbage data was being pointed to in the parameter binding. I also realized that you don't need to specify the last parameter. You can just pass NULL and it will act as if it is a nul-terminated string.

So the fix was to remove the SQLLEN pcbValue(SQL_NTS); variable and to just pass NULL to SQLBindParameter() for the last parameter instead.

Stupid mistake, but worth noting I suppose.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top