ODBC SQLBindCol 绑定 ODBC 浮点类型以返回为 std::string

发布于 2025-01-10 10:40:48 字数 6362 浏览 1 评论 0原文

我正在存储一个数据库记录,其中包含与 SQL 查询一起存储的浮点字段,

create table test_table_3 (a float, b varchar(10));
insert into test_table_3 VALUES (1.4, 'foo');

我想定义一个函数,该函数将记录的值返回为 std::string。

附加程序执行此操作,但返回字符串值为“1.39999”。有没有办法将 ODBC API 设置为返回与插入的值相同的值(“1.4”)。 (这只是因为需要在将输入与输出值匹配的测试中使用代码)

我正在使用 SQLBindCol ODBC 函数,用于将程序中的“TargetType”参数(C 数据类型的标识符)定义为

bind_data[idx].target_type = SQL_C_CHAR;

ODBC C数据类型

完整的程序是:

#ifdef _MSC_VER
#include <windows.h>
#endif
#include <sql.h>
#include <sqlext.h>
#include <string>
#include <iostream>

struct bind_column_data_t
{
  SQLSMALLINT target_type; //the C data type of the result data
  SQLPOINTER target_value_ptr; //pointer to storage for the data
  SQLINTEGER buf_len; //maximum length of the buffer being bound for data (including null-termination byte for char)
  SQLLEN strlen_or_ind; //number of bytes(excluding the null termination byte for character data) available
  //to return in the buffer prior to calling SQLFetch
};

const std::string conn("Driver={SQL Server};Server=localhost;Database=test_db;");
int exec_direct(SQLHSTMT hstmt, const std::string& sql);
int fetch(SQLHSTMT hstmt, const std::string& sql);

/////////////////////////////////////////////////////////////////////////////////////////////////////
//main
/////////////////////////////////////////////////////////////////////////////////////////////////////

int main()
{
  SQLHENV  henv = SQL_NULL_HENV; //environment
  SQLHDBC  hdbc = SQL_NULL_HDBC; //connection handle
  SQLHSTMT hstmt = SQL_NULL_HSTMT; //statement handle

  std::string sql1 = "drop table if exists test_table_3;";
  std::string sql2 = "create table test_table_3 (a float, b varchar(10));";
  std::string sql3 = "insert into test_table_3 VALUES (1.4, 'foo');";
  std::string sql4 = "select * from[test_table_3];";

  if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv)))
  {
  }
  if (!SQL_SUCCEEDED(SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0)))
  {
  }
  if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc)))
  {
  }
  if (!SQL_SUCCEEDED(SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)TRUE, 0)))
  {
  }
  if (!SQL_SUCCEEDED(SQLDriverConnect(hdbc, NULL, (SQLCHAR*)conn.c_str(), SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE)))
  {
    return -1;
  }
  if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt)))
  {
  }

  exec_direct(hstmt, sql1);
  exec_direct(hstmt, sql2);
  exec_direct(hstmt, sql3);
  fetch(hstmt, sql4);

  SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  SQLDisconnect(hdbc);
  SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
  SQLFreeHandle(SQL_HANDLE_ENV, henv);
  return 0;

  return 0;
}


int exec_direct(SQLHSTMT hstmt, const std::string& sql)
{
  if (!SQL_SUCCEEDED(SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), SQL_NTS)))
  {
    return -1;
  }
  return 0;
}

int fetch(SQLHSTMT hstmt, const std::string& sql)
{
  SQLSMALLINT nbr_cols;
  SQLCHAR* sqlstr = (SQLCHAR*)sql.c_str();
  struct bind_column_data_t* bind_data = NULL;

  if (!SQL_SUCCEEDED(SQLExecDirect(hstmt, sqlstr, SQL_NTS)))
  {
    return -1;
  }

  if (!SQL_SUCCEEDED(SQLNumResultCols(hstmt, &nbr_cols)))
  {
  }

  bind_data = (bind_column_data_t*)malloc(nbr_cols * sizeof(bind_column_data_t));
  for (SQLUSMALLINT idx = 0; idx < nbr_cols; idx++)
  {
    bind_data[idx].target_value_ptr = NULL;
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////////
  //get column names
  /////////////////////////////////////////////////////////////////////////////////////////////////////

  for (SQLUSMALLINT idx = 0; idx < nbr_cols; idx++)
  {
    SQLCHAR buf[1024];
    SQLSMALLINT sqltype = 0;
    SQLSMALLINT scale = 0;
    SQLSMALLINT nullable = 0;
    SQLSMALLINT len = 0;
    SQLULEN sqlsize = 0;

    if (!SQL_SUCCEEDED(SQLDescribeCol(
      hstmt,
      idx + 1,
      (SQLCHAR*)buf, //column name
      sizeof(buf) / sizeof(SQLCHAR),
      &len,
      &sqltype,
      &sqlsize,
      &scale,
      &nullable)))
    {
    }
  }

  for (SQLUSMALLINT idx = 0; idx < nbr_cols; idx++)
  {
    bind_data[idx].target_type = SQL_C_CHAR;
    bind_data[idx].buf_len = (1024 + 1);
    bind_data[idx].target_value_ptr = malloc(sizeof(unsigned char) * bind_data[idx].buf_len);
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////////
  //SQLBindCol assigns the storage and data type for a column in a result set,
  //including:
  //a storage buffer that will receive the contents of a column of data
  //the length of the storage buffer
  //a storage location that will receive the actual length of the column of data
  //returned by the fetch operation data type conversion
  /////////////////////////////////////////////////////////////////////////////////////////////////////

  for (SQLUSMALLINT idx = 0; idx < nbr_cols; idx++)
  {
    if (!SQL_SUCCEEDED(SQLBindCol(
      hstmt,
      idx + 1,
      bind_data[idx].target_type,
      bind_data[idx].target_value_ptr,
      bind_data[idx].buf_len,
      &(bind_data[idx].strlen_or_ind))))
    {
    }
  }

  size_t nbr_rows = 0;
  while (SQL_SUCCEEDED(SQLFetch(hstmt)))
  {
    for (int idx_col = 0; idx_col < nbr_cols; idx_col++)
    {
      std::string str;
      if (bind_data[idx_col].strlen_or_ind != SQL_NULL_DATA)
      {
        str = (char*)bind_data[idx_col].target_value_ptr;
      }
      else
      {
        str = "NULL";
      }
      std::cout << str << " ";
    }
    std::cout << "\n";
    nbr_rows++;
  }

  for (SQLUSMALLINT idx_col = 0; idx_col < nbr_cols; idx_col++)
  {
    if (bind_data[idx_col].target_value_ptr != NULL)
    {
      free(bind_data[idx_col].target_value_ptr);
    }
  }
  if (bind_data != NULL)
  {
    free(bind_data);
  }
  SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  return 0;
}

输出是:

1.3999999999999999 foo

I am storing a database record with a float field stored with the SQL queries

create table test_table_3 (a float, b varchar(10));
insert into test_table_3 VALUES (1.4, 'foo');

I want to define a function that returns the values of the records as std::string.

The attached program does that, but the return value as string is "1.39999". Would there be a way to set the ODBC API to return the same value as inserted ("1.4"). (This is just because the code needs to be used in a test that matches input with output values)

I am using SQLBindCol ODBC function to define the "TargetType" parameter (identifier of the C data type) in the program as

bind_data[idx].target_type = SQL_C_CHAR;

ODBC C Data Types

Complete program is:

#ifdef _MSC_VER
#include <windows.h>
#endif
#include <sql.h>
#include <sqlext.h>
#include <string>
#include <iostream>

struct bind_column_data_t
{
  SQLSMALLINT target_type; //the C data type of the result data
  SQLPOINTER target_value_ptr; //pointer to storage for the data
  SQLINTEGER buf_len; //maximum length of the buffer being bound for data (including null-termination byte for char)
  SQLLEN strlen_or_ind; //number of bytes(excluding the null termination byte for character data) available
  //to return in the buffer prior to calling SQLFetch
};

const std::string conn("Driver={SQL Server};Server=localhost;Database=test_db;");
int exec_direct(SQLHSTMT hstmt, const std::string& sql);
int fetch(SQLHSTMT hstmt, const std::string& sql);

/////////////////////////////////////////////////////////////////////////////////////////////////////
//main
/////////////////////////////////////////////////////////////////////////////////////////////////////

int main()
{
  SQLHENV  henv = SQL_NULL_HENV; //environment
  SQLHDBC  hdbc = SQL_NULL_HDBC; //connection handle
  SQLHSTMT hstmt = SQL_NULL_HSTMT; //statement handle

  std::string sql1 = "drop table if exists test_table_3;";
  std::string sql2 = "create table test_table_3 (a float, b varchar(10));";
  std::string sql3 = "insert into test_table_3 VALUES (1.4, 'foo');";
  std::string sql4 = "select * from[test_table_3];";

  if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv)))
  {
  }
  if (!SQL_SUCCEEDED(SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0)))
  {
  }
  if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc)))
  {
  }
  if (!SQL_SUCCEEDED(SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)TRUE, 0)))
  {
  }
  if (!SQL_SUCCEEDED(SQLDriverConnect(hdbc, NULL, (SQLCHAR*)conn.c_str(), SQL_NTS, NULL, 0, NULL, SQL_DRIVER_COMPLETE)))
  {
    return -1;
  }
  if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt)))
  {
  }

  exec_direct(hstmt, sql1);
  exec_direct(hstmt, sql2);
  exec_direct(hstmt, sql3);
  fetch(hstmt, sql4);

  SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  SQLDisconnect(hdbc);
  SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
  SQLFreeHandle(SQL_HANDLE_ENV, henv);
  return 0;

  return 0;
}


int exec_direct(SQLHSTMT hstmt, const std::string& sql)
{
  if (!SQL_SUCCEEDED(SQLExecDirect(hstmt, (SQLCHAR*)sql.c_str(), SQL_NTS)))
  {
    return -1;
  }
  return 0;
}

int fetch(SQLHSTMT hstmt, const std::string& sql)
{
  SQLSMALLINT nbr_cols;
  SQLCHAR* sqlstr = (SQLCHAR*)sql.c_str();
  struct bind_column_data_t* bind_data = NULL;

  if (!SQL_SUCCEEDED(SQLExecDirect(hstmt, sqlstr, SQL_NTS)))
  {
    return -1;
  }

  if (!SQL_SUCCEEDED(SQLNumResultCols(hstmt, &nbr_cols)))
  {
  }

  bind_data = (bind_column_data_t*)malloc(nbr_cols * sizeof(bind_column_data_t));
  for (SQLUSMALLINT idx = 0; idx < nbr_cols; idx++)
  {
    bind_data[idx].target_value_ptr = NULL;
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////////
  //get column names
  /////////////////////////////////////////////////////////////////////////////////////////////////////

  for (SQLUSMALLINT idx = 0; idx < nbr_cols; idx++)
  {
    SQLCHAR buf[1024];
    SQLSMALLINT sqltype = 0;
    SQLSMALLINT scale = 0;
    SQLSMALLINT nullable = 0;
    SQLSMALLINT len = 0;
    SQLULEN sqlsize = 0;

    if (!SQL_SUCCEEDED(SQLDescribeCol(
      hstmt,
      idx + 1,
      (SQLCHAR*)buf, //column name
      sizeof(buf) / sizeof(SQLCHAR),
      &len,
      &sqltype,
      &sqlsize,
      &scale,
      &nullable)))
    {
    }
  }

  for (SQLUSMALLINT idx = 0; idx < nbr_cols; idx++)
  {
    bind_data[idx].target_type = SQL_C_CHAR;
    bind_data[idx].buf_len = (1024 + 1);
    bind_data[idx].target_value_ptr = malloc(sizeof(unsigned char) * bind_data[idx].buf_len);
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////////
  //SQLBindCol assigns the storage and data type for a column in a result set,
  //including:
  //a storage buffer that will receive the contents of a column of data
  //the length of the storage buffer
  //a storage location that will receive the actual length of the column of data
  //returned by the fetch operation data type conversion
  /////////////////////////////////////////////////////////////////////////////////////////////////////

  for (SQLUSMALLINT idx = 0; idx < nbr_cols; idx++)
  {
    if (!SQL_SUCCEEDED(SQLBindCol(
      hstmt,
      idx + 1,
      bind_data[idx].target_type,
      bind_data[idx].target_value_ptr,
      bind_data[idx].buf_len,
      &(bind_data[idx].strlen_or_ind))))
    {
    }
  }

  size_t nbr_rows = 0;
  while (SQL_SUCCEEDED(SQLFetch(hstmt)))
  {
    for (int idx_col = 0; idx_col < nbr_cols; idx_col++)
    {
      std::string str;
      if (bind_data[idx_col].strlen_or_ind != SQL_NULL_DATA)
      {
        str = (char*)bind_data[idx_col].target_value_ptr;
      }
      else
      {
        str = "NULL";
      }
      std::cout << str << " ";
    }
    std::cout << "\n";
    nbr_rows++;
  }

  for (SQLUSMALLINT idx_col = 0; idx_col < nbr_cols; idx_col++)
  {
    if (bind_data[idx_col].target_value_ptr != NULL)
    {
      free(bind_data[idx_col].target_value_ptr);
    }
  }
  if (bind_data != NULL)
  {
    free(bind_data);
  }
  SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  return 0;
}

Output is :

1.3999999999999999 foo

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

季末如歌 2025-01-17 10:40:48

因为这是针对 MS SQL Server 的,所以可以使用 STR 函数,长度为3个字符,精度为1,精确匹配字符串“1.4”

INSERT INTO test_table_3 VALUES (1.4, 'foo');
SELECT STR(a,3,1), b FROM[test_table_3];

输出

1.4 foo

Because this is for MS SQL Server, it's possible to use the STR function with a length of 3 characters and precision of 1, to match exactly the string "1.4"

INSERT INTO test_table_3 VALUES (1.4, 'foo');
SELECT STR(a,3,1), b FROM[test_table_3];

Output

1.4 foo
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文