Project

General

Profile

Bug #9055 ยป wt.db2.diff

Torsten Schulz, 09/09/2021 04:43 PM

View differences:

wt-4.5.0-patched/CMakeLists.txt 2021-04-20 16:48:29.573236713 +0200
OPTION(ENABLE_FIREBIRD "Build FirebirdSQL backend for Wt::Dbo" ON)
OPTION(ENABLE_MYSQL "Build mariadb/mysql backend for Wt::Dbo" ON)
OPTION(ENABLE_MSSQLSERVER "Build Microsoft SQL Server backend for Wt::Dbo" ON)
OPTION(ENABLE_DB2 "Build DB2 backend for Wt::DBO" ON)
OPTION(ENABLE_QT4 "Build Qt4 interworking library (libwtwithqt)" ON)
OPTION(ENABLE_QT5 "Build Qt5 interworking library (libwtwithqt5)" ON)
OPTION(ENABLE_LIBWTTEST "Build Wt::Test" ON)
wt-4.5.0-patched/src/Wt/Auth/Dbo/UserDatabase.h 2021-04-20 16:48:27.409255910 +0200
session_.query<Wt::Dbo::ptr<DboType> >(std::string() +
"select u from " + session_.tableNameQuoted<DboType>() + " u "
"join " + session_.tableNameQuoted<AuthIdentityType>() + " i "
"on u.id = i.\"" + session_.tableName<DboType>() + "_id\"")
"on u.\"id\" = i.\"" + session_.tableName<DboType>() + "_id\"")
.where("i.\"provider\" = ?").bind(provider);
if (authService_ && authService_->identityPolicy() == IdentityPolicy::EmailAddress) {
query.where("lower(i.\"identity\") = lower(?)").bind(identity);
......
(std::string() +
"select u from " + session_.tableNameQuoted<DboType>() + " u "
"join " + session_.tableNameQuoted<AuthTokenType>() + " t "
"on u.id = t.\"" + session_.tableName<DboType>() + "_id\"")
"on u.\"id\" = t.\"" + session_.tableName<DboType>() + "_id\"")
.where("t.\"value\" = ?").bind(hash)
.where("t.\"expires\" > ?").bind(WDateTime::currentDateTime()));
t.commit();
wt-4.5.0-patched/src/Wt/Dbo/backend/CMakeLists.txt 2021-04-20 16:48:27.289256974 +0200
MESSAGE("** Wt::Dbo: not building Microsoft SQL Server backend.")
SET(HAVE_MSSQLSERVER OFF CACHE INTERNAL "building Microsoft SQL Server backend" FORCE)
ENDIF(ENABLE_MSSQLSERVER AND ODBC_FOUND)
IF(ENABLE_DB2 AND ODBC_FOUND)
SET(HAVE_DB2 ON CACHE INTERNAL "building DB2 backend" FORCE)
MESSAGE("** Wt::Dbo: building DB2 backend.")
IF(WIN32)
IF(SHARED_LIBS)
CONFIGURE_FILE(wtdbodb2-version.rc.in
${CMAKE_CURRENT_BINARY_DIR}/wtdbodb2-version.rc)
SET(db2_SRCS ${db2_SRCS}
${CMAKE_CURRENT_BINARY_DIR}/wtdbodb2-version.rc)
ENDIF(SHARED_LIBS)
ENDIF(WIN32)
ADD_LIBRARY(wtdbodb2
DB2.h DB2.C
${db2_SRCS}
)
SET_PROPERTY(TARGET wtdbodb2 PROPERTY C_VISIBILITY_PRESET hidden)
SET_PROPERTY(TARGET wtdbodb2 PROPERTY CXX_VISIBILITY_PRESET hidden)
SET_PROPERTY(TARGET wtdbodb2 PROPERTY VISIBILITY_INLINES_HIDDEN YES)
TARGET_LINK_LIBRARIES(wtdbodb2
PUBLIC
wtdbo
PRIVATE
${ODBC_LIBRARIES}
)
INCLUDE_DIRECTORIES(${ODBC_INCLUDE})
INSTALL(TARGETS wtdbodb2
EXPORT wt-target-wtdbodb2
RUNTIME DESTINATION bin
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
INSTALL(EXPORT wt-target-wtdbodb2
DESTINATION ${CMAKE_INSTALL_DIR}/wt
NAMESPACE Wt::)
SET_TARGET_PROPERTIES(
wtdbodb2
PROPERTIES
EXPORT_NAME DboDB2
VERSION ${VERSION_SERIES}.${VERSION_MAJOR}.${VERSION_MINOR}
DEBUG_POSTFIX ${DEBUG_LIB_POSTFIX}
)
IF(MSVC)
SET_TARGET_PROPERTIES(
wtdbodb2
PROPERTIES
COMPILE_FLAGS "${BUILD_PARALLEL} /wd4251 /wd4275 /wd4267"
)
SET_TARGET_PROPERTIES(wtdbodb2 PROPERTIES FOLDER "dbo")
endif(MSVC)
INSTALL_FILES(/include/Wt/Dbo/backend "^.*DB2.*h$")
ELSE(ENABLE_DB2 AND ODBC_FOUND)
MESSAGE("** Wt::Dbo: not building DB2 backend.")
IF(ENABLE_DB2)
MESSAGE(" ODBC not found")
ENDIF(ENABLE_DB2)
SET(HAVE_DB2 OFF CACHE INTERNAL "building DB2 backend" FORCE)
ENDIF(ENABLE_DB2 AND ODBC_FOUND)
wt-4.5.0-patched/src/Wt/Dbo/backend/DB2.C 2021-04-20 16:48:27.289256974 +0200
/*
* Copyright (C) 2017 Emweb bv, Herent, Belgium.
*
* See the LICENSE file for terms of use.
*/
#include "Wt/Dbo/backend/DB2.h"
#include "Wt/Dbo/Exception.h"
#include "Wt/Dbo/Logger.h"
#include "Wt/Dbo/StringStream.h"
#include "Wt/Date/date.h"
#ifdef WT_WIN32
#define NOMINMAX
#include <windows.h>
#endif // WT_WIN32
#include <sql.h>
#include <sqlext.h>
#ifndef WT_WIN32
#include <sqlucode.h>
#include <codecvt>
#include <string>
#endif // WT_WIN32
#include <cstring>
#include <iostream>
#include <vector>
// define from sqlncli.h
// See: https://docs.microsoft.com/en-us/sql/relational-databases/native-client/features/using-multiple-active-result-sets-mars
#ifndef SQL_COPT_SS_MARS_ENABLED
#define SQL_COPT_SS_MARS_ENABLED 1224
#define SQL_MARS_ENABLED_YES 1L
#endif
namespace Wt {
namespace Dbo {
LOGGER("Dbo.backend.DB2Server");
namespace backend {
class DB2ServerException : public Exception
{
public:
DB2ServerException(const std::string& msg,
const std::string &sqlState = std::string())
: Exception(msg, sqlState)
{ }
};
namespace {
static void handleErr(SQLSMALLINT handleType, SQLHANDLE handle, SQLRETURN rc)
{
if (SQL_SUCCEEDED(rc))
return;
SQLCHAR sqlState[6];
SQLINTEGER nativeErr = 0;
SQLSMALLINT msgLength = 0;
SQLCHAR buf[SQL_MAX_MESSAGE_LENGTH];
SQLGetDiagRec(
handleType,
handle,
1,
sqlState,
&nativeErr,
buf,
sizeof(buf),
&msgLength
);
throw DB2ServerException(
std::string((const char*)buf, msgLength),
std::string((const char*)sqlState, 5));
}
#ifndef WT_WIN32
std::u16string toUTF16(const std::string &str)
{
return
std::wstring_convert<
std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(str.data());
}
#endif // WT_WIN32
#ifdef WT_WIN32
typedef std::basic_string<SQLWCHAR> ConnectionStringType;
#else // WT_WIN32
typedef std::u16string ConnectionStringType;
#endif // WT_WIN32
}
struct DB2::Impl {
Impl(const ConnectionStringType &str)
: env(NULL),
dbc(NULL),
stmt(NULL),
connectionString(str)
{
resultBuffer.buf = (char*)malloc(256);
resultBuffer.size = 256;
}
Impl(const Impl &other)
: env(NULL),
dbc(NULL),
stmt(NULL),
connectionString(other.connectionString)
{
resultBuffer.buf = (char*)malloc(256);
resultBuffer.size = 256;
}
~Impl()
{
free(resultBuffer.buf);
if (stmt)
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
if (dbc) {
SQLDisconnect(dbc);
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
}
if (env)
SQLFreeHandle(SQL_HANDLE_ENV, env);
}
void connect()
{
// Create SQL env handle
SQLRETURN res = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
if (res == SQL_ERROR)
throw DB2ServerException("Failed to allocate ODBC environment handle!");
// Set ODBC version to 3
res = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
handleErr(SQL_HANDLE_ENV, env, res);
// Create SQL connection handle
res = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
handleErr(SQL_HANDLE_ENV, env, res);
// Turn off autocommit
res = SQLSetConnectAttrW(dbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER);
handleErr(SQL_HANDLE_DBC, dbc, res);
// Turn on MARS (Multiple Active Result Sets)
// res = SQLSetConnectAttrW(dbc, SQL_COPT_SS_MARS_ENABLED, (SQLPOINTER)SQL_MARS_ENABLED_YES, SQL_IS_UINTEGER);
handleErr(SQL_HANDLE_DBC, dbc, res);
// Connect
res = SQLDriverConnectW(
dbc,
NULL,
(SQLWCHAR*)&(connectionString)[0],
connectionString.size(),
NULL,
0,
NULL,
SQL_DRIVER_NOPROMPT);
handleErr(SQL_HANDLE_DBC, dbc, res);
}
SQLHENV env; // Environment handle
SQLHDBC dbc; // Connection handle
SQLHSTMT stmt; // Statement handle for executeSql
ConnectionStringType connectionString;
struct ResultBuffer {
char *buf;
std::size_t size;
} resultBuffer;
};
class DB2ServerStatement : public SqlStatement {
public:
DB2ServerStatement(DB2 &conn, const std::string &sql)
: paramValues_(NULL),
parameterCount_(0),
resultColCount_(0),
stmt_(NULL),
conn_(conn),
sql_(sql),
affectedRows_(0),
lastId_(-1)
{
SQLRETURN rc = SQLAllocHandle(SQL_HANDLE_STMT, conn.impl_->dbc, &stmt_);
handleErr(SQL_HANDLE_DBC, conn.impl_->dbc, rc);
#ifdef WT_WIN32
if (sql.empty()) {
// Empty query, should be an error, but we'll leave the reporting
// of that error to ODBC
SQLWCHAR wstr[] = L"";
rc = SQLPrepareW(stmt_, wstr, 0);
} else {
int wstrlen = MultiByteToWideChar(CP_UTF8, 0, &sql[0], sql.size(), NULL, 0);
assert(wstrlen != 0);
SQLWCHAR *wstr = new SQLWCHAR[wstrlen + 1];
wstrlen = MultiByteToWideChar(CP_UTF8, 0, &sql[0], sql.size(), wstr, wstrlen);
assert(wstrlen != 0);
wstr[wstrlen] = 0;
rc = SQLPrepareW(stmt_, wstr, wstrlen);
delete[] wstr;
}
#else // WT_WIN32
std::u16string wstr = toUTF16(sql);
rc = SQLPrepareW(stmt_, (SQLWCHAR*)&wstr[0], wstr.size());
#endif // WT_WIN32
handleErr(SQL_HANDLE_STMT, stmt_, rc);
rc = SQLNumParams(stmt_, &parameterCount_);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
SQLSMALLINT numCols = 0;
rc = SQLNumResultCols(stmt_, &numCols);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
resultColCount_ = numCols;
if (parameterCount_ > 0) {
paramValues_ = new Value[parameterCount_];
std::memset(paramValues_, 0, parameterCount_ * sizeof(Value));
}
}
virtual ~DB2ServerStatement()
{
if (stmt_) {
SQLFreeStmt(stmt_, SQL_CLOSE);
SQLFreeHandle(SQL_HANDLE_STMT, stmt_);
}
for (SQLSMALLINT i = 0; i < parameterCount_; ++i)
paramValues_[i].clear();
delete[] paramValues_;
}
virtual void reset() override
{
SQLFreeStmt(stmt_, SQL_CLOSE);
}
virtual void bind(int column, const std::string &value) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
bool newPtr = true;
if (value.empty()) {
newPtr = createOrResizeBuffer(v, 0);
v.lengthOrInd = 0;
} else {
#ifdef WT_WIN32
// Convert value from UTF-8 to WCHAR (UTF-16)
// Measure length required
int bufsize = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), value.size(), NULL, 0);
assert(bufsize != 0);
newPtr = createOrResizeBuffer(v, (bufsize + 1) * sizeof(WCHAR));
bufsize = MultiByteToWideChar(CP_UTF8, 0, value.c_str(), value.size(), (WCHAR*)v.v.buf.p, bufsize);
assert(bufsize != 0);
((WCHAR*)v.v.buf.p)[bufsize] = 0;
v.lengthOrInd = bufsize * sizeof(WCHAR);
#else // WT_WIN32
newPtr = createOrResizeBuffer(v, value.size() + 1);
memcpy(v.v.buf.p, value.c_str(), value.size() + 1);
v.lengthOrInd = value.size();
#endif // WT_WIN32
}
if (newPtr || v.type != SQL_C_WCHAR) {
v.type = SQL_C_WCHAR;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
#ifdef WT_WIN32
/*ValueType: */SQL_C_WCHAR,
#else // WT_WIN32
/*ValueType: */SQL_C_CHAR, // UTF-8 to UTF-16 done by SQL Server driver
#endif // WT_WIN32
/*ParameterType: */SQL_WVARCHAR,
/*ColumnSize: */0, // Ignored
/*DecimalDigits: */0, // ignored
/*ParameterValuePtr: */v.v.buf.p,
/*BufferLength: */v.v.buf.size,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
virtual void bind(int column, short value) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
if (v.type == SQL_C_SSHORT) {
v.v.s = value;
v.lengthOrInd = 0;
} else {
v.clear();
v.v.s = value;
v.type = SQL_C_SSHORT;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_SSHORT,
/*ParameterType: */SQL_INTEGER,
/*ColumnSize: */0, // ignored
/*DecimalDigits: */0, // ignored
/*ParameterValuePtr: */&v.v.s,
/*BufferLength: */0,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
virtual void bind(int column, int value) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
if (v.type == SQL_C_SLONG) {
v.v.i = value;
v.lengthOrInd = 0;
} else {
v.clear();
v.v.i = value;
v.type = SQL_C_SLONG;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_SLONG,
/*ParameterType: */SQL_INTEGER,
/*Columnsize: */0, // seems ignored?
/*DecimalDigits: */0, // ignored
/*ParameterValuePtr: */&v.v.i,
/*BufferLength: */0,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
virtual void bind(int column, long long value) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
if (v.type == SQL_C_SBIGINT) {
v.v.ll = value;
v.lengthOrInd = 0;
} else {
v.clear();
v.v.ll = value;
v.type = SQL_C_SBIGINT;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_SBIGINT,
/*ParameterType: */SQL_BIGINT,
/*Columnsize: */0, // ignored
/*DecimalDigits: */0, // ignored
/*ParameterValuePtr: */&v.v.ll,
/*BufferLength: */0,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
virtual void bind(int column, float value) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
if (v.type == SQL_C_FLOAT) {
v.v.f = value;
v.lengthOrInd = 0;
} else {
v.clear();
v.v.f = value;
v.type = SQL_C_FLOAT;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_FLOAT,
/*ParameterType: */SQL_REAL,
/*Columnsize: */0, // seems ignored?
/*DecimalDigits: */0, // ignored
/*ParameterValuePtr: */&v.v.f,
/*BufferLength: */0,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
virtual void bind(int column, double value) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
if (v.type == SQL_C_DOUBLE) {
v.v.d = value;
v.lengthOrInd = 0;
} else {
v.clear();
v.v.d = value;
v.type = SQL_C_DOUBLE;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_DOUBLE,
/*ParameterType: */SQL_DOUBLE,
/*Columnsize: */0, // seems ignored?
/*DecimalDigits: */0, // ignored
/*ParameterValuePtr: */&v.v.d,
/*BufferLength: */0,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
virtual void bind(
int column,
const std::chrono::system_clock::time_point& value,
SqlDateTimeType type) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
if (type == SqlDateTimeType::Date) {
if (v.type != SQL_C_TYPE_DATE)
v.clear();
v.lengthOrInd = 0;
SQL_DATE_STRUCT &date = paramValues_[column].v.date;
auto daypoint = date::floor<date::days>(value);
auto ymd = date::year_month_day(daypoint);
date.year = (int)ymd.year();
date.month = (unsigned)ymd.month();
date.day = (unsigned)ymd.day();
if (v.type != SQL_C_TYPE_DATE) {
v.type = SQL_C_TYPE_DATE;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_TYPE_DATE,
/*ParameterType: */SQL_TYPE_DATE,
/*ColumnSize: */0,
/*DecimalDigits: */0,
/*ParameterValuePtr: */&date,
/*BufferLength: */0,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
} else {
if (v.type != SQL_C_TYPE_TIMESTAMP)
v.clear();
v.lengthOrInd = 0;
SQL_TIMESTAMP_STRUCT &ts = paramValues_[column].v.timestamp;
auto daypoint = date::floor<date::days>(value);
auto ymd = date::year_month_day(daypoint);
auto tod = date::make_time(value - daypoint);
ts.year = (int)ymd.year();
ts.month = (unsigned)ymd.month();
ts.day = (unsigned)ymd.day();
ts.hour = tod.hours().count();
ts.minute = tod.minutes().count();
ts.second = tod.seconds().count();
ts.fraction = std::chrono::nanoseconds(std::chrono::duration_cast<
std::chrono::duration<long long, std::ratio_multiply<std::ratio<100>, std::nano>>>(tod.subseconds())).count();
if (v.type != SQL_C_TYPE_TIMESTAMP) {
v.type = SQL_C_TYPE_TIMESTAMP;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_TYPE_TIMESTAMP,
/*ParameterType: */SQL_TYPE_TIMESTAMP,
/*ColumnSize: */0,
/*DecimalDigits: */7, // SQL Server limit: max 7 decimal digits
/*ParameterValuePtr: */&ts,
/*BufferLength: */0,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
}
virtual void bind(
int column,
const std::chrono::duration<int, std::milli>& value) override
{
long long msec = value.count();
bind(column, msec);
}
virtual void bind(
int column,
const std::vector<unsigned char>& value) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
bool newPtr = createOrResizeBuffer(v, value.size());
v.lengthOrInd = value.size();
memcpy(v.v.buf.p, value.data(), value.size());
if (newPtr || v.type != SQL_C_BINARY) {
v.type = SQL_C_BINARY;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_BINARY,
/*ParameterType: */SQL_VARBINARY,
/*ColumnSize: */0, // ignored
/*DecimalDigits: */0, // ignored
/*ParameterValuePtr: */v.v.buf.p,
/*BufferLength: */v.v.buf.size,
/*StrLen_or_IndPtr: */&v.lengthOrInd
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
virtual void bindNull(int column) override
{
checkColumnIndex(column);
Value &v = paramValues_[column];
if (v.type != 0)
v.lengthOrInd = SQL_NULL_DATA;
else {
v.clear();
v.type = SQL_C_CHAR;
v.v.buf.p = 0;
v.v.buf.size = SQL_NULL_DATA;
SQLRETURN rc = SQLBindParameter(
/*StatementHandle: */stmt_,
/*ParameterNumber: */column + 1,
/*InputOutputType: */SQL_PARAM_INPUT,
/*ValueType: */SQL_C_CHAR,
/*ParameterType: */SQL_VARCHAR,
/*Columnsize: */0,
/*DecimalDigits: */0,
/*ParameterValuePtr: */NULL,
/*BufferLength: */0,
/*StrLen_or_IndPtr: */&v.v.buf.size
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
}
virtual void execute() override
{
if (conn_.showQueries()) {
LOG_INFO(sql_);
}
SQLRETURN rc = SQLExecute(stmt_);
if (rc != SQL_NO_DATA) // SQL_NO_DATA can occur when no rows are affected
handleErr(SQL_HANDLE_STMT, stmt_, rc);
rc = SQLRowCount(stmt_, &affectedRows_);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
}
virtual long long insertedId() override
{
char *last_id = (char*)malloc(32);
char *sql;
SQLHANDLE hstmt;
SQLLEN out_length;
SQLAllocHandle(SQL_HANDLE_STMT, conn_.impl_->dbc, &hstmt);
sql = (char*)"SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1";
SQLExecDirect(hstmt, (SQLCHAR*)sql, strlen(sql));
SQLBindCol(hstmt, 1, SQL_C_CHAR, last_id, 32, &out_length);
SQLFetch(hstmt);
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
return std::atoi(last_id);
}
virtual int affectedRowCount() override
{
return static_cast<int>(affectedRows_);
}
virtual bool nextRow() override
{
SQLRETURN rc = SQLFetch(stmt_);
if (rc == SQL_NO_DATA)
return false;
else {
handleErr(SQL_HANDLE_STMT, stmt_, rc);
return true;
}
}
virtual int columnCount() const override
{
return resultColCount_;
}
virtual bool getResult(int column, std::string *value, int size) override
{
SQLCHAR colName[32];
SQLSMALLINT colNameLen;
SQLSMALLINT colType;
SQLULEN colSize;
SQLSMALLINT colScale;
SQLLEN colDataDisplaySize;
SQLDescribeCol(stmt_, (SQLSMALLINT)(column + 1), colName, sizeof(colName), &colNameLen, &colType, &colSize, &colScale, NULL);
SQLColAttribute(stmt_, (SQLSMALLINT)(column + 1), SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &colDataDisplaySize);
DB2::Impl::ResultBuffer &resultBuffer = conn_.impl_->resultBuffer;
std::size_t resultBufferPos = 0;
SQLLEN strLen_or_ind = SQL_NO_TOTAL;
SQLRETURN rc = SQLGetData(
stmt_,
column + 1,
#ifdef WT_WIN32
SQL_C_WCHAR,
#else // WT_WIN32
SQL_C_CHAR, // conversion from UTF-16 to UTF-8 done by driver
#endif // WT_WIN32
&resultBuffer.buf[resultBufferPos],
colSize,
&strLen_or_ind
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
if (strLen_or_ind == SQL_NULL_DATA) {
return false; // NULL
} else {
resultBuffer.buf = reinterpret_cast<char*>(resultBuffer.buf);
strLen_or_ind = strlen(resultBuffer.buf);
}
if (resultBufferPos == 0 && strLen_or_ind == 0) {
value->clear();
return true; // empty string
}
std::size_t totalDataSize = resultBufferPos + strLen_or_ind;
#ifdef WT_WIN32
int strlen = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)resultBuffer.buf, totalDataSize / sizeof(WCHAR), NULL, 0, NULL, NULL);
assert(strlen != 0);
value->clear();
value->resize(strlen);
strlen = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)resultBuffer.buf, totalDataSize / sizeof(WCHAR), &(*value)[0], strlen, NULL, NULL);
assert(strlen != 0);
#else // WT_WIN32
*value = std::string(resultBuffer.buf, totalDataSize);
#endif // WT_WIN32
return true;
}
virtual bool getResult(int column, short * value) override
{
return getRes<SQL_C_SSHORT>(column, value);
}
virtual bool getResult(int column, int * value) override
{
return getRes<SQL_C_SLONG>(column, value);
}
virtual bool getResult(int column, long long * value) override
{
return getRes<SQL_C_SBIGINT>(column, value);
}
virtual bool getResult(int column, float * value) override
{
return getRes<SQL_C_FLOAT>(column, value);
}
virtual bool getResult(int column, double * value) override
{
return getRes<SQL_C_DOUBLE>(column, value);
}
virtual bool getResult(
int column,
std::chrono::system_clock::time_point *value,
SqlDateTimeType type) override
{
if (type == SqlDateTimeType::Date) {
SQL_DATE_STRUCT date;
bool result = getRes<SQL_C_TYPE_DATE>(column, &date);
if (!result)
return false; // NULL
*value = date::sys_days{ date::year{ date.year } / date.month / date.day };
return true;
} else {
SQL_TIMESTAMP_STRUCT ts;
bool result = getRes<SQL_C_TYPE_TIMESTAMP>(column, &ts);
if (!result)
return false; // NULL
*value =
date::sys_days{ date::year{ ts.year } / ts.month / ts.day } +
std::chrono::duration_cast<std::chrono::system_clock::duration>(
std::chrono::hours{ ts.hour } +
std::chrono::minutes{ ts.minute } +
std::chrono::seconds{ ts.second } +
std::chrono::nanoseconds{ ts.fraction }
);
return true;
}
assert(false);
return false;
}
virtual bool getResult(
int column,
std::chrono::duration<int, std::milli> *value) override
{
long long msec;
bool res = getResult(column, &msec);
if (!res)
return res;
*value = std::chrono::duration<int, std::milli>(msec);
return true;
}
virtual bool getResult(
int column,
std::vector<unsigned char> *value,
int) override
{
DB2::Impl::ResultBuffer &resultBuffer = conn_.impl_->resultBuffer;
std::size_t resultBufferPos = 0;
SQLLEN strLen_or_ind = SQL_NO_TOTAL;
while (strLen_or_ind == SQL_NO_TOTAL) {
SQLRETURN rc = SQLGetData(
/*StatementHandle: */stmt_,
/*ColumnNumber: */column + 1,
/*TargetType: */SQL_C_BINARY,
/*TargetValue: */&resultBuffer.buf[resultBufferPos],
/*BufferLength: */resultBuffer.size - resultBufferPos,
/*StrLen_or_IndPtr: */&strLen_or_ind
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
if (strLen_or_ind == SQL_NULL_DATA) {
return false; // NULL
} else if (strLen_or_ind == SQL_NO_TOTAL || strLen_or_ind > resultBuffer.size - resultBufferPos) {
std::size_t pos = resultBuffer.size;
resultBuffer.size *= 2;
while (strLen_or_ind != SQL_NO_TOTAL &&
strLen_or_ind > resultBuffer.size - resultBufferPos)
resultBuffer.size *= 2;
resultBufferPos = pos;
resultBuffer.buf = (char*)realloc(resultBuffer.buf, resultBuffer.size);
strLen_or_ind = SQL_NO_TOTAL;
}
}
std::size_t totalDataSize = resultBufferPos + strLen_or_ind;
*value = std::vector<unsigned char>(resultBuffer.buf, resultBuffer.buf + totalDataSize);
return true;
}
virtual std::string sql() const override
{
return sql_;
}
private:
struct Value {
union {
struct {
void *p; // buffer pointer
SQLLEN size; // buffer size
} buf;
short s;
int i;
long long ll;
float f;
double d;
SQL_DATE_STRUCT date;
SQL_TIME_STRUCT time;
SQL_TIMESTAMP_STRUCT timestamp;
} v;
SQLLEN lengthOrInd; // length for binary data or string or NULL indicator
SQLSMALLINT type; // SQL_C... type
Value()
{
std::memset(&v, 0, sizeof(v));
lengthOrInd = 0;
type = 0;
}
~Value()
{
if (type == SQL_C_BINARY ||
type == SQL_C_WCHAR)
free(v.buf.p);
}
#ifdef WT_CXX11
Value(const Value &other) = delete;
Value &operator=(const Value &other) = delete;
Value(Value &&other) = delete;
Value &operator=(Value &&other) = delete;
#endif // WT_CXX11
void clear()
{
if (type == SQL_C_BINARY ||
type == SQL_C_WCHAR)
free(v.buf.p);
std::memset(&v, 0, sizeof(v));
lengthOrInd = 0;
type = 0;
}
private:
#ifndef WT_CXX11
Value(const Value &other);
Value& operator=(const Value &other);
#endif // WT_CXX11
};
Value *paramValues_;
SQLSMALLINT parameterCount_;
SQLSMALLINT resultColCount_;
SQLHSTMT stmt_;
DB2 &conn_;
std::string sql_;
SQLLEN affectedRows_;
long long lastId_;
void checkColumnIndex(int column)
{
if (column >= parameterCount_)
throw DB2ServerException(
std::string("Trying to bind too many parameters (parameter count = ") +
std::to_string(parameterCount_) +
", column = " +
std::to_string(column) +
std::string(")"));
}
// bool returns whether buffer changed
bool createOrResizeBuffer(Value &v, SQLLEN size)
{
if (v.type == SQL_C_WCHAR || v.type == SQL_C_BINARY) {
// We already have a buffer
if (v.v.buf.size >= size)
return false;
v.v.buf.size = size;
v.v.buf.p = realloc(v.v.buf.p, v.v.buf.size);
return true;
}
else {
// New buffer
v.clear();
v.v.buf.size = size == 0 ? 1 : size;
v.v.buf.p = malloc(v.v.buf.size);
return true;
}
}
template<SQLSMALLINT TargetType, typename ReturnType>
bool getRes(int column, ReturnType * value)
{
SQLLEN strLen_or_ind = 0;
SQLRETURN rc = SQLGetData(
/*StatementHandle: */stmt_,
/*ColumnNumber: */column + 1,
/*TargetType: */TargetType,
/*TargetValue: */value,
/*BufferLength: */0,
/*StrLen_or_IndPtr*/&strLen_or_ind
);
handleErr(SQL_HANDLE_STMT, stmt_, rc);
return rc == 0;
}
};
DB2::DB2()
: impl_(0)
{ }
DB2::DB2(const std::string &connectionString)
: impl_(0)
{
connect(connectionString);
}
DB2::DB2(const DB2 &other)
: SqlConnection(other),
impl_(other.impl_ ? new Impl(*other.impl_) : 0)
{
if (impl_)
impl_->connect();
}
DB2::~DB2()
{
clearStatementCache();
delete impl_;
}
std::unique_ptr<SqlConnection> DB2::clone() const
{
return std::unique_ptr<SqlConnection>(new DB2(*this));
}
bool DB2::connect(const std::string &connectionString)
{
if (impl_)
throw DB2ServerException("Can't connect: already connected.");
#ifdef WT_WIN32
ConnectionStringType connStr;
if (!connectionString.empty()) {
int wstrlen = MultiByteToWideChar(CP_UTF8, 0, &connectionString[0], connectionString.size(), 0, 0);
assert(wstrlen != 0);
connStr.resize(wstrlen);
wstrlen = MultiByteToWideChar(CP_UTF8, 0, &connectionString[0], connectionString.size(), &connStr[0], connStr.size());
assert(wstrlen != 0);
}
#else // WT_WIN32
ConnectionStringType connStr = toUTF16(connectionString);
#endif // WT_WIN32
Impl *impl = new Impl(connStr);
try {
impl->connect();
} catch (...) {
delete impl;
throw;
}
impl_ = impl;
return true;
}
void DB2::executeSql(const std::string &sql)
{
if (showQueries()) {
LOG_INFO(sql);
}
SQLRETURN rc = SQL_SUCCESS;
if (!impl_->stmt) {
rc = SQLAllocHandle(SQL_HANDLE_STMT, impl_->dbc, &impl_->stmt);
handleErr(SQL_HANDLE_DBC, impl_->dbc, rc);
}
#ifdef WT_WIN32
if (sql.empty()) {
SQLWCHAR wstr[] = L"";
rc = SQLExecDirectW(impl_->stmt, wstr, 0);
} else {
int wstrlen = MultiByteToWideChar(CP_UTF8, 0, &sql[0], sql.size(), 0, 0);
assert(wstrlen != 0);
SQLWCHAR *wstr = new SQLWCHAR[wstrlen + 1];
wstrlen = MultiByteToWideChar(CP_UTF8, 0, &sql[0], sql.size(), wstr, wstrlen);
assert(wstrlen != 0);
wstr[wstrlen] = 0;
rc = SQLExecDirectW(impl_->stmt, wstr, wstrlen);
delete[] wstr;
}
#else // WT_WIN32
std::u16string wstr = toUTF16(sql);
rc = SQLExecDirectW(impl_->stmt, (SQLWCHAR*)&wstr[0], wstr.size());
#endif // WT_WIN32
try {
handleErr(SQL_HANDLE_STMT, impl_->stmt, rc);
} catch (...) {
SQLFreeStmt(impl_->stmt, SQL_CLOSE);
throw;
}
SQLFreeStmt(impl_->stmt, SQL_CLOSE);
}
void DB2::startTransaction()
{
if (showQueries()) {
LOG_INFO("begin transaction -- implicit");
}
}
void DB2::commitTransaction()
{
if (showQueries()) {
LOG_INFO("commit transaction -- using SQLEndTran");
}
SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, impl_->dbc, SQL_COMMIT);
handleErr(SQL_HANDLE_DBC, impl_->dbc, rc);
}
void DB2::rollbackTransaction()
{
if (showQueries()) {
LOG_INFO("rollback transaction -- using SQLEndTran");
}
SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, impl_->dbc, SQL_ROLLBACK);
handleErr(SQL_HANDLE_DBC, impl_->dbc, rc);
}
std::unique_ptr<SqlStatement> DB2::prepareStatement(const std::string &sql)
{
return std::unique_ptr<SqlStatement>(new DB2ServerStatement(*this, sql));
}
std::string DB2::autoincrementSql() const
{
return "not null GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1)";
}
std::vector<std::string> DB2::autoincrementCreateSequenceSql(const std::string &table, const std::string &id) const
{
return std::vector<std::string>();
}
std::vector<std::string> DB2::autoincrementDropSequenceSql(const std::string &table, const std::string &id) const
{
return std::vector<std::string>();
}
std::string DB2::autoincrementType() const
{
return "bigint";
}
std::string DB2::autoincrementInsertSuffix(const std::string &id) const
{
return "";
}
const char *DB2::dateTimeType(SqlDateTimeType type) const
{
if (type == SqlDateTimeType::Date)
return "date";
if (type == SqlDateTimeType::Time)
return "bigint"; // SQL Server has no proper duration type, so store duration as number of milliseconds
if (type == SqlDateTimeType::DateTime)
return "timestamp";
return "";
}
bool DB2::requireSubqueryAlias() const
{
return true;
}
const char *DB2::blobType() const
{
return "varbinary(max)";
}
const char *DB2::booleanType() const
{
return "smallint";
}
bool DB2::supportAlterTable() const
{
return true;
}
std::string DB2::textType(int size) const
{
if (size == -1)
return "varchar(32672)";
else
return std::string("varchar(") + std::to_string(size) + ")";
}
LimitQuery DB2::limitQueryMethod() const
{
return LimitQuery::OffsetFetch;
}
}
}
}
wt-4.5.0-patched/src/Wt/Dbo/backend/DB2.h 2021-04-20 16:48:27.289256974 +0200
// This may look like C code, but it's really -*- C++ -*-
/*
* Copyright (C) 2017 Emweb bv, Herent, Belgium.
*
* See the LICENSE file for terms of use.
*/
#ifndef WT_DBO_BACKEND_DB2SERVER_H_
#define WT_DBO_BACKEND_DB2SERVER_H_
#include <Wt/Dbo/SqlConnection.h>
#include <Wt/Dbo/SqlStatement.h>
#include "WDboDB2DllDefs.h"
namespace Wt {
namespace Dbo {
namespace backend {
/*! \class DB2SErver Wt/Dbo/backend/DB2Server Wt/Dbo/backend/DB2Server
* \brief A DB2 SQL Server connection
*
* This class provides the backend implementation for DB2 Server databases.
*
* \ingroup dbo
*/
class WTDBODB2_API DB2 : public SqlConnection
{
public:
/*! \brief Creates a new DB2 SQL Server backend connection.
*
* The connection is not yet open, and requires a connect() before it
* can be used.
*/
DB2();
/*! \brief Creates a new DB2 Server backend connection.
*
* For info about the connection string, see the connect() method.
*
* \sa connect()
*/
DB2(const std::string &connectionString);
/*! \brief Copy constructor.
*
* This creates a new backend connection with the same settings
* as another connection.
*
* \sa clone()
*/
DB2(const DB2& other);
/*! \brief Destructor.
*
* Closes the connection.
*/
virtual ~DB2();
virtual std::unique_ptr<Wt::Dbo::SqlConnection> clone() const override;
/*! \brief Tries to connect.
*
* Throws an exception if there was a problem, otherwise returns true.
*
* The connection string is the connection string that should be passed
* to SQLDriverConnectW to connect to the DB2 Server database.
*
* The \p connectionString should be UTF-8 encoded.
*
* Example connection string:
*
* \code
* Driver={ODBC Driver DB2};
* Server=localhost;
* UID=SA;
* PWD={example password};
* Database=example_db;
* \endcode
*
* You could also specify a DSN (Data Source Name) if you have it configured.
*
* See the
* <a href="https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqldriverconnect-function">SQLDriverConnect</a>
* function documentation on MSDN for more info.
*/
bool connect(const std::string &connectionString);
virtual void executeSql(const std::string &sql) override;
virtual void startTransaction() override;
virtual void commitTransaction() override;
virtual void rollbackTransaction() override;
virtual std::unique_ptr<SqlStatement> prepareStatement(const std::string &sql) override;
/** @name Methods that return dialect information
*/
//!@{
virtual std::string autoincrementSql() const override;
virtual std::vector<std::string> autoincrementCreateSequenceSql(const std::string &table, const std::string &id) const override;
virtual std::vector<std::string> autoincrementDropSequenceSql(const std::string &table, const std::string &id) const override;
virtual std::string autoincrementType() const override;
virtual std::string autoincrementInsertSuffix(const std::string &id) const override;
virtual const char *dateTimeType(SqlDateTimeType type) const override;
virtual const char *blobType() const override;
virtual bool requireSubqueryAlias() const override;
virtual const char *booleanType() const override;
virtual bool supportAlterTable() const override;
virtual std::string textType(int size) const override;
virtual LimitQuery limitQueryMethod() const override;
//!@}
private:
struct Impl;
Impl *impl_;
friend class DB2ServerStatement;
};
}
}
}
#endif // WT_DBO_BACKEND_MSSQLSERVER_H_
wt-4.5.0-patched/src/Wt/Dbo/backend/WDboDB2DllDefs.h 2021-04-20 16:48:27.293256939 +0200
// This may look like C code, but it's really -*- C++ -*-
/*
* Copyright (C) 2017 Emweb bv, Herent, Belgium.
*
* See the LICENSE file for terms of use.
*/
#ifndef WDBODB2DLLDEFS_H_
#define WDBODB2DLLDEFS_H_
#include <Wt/WConfig.h>
// Source: http://www.nedprod.com/programs/gccvisibility.html
#ifdef WT_WIN32
#define WTDBODB2_IMPORT __declspec(dllimport)
#define WTDBODB2_EXPORT __declspec(dllexport)
#define WTDBODB2_DLLLOCAL
#define WTDBODB2_DLLPUBLIC
#else
#define WTDBODB2_IMPORT __attribute__ ((visibility("default")))
#define WTDBODB2_EXPORT __attribute__ ((visibility("default")))
#define WTDBODB2_DLLLOCAL __attribute__ ((visibility("hidden")))
#define WTDBODB2_DLLPUBLIC __attribute__ ((visibility("default")))
#endif
#ifdef WTDBODB2_EXPORTS
#define WTDBODB2_API WTDBODB2_EXPORT
#else
#ifdef WTDBODB2_STATIC
#define WTDBODB2_API
#else
#define WTDBODB2_API WTDBODB2_IMPORT
#endif
#endif
#endif // WDBOMSSQLSERVERDLLDEFS_H_
wt-4.5.0-patched/src/Wt/Dbo/backend/wtdbodb2-version.rc.in 2021-04-20 16:48:27.293256939 +0200
/* Template from MSDN */
#include <windows.h>
#define VER_PRODUCTVERSION @VERSION_SERIES@,@VERSION_MAJOR@,@VERSION_MINOR@,0
#define VER_PRODUCTVERSION_STR "@VERSION_SERIES@.@VERSION_MAJOR@.@VERSION_MINOR@"
#define VER_FILEVERSION @VERSION_SERIES@,@VERSION_MAJOR@,@VERSION_MINOR@,0
#define VER_FILEVERSION_STR "@VERSION_SERIES@.@VERSION_MAJOR@.@VERSION_MINOR@"
#define VER_COMPANYNAME_STR "Emweb bv, Belgium"
#define VER_FILEDESCRIPTION_STR "Wt C++ Dbo Microsoft SQL Server backend DLL"
#define VER_INTERNALNAME_STR ""
#define VER_LEGALCOPYRIGHT_STR "(c) 2020 Emweb bv, Belgium. See LICENSE file for details"
#define VER_PRODUCTNAME_STR "Wt C++ Wt::DBO Microsoft SQL Server backend"
#define VER_ORIGINALFILENAME_STR "wtdbomssqlserver.dll"
#ifndef DEBUG
#define VER_DEBUG 0
#else
#define VER_DEBUG VS_FF_DEBUG
#endif
#define VER_PRIVATEBUILD 0
#define VER_PRERELEASE 0
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
PRODUCTVERSION VER_PRODUCTVERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS (VER_PRIVATEBUILD|VER_PRERELEASE|VER_DEBUG)
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", VER_COMPANYNAME_STR
VALUE "FileDescription", VER_FILEDESCRIPTION_STR
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", VER_INTERNALNAME_STR
VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR
VALUE "ProductName", VER_PRODUCTNAME_STR
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
    (1-1/1)