Skip to content

Instantly share code, notes, and snippets.

@mloskot
Created April 15, 2020 19:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mloskot/207fed21c6ec14bb3a6f817b4bc9b8c6 to your computer and use it in GitHub Desktop.
Save mloskot/207fed21c6ec14bb3a6f817b4bc9b8c6 to your computer and use it in GitHub Desktop.
Conversion with CAST(? AS BIT) only works for 0 or 1 when using ODBC API. 22003: [Microsoft][ODBC Driver 17 for SQL Server]Numeric value out of rang
// MWE for bug report submitted to SQL Server:
// https://feedback.azure.com/forums/908035-sql-server/suggestions/40180918-conversion-with-cast-as-bit-only-works-for-0-or
#include <cassert>
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>
#define NOMINMAX
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
int main()
{
SQLRETURN rc = 0;
SQLHENV henv = SQL_NULL_HENV;
rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
assert(rc == SQL_SUCCESS);
rc = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER*)SQL_OV_ODBC3_80, 0);
assert(rc == SQL_SUCCESS);
SQLHDBC hdbc = SQL_NULL_HDBC;
rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
assert(rc == SQL_SUCCESS);
char* cs = "Driver={ODBC Driver 17 for SQL Server};Server=myserver;Database=mydb;UID=myuser;PWD=mypwd;";
rc = SQLDriverConnect(hdbc, nullptr, (SQLCHAR*)cs, SQL_NTS, nullptr, 0, nullptr, SQL_DRIVER_NOPROMPT);
assert(rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO);
SQLHSTMT hstmt = SQL_NULL_HSTMT;
rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
assert(rc == SQL_SUCCESS);
rc = SQLExecDirect(hstmt, (SQLCHAR*)"DROP TABLE IF EXISTS [t_bit]", SQL_NTS);
assert(rc == SQL_SUCCESS);
rc = SQLExecDirect(hstmt, (SQLCHAR*)"CREATE TABLE [t_bit]([fid] [int] IDENTITY(1,1) NOT NULL,[b] [bit] NULL)", SQL_NTS);
assert(rc == SQL_SUCCESS);
// NOTE: 22003 failures happen for any negative or positive value other than j0 or 1
// Originally, the issue was discovered for -1 which is raw value of VARIANT_TRUE
SQLINTEGER bit_value = -1;
// Case 0: SQL State=22003 Error=[Microsoft][ODBC Driver 17 for SQL Server]Numeric value out of range
//rc = SQLPrepare(hstmt, (SQLCHAR*)"INSERT INTO [t_bit] ([b]) VALUES (?)", SQL_NTS);
// Case 1: SQL State=22003 Error=[Microsoft][ODBC Driver 17 for SQL Server]Numeric value out of range
rc = SQLPrepare(hstmt, (SQLCHAR*)"INSERT INTO [t_bit] ([b]) VALUES (CAST(? AS BIT))", SQL_NTS);
// Case 2: OK
//rc = SQLPrepare(hstmt, (SQLCHAR*)"INSERT INTO [t_bit] ([b]) VALUES (CAST(CAST(? AS INT) AS BIT))", SQL_NTS);
// Case 3: OK
//rc = SQLPrepare(hstmt, (SQLCHAR*)"INSERT INTO [t_bit] ([b]) VALUES (CAST(ABS(?) AS BIT))", SQL_NTS);
assert(rc == SQL_SUCCESS);
SQLSMALLINT p_type{0}, p_scale{0}, p_nullable{0};
SQLULEN p_size = 0;
rc = SQLDescribeParam(hstmt, 1, &p_type, &p_size, &p_scale, &p_nullable);
SQLLEN cb_value = 0;
rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, p_type, p_size, p_scale, &bit_value, p_size, &cb_value);
assert(rc == SQL_SUCCESS);
rc = SQLExecute(hstmt);
if (rc != SQL_SUCCESS)
{
SQLCHAR s[6] = {0};
SQLINTEGER e = 0;
SQLSMALLINT b = 0;
rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, s, &e, 0, 0,& b);
assert(rc == SQL_SUCCESS);
std::vector<SQLCHAR> v; v.resize(b > 0 ? b + 1 : 512);
rc = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, s, &e, v.data(), v.size(),& b);
assert(rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO);
std::string m(v.begin(), v.end());
std::cout << "SQL State=" << s << "\tError=" << m << std::endl;
}
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment