Skip to content

Commit

Permalink
Fixing issues when binding strings using s.c_str().
Browse files Browse the repository at this point in the history
  • Loading branch information
lexicalunit committed Jan 20, 2014
1 parent ef957d0 commit f024f84
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 55 deletions.
141 changes: 87 additions & 54 deletions nanodbc.cpp
Expand Up @@ -1083,7 +1083,7 @@ class statement::statement_impl

// calls actual ODBC bind parameter function
template<class T>
void bind_parameter(short param, const T* data, SQLSMALLINT data_type, SQLSMALLINT param_type, SQLULEN parameter_size)
void bind_parameter(short param, const T* data, std::size_t elements, SQLSMALLINT data_type, SQLSMALLINT param_type, SQLULEN parameter_size)
{
RETCODE rc;
NANODBC_CALL_RC(
Expand All @@ -1099,24 +1099,14 @@ class statement::statement_impl
, (SQLPOINTER)data // parameter value
, parameter_size // buffer length
, bind_len_or_null_[param].data());

if(!success(rc))
NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
}

// handles a single value (possibly a single string value), or multiple non-string values
template<class T>
void bind(short param, const T* values, std::size_t elements, param_direction direction)
{
SQLSMALLINT data_type;
SQLSMALLINT param_type;
SQLULEN parameter_size;
prepare_bind(param, elements, direction, data_type, param_type, parameter_size);

for(std::size_t i = 0; i < elements; ++i)
bind_len_or_null_[param][i] = parameter_size;

bind_parameter(param, values, data_type, param_type, parameter_size);
}
void bind(short param, const T* values, std::size_t elements, param_direction direction);

// handles multiple string values
void bind_strings(short param, const string_type::value_type* values, std::size_t length, std::size_t elements, param_direction direction)
Expand Down Expand Up @@ -1159,49 +1149,10 @@ class statement::statement_impl

// handles multiple non-string values with a null sentry
template<class T>
void bind(short param, const T* values, std::size_t elements, const bool* nulls, const T* null_sentry, param_direction direction)
{
SQLSMALLINT data_type;
SQLSMALLINT param_type;
SQLULEN parameter_size;
prepare_bind(param, elements, direction, data_type, param_type, parameter_size);

for(std::size_t i = 0; i < elements; ++i)
if((null_sentry && !equals(values[i], *null_sentry)) || (nulls && !nulls[i]))
bind_len_or_null_[param][i] = parameter_size;

bind_parameter(param, values, data_type, param_type, parameter_size);
}
void bind(short param, const T* values, std::size_t elements, const bool* nulls, const T* null_sentry, param_direction direction);

// handles multiple string values
void bind_strings(short param, const string_type::value_type* values, std::size_t length, std::size_t elements, const bool* nulls, const string_type::value_type* null_sentry, param_direction direction)
{
SQLSMALLINT data_type;
SQLSMALLINT param_type;
SQLULEN parameter_size;
prepare_bind(param, elements, direction, data_type, param_type, parameter_size);

if(null_sentry)
{
const string_type rhs(null_sentry);
for(std::size_t i = 0; i < elements; ++i)
{
const string_type lhs(values + i * length, values + (i + 1) * length);
if(NANADBC_STRNCMP(lhs.c_str(), rhs.c_str(), length))
bind_len_or_null_[param][i] = parameter_size;
}
}
else if(nulls)
{
for(std::size_t i = 0; i < elements; ++i)
{
if(!nulls[i])
bind_len_or_null_[param][i] = parameter_size;
}
}

bind_parameter(param, values, data_type, param_type, parameter_size);
}
void bind_strings(short param, const string_type::value_type* values, std::size_t length, std::size_t elements, const bool* nulls, const string_type::value_type* null_sentry, param_direction direction);

private:
statement_impl(const statement_impl&); // not defined
Expand All @@ -1214,6 +1165,88 @@ class statement::statement_impl
std::map<short, std::vector<null_type> > bind_len_or_null_;
};

// Supports code like: query.bind(0, std_string.c_str())
// In this case, we need to pass NULL to the final parameter of SQLBindParameter().
template<>
void statement::statement_impl::bind_parameter<string_type::value_type>(short param, const string_type::value_type* data, std::size_t elements, SQLSMALLINT data_type, SQLSMALLINT param_type, SQLULEN parameter_size)
{
RETCODE rc;
NANODBC_CALL_RC(
SQLBindParameter
, rc
, stmt_ // handle
, param + 1 // parameter number
, param_type // input or output type
, sql_type_info<string_type::value_type>::ctype // value type
, data_type // parameter type
, parameter_size // column size ignored for many types, but needed for strings
, 0 // decimal digits
, (SQLPOINTER)data // parameter value
, parameter_size // buffer length
, (elements <= 1 ? NULL : bind_len_or_null_[param].data()));

if(!success(rc))
NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
}

template<class T>
void statement::statement_impl::bind(short param, const T* values, std::size_t elements, param_direction direction)
{
SQLSMALLINT data_type;
SQLSMALLINT param_type;
SQLULEN parameter_size;
prepare_bind(param, elements, direction, data_type, param_type, parameter_size);

for(std::size_t i = 0; i < elements; ++i)
bind_len_or_null_[param][i] = parameter_size;

bind_parameter(param, values, elements, data_type, param_type, parameter_size);
}

template<class T>
void statement::statement_impl::bind(short param, const T* values, std::size_t elements, const bool* nulls, const T* null_sentry, param_direction direction)
{
SQLSMALLINT data_type;
SQLSMALLINT param_type;
SQLULEN parameter_size;
prepare_bind(param, elements, direction, data_type, param_type, parameter_size);

for(std::size_t i = 0; i < elements; ++i)
if((null_sentry && !equals(values[i], *null_sentry)) || (nulls && !nulls[i]))
bind_len_or_null_[param][i] = parameter_size;

bind_parameter(param, values, elements, data_type, param_type, parameter_size);
}

void statement::statement_impl::bind_strings(short param, const string_type::value_type* values, std::size_t length, std::size_t elements, const bool* nulls, const string_type::value_type* null_sentry, param_direction direction)
{
SQLSMALLINT data_type;
SQLSMALLINT param_type;
SQLULEN parameter_size;
prepare_bind(param, elements, direction, data_type, param_type, parameter_size);

if(null_sentry)
{
const string_type rhs(null_sentry);
for(std::size_t i = 0; i < elements; ++i)
{
const string_type lhs(values + i * length, values + (i + 1) * length);
if(NANADBC_STRNCMP(lhs.c_str(), rhs.c_str(), length))
bind_len_or_null_[param][i] = parameter_size;
}
}
else if(nulls)
{
for(std::size_t i = 0; i < elements; ++i)
{
if(!nulls[i])
bind_len_or_null_[param][i] = parameter_size;
}
}

bind_parameter(param, values, elements, data_type, param_type, parameter_size);
}

template<>
bool statement::statement_impl::equals(const date& lhs, const date& rhs)
{
Expand Down
57 changes: 56 additions & 1 deletion test/sqlite_test.cpp
Expand Up @@ -24,12 +24,24 @@ struct sqlite_fixture

~sqlite_fixture()
{
// system("rm nanodbc.db");
std::remove("nanodbc.db");
}

int i;
};

typedef boost::mpl::list<
string_type::value_type
, short
, unsigned short
, int32_t
, uint32_t
, int64_t
, uint64_t
, float
, double
> integral_test_types;

BOOST_FIXTURE_TEST_SUITE(s, sqlite_fixture)

BOOST_AUTO_TEST_CASE(simple_test)
Expand Down Expand Up @@ -102,4 +114,47 @@ BOOST_AUTO_TEST_CASE(simple_test)
BOOST_CHECK(!connection_copy.connected());
}

BOOST_AUTO_TEST_CASE(string_test)
{
nanodbc::connection connection = connect();
BOOST_CHECK(connection.connected());
BOOST_CHECK(connection.native_dbc_handle());
BOOST_CHECK(connection.native_env_handle());
BOOST_CHECK_EQUAL(connection.transactions(), 0);
BOOST_CHECK_EQUAL(connection.driver_name(), "sqlite3odbc.so");

const std::string name = "Fred";

execute(connection, "drop table if exists string_test;");
execute(connection, "create table string_test (s varchar(10));");

nanodbc::statement query(connection);
prepare(query, "insert into string_test(s) values(?)");
query.bind(0, name.c_str());
nanodbc::execute(query);

nanodbc::result results = execute(connection, "select s from string_test;");
BOOST_CHECK(results.next());
BOOST_CHECK_EQUAL(results.get<std::string>(0), "Fred");
}

BOOST_AUTO_TEST_CASE_TEMPLATE(integral_test, T, integral_test_types)
{
nanodbc::connection connection = connect();
BOOST_CHECK(connection.connected());

execute(connection, "drop table if exists simple_test;");
execute(connection, "create table simple_test (a int, b varchar(10));");

nanodbc::statement statement(connection);
prepare(statement, "insert into simple_test (a, b) values (?, ?);");
const int eight_int = 8;
statement.bind(0, &eight_int);
const string eight_str = "eight";
statement.bind(1, eight_str.c_str());

BOOST_CHECK(statement.connected());
execute(statement);
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit f024f84

Please sign in to comment.