Coverage Report

Created: 2025-07-01 06:08

/src/logging-log4cxx/src/main/cpp/odbcappender.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
#include <log4cxx/db/odbcappender.h>
18
#include <log4cxx/helpers/loglog.h>
19
#include <log4cxx/helpers/optionconverter.h>
20
#include <log4cxx/helpers/stringhelper.h>
21
#include <log4cxx/helpers/transcoder.h>
22
#include <log4cxx/patternlayout.h>
23
#include <log4cxx/pattern/mdcpatternconverter.h>
24
#include <apr_strings.h>
25
#include <apr_time.h>
26
#include <cmath> // std::pow
27
28
#include <log4cxx/pattern/loggerpatternconverter.h>
29
#include <log4cxx/pattern/classnamepatternconverter.h>
30
#include <log4cxx/pattern/datepatternconverter.h>
31
#include <log4cxx/pattern/filelocationpatternconverter.h>
32
#include <log4cxx/pattern/fulllocationpatternconverter.h>
33
#include <log4cxx/pattern/shortfilelocationpatternconverter.h>
34
#include <log4cxx/pattern/linelocationpatternconverter.h>
35
#include <log4cxx/pattern/messagepatternconverter.h>
36
#include <log4cxx/pattern/methodlocationpatternconverter.h>
37
#include <log4cxx/pattern/levelpatternconverter.h>
38
#include <log4cxx/pattern/threadpatternconverter.h>
39
#include <log4cxx/pattern/threadusernamepatternconverter.h>
40
#include <log4cxx/pattern/ndcpatternconverter.h>
41
42
#if !defined(LOG4CXX)
43
  #define LOG4CXX 1
44
#endif
45
#include <log4cxx/private/log4cxx_private.h>
46
#if LOG4CXX_HAVE_ODBC
47
  #if defined(WIN32) || defined(_WIN32)
48
    #include <windows.h>
49
  #endif
50
  #include <sqlext.h>
51
#else
52
  typedef void* SQLHSTMT;
53
#endif
54
#include <log4cxx/private/odbcappender_priv.h>
55
#if defined(min)
56
  #undef min
57
#endif
58
#include <cstring>
59
#include <algorithm>
60
61
62
using namespace LOG4CXX_NS;
63
using namespace LOG4CXX_NS::helpers;
64
using namespace LOG4CXX_NS::db;
65
using namespace LOG4CXX_NS::spi;
66
using namespace LOG4CXX_NS::pattern;
67
68
SQLException::SQLException(short fHandleType,
69
  void* hInput, const char* prolog,
70
  LOG4CXX_NS::helpers::Pool& p)
71
0
  : Exception(formatMessage(fHandleType, hInput, prolog, p))
72
0
{
73
0
}
74
75
76
SQLException::SQLException(const char* msg)
77
0
  : Exception(msg)
78
0
{
79
0
}
80
81
SQLException::SQLException(const SQLException& src)
82
0
  : Exception(src)
83
0
{
84
0
}
85
86
const char* SQLException::formatMessage(short fHandleType,
87
  void* hInput, const char* prolog, LOG4CXX_NS::helpers::Pool& p)
88
0
{
89
0
  std::string strReturn(prolog);
90
0
  strReturn.append(" - ");
91
#if LOG4CXX_HAVE_ODBC
92
  SQLCHAR       SqlState[6];
93
  SQLCHAR       Msg[SQL_MAX_MESSAGE_LENGTH];
94
  SQLINTEGER    NativeError;
95
  SQLSMALLINT   i;
96
  SQLSMALLINT   MsgLen;
97
  SQLRETURN     rc2;
98
99
  // Get the status records.
100
  i = 1;
101
102
  while ((rc2 = SQLGetDiagRecA(fHandleType, hInput, i, SqlState, &NativeError,
103
          Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
104
  {
105
    strReturn.append((char*) Msg);
106
    i++;
107
  }
108
109
#else
110
0
  strReturn.append("log4cxx built without ODBC support");
111
0
#endif
112
113
0
  return apr_pstrdup((apr_pool_t*) p.getAPRPool(), strReturn.c_str());
114
0
}
115
116
117
IMPLEMENT_LOG4CXX_OBJECT(ODBCAppender)
118
119
0
#define _priv static_cast<ODBCAppenderPriv*>(m_priv.get())
120
121
ODBCAppender::ODBCAppender()
122
0
  : AppenderSkeleton (std::make_unique<ODBCAppenderPriv>(
123
#if LOG4CXX_EVENTS_AT_EXIT
124
    [this] {
125
      std::lock_guard<std::recursive_mutex> lock(_priv->mutex);
126
      if(_priv->closed)
127
        return;
128
      try
129
      {
130
        flushBuffer(_priv->pool);
131
      }
132
      catch (SQLException& e)
133
      {
134
        _priv->errorHandler->error(LOG4CXX_STR("Error flushing connection"),
135
          e, ErrorCode::GENERIC_FAILURE);
136
      }
137
    }
138
#endif
139
0
                ))
140
0
{
141
0
}
Unexecuted instantiation: log4cxx::db::ODBCAppender::ODBCAppender()
Unexecuted instantiation: log4cxx::db::ODBCAppender::ODBCAppender()
142
143
ODBCAppender::~ODBCAppender()
144
0
{
145
0
  finalize();
146
0
}
147
148
#define RULES_PUT(spec, cls) \
149
  specs.insert(PatternMap::value_type(LogString(LOG4CXX_STR(spec)), cls ::newInstance))
150
151
static PatternMap getFormatSpecifiers()
152
0
{
153
0
  PatternMap specs;
154
0
  if (specs.empty())
155
0
  {
156
0
    RULES_PUT("logger", LoggerPatternConverter);
157
0
    RULES_PUT("class", ClassNamePatternConverter);
158
0
    RULES_PUT("time", DatePatternConverter);
159
0
    RULES_PUT("shortfilename", ShortFileLocationPatternConverter);
160
0
    RULES_PUT("fullfilename", FileLocationPatternConverter);
161
0
    RULES_PUT("location", FullLocationPatternConverter);
162
0
    RULES_PUT("line", LineLocationPatternConverter);
163
0
    RULES_PUT("message", MessagePatternConverter);
164
0
    RULES_PUT("method", MethodLocationPatternConverter);
165
0
    RULES_PUT("level", LevelPatternConverter);
166
0
    RULES_PUT("thread", ThreadPatternConverter);
167
0
    RULES_PUT("threadname", ThreadUsernamePatternConverter);
168
0
    RULES_PUT("mdc", MDCPatternConverter);
169
0
    RULES_PUT("ndc", NDCPatternConverter);
170
0
  }
171
0
  return specs;
172
0
}
173
174
void ODBCAppender::setOption(const LogString& option, const LogString& value)
175
0
{
176
0
  if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), LOG4CXX_STR("buffersize")))
177
0
  {
178
0
    setBufferSize((size_t)OptionConverter::toInt(value, 1));
179
0
  }
180
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("PASSWORD"), LOG4CXX_STR("password")))
181
0
  {
182
0
    setPassword(value);
183
0
  }
184
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SQL"), LOG4CXX_STR("sql")))
185
0
  {
186
0
    setSql(value);
187
0
  }
188
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("URL"), LOG4CXX_STR("url"))
189
0
    || StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("DSN"), LOG4CXX_STR("dsn"))
190
0
    || StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("CONNECTIONSTRING"), LOG4CXX_STR("connectionstring"))  )
191
0
  {
192
0
    setURL(value);
193
0
  }
194
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("USER"), LOG4CXX_STR("user")))
195
0
  {
196
0
    setUser(value);
197
0
  }
198
0
  else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("COLUMNMAPPING"), LOG4CXX_STR("columnmapping")))
199
0
  {
200
0
    _priv->mappedName.push_back(value);
201
0
  }
202
0
  else
203
0
  {
204
0
    AppenderSkeleton::setOption(option, value);
205
0
  }
206
0
}
207
208
// Does ODBCAppender require a layout?
209
210
bool ODBCAppender::requiresLayout() const
211
0
{
212
0
  return false;
213
0
}
214
215
void ODBCAppender::activateOptions(LOG4CXX_NS::helpers::Pool&)
216
0
{
217
0
#if !LOG4CXX_HAVE_ODBC
218
0
  LogLog::error(LOG4CXX_STR("Can not activate ODBCAppender unless compiled with ODBC support."));
219
#else
220
  if (_priv->mappedName.empty())
221
  {
222
    LogLog::error(LOG4CXX_STR("ODBCAppender column mappings not defined, logging events will not be inserted"));
223
  }
224
  auto specs = getFormatSpecifiers();
225
  for (auto& name : _priv->mappedName)
226
  {
227
    auto lowerName = StringHelper::toLowerCase(name);
228
    auto pItem = specs.find(lowerName);
229
    if (specs.end() == pItem)
230
    {
231
      if (lowerName.size() < 5
232
       || lowerName.substr(0, 4) != LOG4CXX_STR("mdc{"))
233
        LogLog::error(name + LOG4CXX_STR(" is not a supported ColumnMapping value"));
234
      else // A single MDC entry
235
      {
236
        auto index = lowerName.find(0x7D /* '}' */, 4);
237
        auto len = (lowerName.npos == index ? lowerName.size() : index) - 4;
238
        ODBCAppenderPriv::DataBinding paramData{ 0, 0, 0, 0, 0 };
239
        paramData.converter = std::make_shared<MDCPatternConverter>(lowerName.substr(4, len));
240
        _priv->parameterValue.push_back(paramData);
241
      }
242
    }
243
    else
244
    {
245
      ODBCAppenderPriv::DataBinding paramData{ 0, 0, 0, 0, 0 };
246
      std::vector<LogString> options;
247
      if (LOG4CXX_STR("time") == pItem->first)
248
        options.push_back(LOG4CXX_STR("yyyy-MM-dd HH:mm:ss.SSSSSS"));
249
      paramData.converter = LOG4CXX_NS::cast<LoggingEventPatternConverter>((pItem->second)(options));
250
      _priv->parameterValue.push_back(paramData);
251
    }
252
  }
253
#endif
254
0
}
255
256
257
void ODBCAppender::append(const spi::LoggingEventPtr& event, LOG4CXX_NS::helpers::Pool& p)
258
0
{
259
#if LOG4CXX_HAVE_ODBC
260
  _priv->buffer.push_back(event);
261
262
  if (_priv->buffer.size() >= _priv->bufferSize)
263
  {
264
    flushBuffer(p);
265
  }
266
267
#endif
268
0
}
269
270
LogString ODBCAppender::getLogStatement(const spi::LoggingEventPtr& event, LOG4CXX_NS::helpers::Pool& p) const
271
0
{
272
0
    return LogString();
273
0
}
274
275
void ODBCAppender::execute(const LogString& sql, LOG4CXX_NS::helpers::Pool& p)
276
0
{
277
0
}
278
279
/* The default behavior holds a single connection open until the appender
280
is closed (typically when garbage collected).*/
281
void ODBCAppender::closeConnection(ODBCAppender::SQLHDBC /* con */)
282
0
{
283
0
}
284
285
ODBCAppender::SQLHDBC ODBCAppender::getConnection(LOG4CXX_NS::helpers::Pool& p)
286
0
{
287
#if LOG4CXX_HAVE_ODBC
288
  SQLRETURN ret;
289
290
  if (_priv->env == SQL_NULL_HENV)
291
  {
292
    ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &_priv->env);
293
294
    if (ret < 0)
295
    {
296
      SQLException ex(SQL_HANDLE_ENV, _priv->env, "Failed to allocate SQL handle", p);
297
      _priv->env = SQL_NULL_HENV;
298
      throw ex;
299
    }
300
301
    ret = SQLSetEnvAttr(_priv->env, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_INTEGER);
302
303
    if (ret < 0)
304
    {
305
      SQLException ex(SQL_HANDLE_ENV, _priv->env, "Failed to set odbc version", p);
306
      SQLFreeHandle(SQL_HANDLE_ENV, _priv->env);
307
      _priv->env = SQL_NULL_HENV;
308
      throw ex;
309
    }
310
  }
311
312
  if (_priv->connection == SQL_NULL_HDBC)
313
  {
314
    ret = SQLAllocHandle(SQL_HANDLE_DBC, _priv->env, &_priv->connection);
315
316
    if (ret < 0)
317
    {
318
      SQLException ex(SQL_HANDLE_DBC, _priv->connection, "Failed to allocate sql handle", p);
319
      _priv->connection = SQL_NULL_HDBC;
320
      throw ex;
321
    }
322
323
#if LOG4CXX_LOGCHAR_IS_WCHAR
324
    SQLWCHAR *wUser = nullptr, *wPwd = nullptr;
325
    if (!_priv->databaseUser.empty())
326
      wUser = (SQLWCHAR*)_priv->databaseUser.c_str();
327
    if (!_priv->databasePassword.empty())
328
      wPwd = (SQLWCHAR*)_priv->databasePassword.c_str();
329
    ret = SQLConnectW(_priv->connection
330
      , (SQLWCHAR*)_priv->databaseURL.c_str(), SQL_NTS
331
      , wUser, SQL_NTS
332
      , wPwd, SQL_NTS
333
      );
334
#elif LOG4CXX_LOGCHAR_IS_UTF8
335
    SQLCHAR *wUser = nullptr, *wPwd = nullptr;
336
    if (!_priv->databaseUser.empty())
337
      wUser = (SQLCHAR*)_priv->databaseUser.c_str();
338
    if (!_priv->databasePassword.empty())
339
      wPwd = (SQLCHAR*)_priv->databasePassword.c_str();
340
    ret = SQLConnectA(_priv->connection
341
      , (SQLCHAR*)_priv->databaseURL.c_str(), SQL_NTS
342
      , wUser, SQL_NTS
343
      , wPwd, SQL_NTS
344
      );
345
#else
346
    SQLWCHAR* wURL, *wUser = nullptr, *wPwd = nullptr;
347
    encode(&wURL, _priv->databaseURL, p);
348
    if (!_priv->databaseUser.empty())
349
      encode(&wUser, _priv->databaseUser, p);
350
    if (!_priv->databasePassword.empty())
351
      encode(&wPwd, _priv->databasePassword, p);
352
353
    ret = SQLConnectW( _priv->connection
354
      , wURL, SQL_NTS
355
      , wUser, SQL_NTS
356
      , wPwd, SQL_NTS
357
      );
358
#endif
359
360
    if (ret < 0)
361
    {
362
      SQLException ex(SQL_HANDLE_DBC, _priv->connection, "Failed to connect to database", p);
363
      SQLFreeHandle(SQL_HANDLE_DBC, _priv->connection);
364
      _priv->connection = SQL_NULL_HDBC;
365
      throw ex;
366
    }
367
  }
368
369
  return _priv->connection;
370
#else
371
0
  return 0;
372
0
#endif
373
0
}
374
375
void ODBCAppender::close()
376
0
{
377
0
  if (_priv->closed)
378
0
  {
379
0
    return;
380
0
  }
381
382
0
  Pool p;
383
384
0
  try
385
0
  {
386
0
    flushBuffer(p);
387
0
  }
388
0
  catch (SQLException& e)
389
0
  {
390
0
    _priv->errorHandler->error(LOG4CXX_STR("Error closing connection"),
391
0
      e, ErrorCode::GENERIC_FAILURE);
392
0
  }
393
394
#if LOG4CXX_HAVE_ODBC
395
396
  if (_priv->connection != SQL_NULL_HDBC)
397
  {
398
    SQLDisconnect(_priv->connection);
399
    SQLFreeHandle(SQL_HANDLE_DBC, _priv->connection);
400
  }
401
402
  if (_priv->env != SQL_NULL_HENV)
403
  {
404
    SQLFreeHandle(SQL_HANDLE_ENV, _priv->env);
405
  }
406
407
#endif
408
0
  _priv->closed = true;
409
0
}
410
411
#if LOG4CXX_HAVE_ODBC
412
void ODBCAppender::ODBCAppenderPriv::setPreparedStatement(SQLHDBC con, Pool& p)
413
{
414
  auto ret = SQLAllocHandle( SQL_HANDLE_STMT, con, &this->preparedStatement);
415
  if (ret < 0)
416
  {
417
    throw SQLException( SQL_HANDLE_DBC, con, "Failed to allocate statement handle.", p);
418
  }
419
420
#if LOG4CXX_LOGCHAR_IS_WCHAR
421
  ret = SQLPrepareW(this->preparedStatement, (SQLWCHAR*)this->sqlStatement.c_str(), SQL_NTS);
422
#elif LOG4CXX_LOGCHAR_IS_UTF8
423
  ret = SQLPrepareA(this->preparedStatement, (SQLCHAR*)this->sqlStatement.c_str(), SQL_NTS);
424
#else
425
  SQLWCHAR* wsql;
426
  encode(&wsql, this->sqlStatement, p);
427
  ret = SQLPrepareW(this->preparedStatement, wsql, SQL_NTS);
428
#endif
429
  if (ret < 0)
430
  {
431
    throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, "Failed to prepare sql statement.", p);
432
  }
433
434
  int parameterNumber = 0;
435
  for (auto& item : this->parameterValue)
436
  {
437
    ++parameterNumber;
438
    SQLSMALLINT  targetType;
439
    SQLULEN      targetMaxCharCount;
440
    SQLSMALLINT  decimalDigits;
441
    SQLSMALLINT  nullable;
442
    auto ret = SQLDescribeParam
443
      ( this->preparedStatement
444
      , parameterNumber
445
      , &targetType
446
      , &targetMaxCharCount
447
      , &decimalDigits
448
      , &nullable
449
      );
450
    if (ret < 0)
451
    {
452
      throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, "Failed to describe parameter", p);
453
    }
454
    if (SQL_CHAR == targetType || SQL_VARCHAR == targetType || SQL_LONGVARCHAR == targetType)
455
    {
456
      item.paramType = SQL_C_CHAR;
457
      item.paramMaxCharCount = targetMaxCharCount;
458
      item.paramValueSize = (SQLINTEGER)(item.paramMaxCharCount) * sizeof(char) + sizeof(char);
459
      item.paramValue = (SQLPOINTER)p.palloc(item.paramValueSize + sizeof(char));
460
    }
461
    else if (SQL_WCHAR == targetType || SQL_WVARCHAR == targetType || SQL_WLONGVARCHAR == targetType)
462
    {
463
      item.paramType = SQL_C_WCHAR;
464
      item.paramMaxCharCount = targetMaxCharCount;
465
      item.paramValueSize = (SQLINTEGER)(targetMaxCharCount) * sizeof(wchar_t) + sizeof(wchar_t);
466
      item.paramValue = (SQLPOINTER)p.palloc(item.paramValueSize + sizeof(wchar_t));
467
    }
468
    else if (SQL_TYPE_TIMESTAMP == targetType || SQL_TYPE_DATE == targetType || SQL_TYPE_TIME == targetType
469
      || SQL_DATETIME == targetType)
470
    {
471
      item.paramType = SQL_C_TYPE_TIMESTAMP;
472
      item.paramMaxCharCount = (0 <= decimalDigits) ? decimalDigits : 6;
473
      item.paramValueSize = sizeof(SQL_TIMESTAMP_STRUCT);
474
      item.paramValue = (SQLPOINTER)p.palloc(item.paramValueSize);
475
    }
476
    else
477
    {
478
      if (SQL_INTEGER != targetType)
479
      {
480
        LogString msg(LOG4CXX_STR("Unexpected targetType ("));
481
        helpers::StringHelper::toString(targetType, p, msg);
482
        msg += LOG4CXX_STR(") at parameter ");
483
        helpers::StringHelper::toString(parameterNumber, p, msg);
484
        msg += LOG4CXX_STR(" while preparing SQL");
485
        LogLog::warn(msg);
486
      }
487
      item.paramMaxCharCount = 30;
488
#if LOG4CXX_LOGCHAR_IS_UTF8
489
      item.paramType = SQL_C_CHAR;
490
      item.paramValueSize = (SQLINTEGER)(item.paramMaxCharCount) * sizeof(char);
491
      item.paramValue = (SQLPOINTER)p.palloc(item.paramValueSize + sizeof(char));
492
#else
493
      item.paramType = SQL_C_WCHAR;
494
      item.paramValueSize = (SQLINTEGER)(item.paramMaxCharCount) * sizeof(wchar_t);
495
      item.paramValue = (SQLPOINTER)p.palloc(item.paramValueSize + sizeof(wchar_t));
496
#endif
497
    }
498
    item.strLen_or_Ind = SQL_NTS;
499
    ret = SQLBindParameter
500
      ( this->preparedStatement
501
      , parameterNumber
502
      , SQL_PARAM_INPUT
503
      , item.paramType  // ValueType
504
      , targetType
505
      , targetMaxCharCount
506
      , decimalDigits
507
      , item.paramValue
508
      , item.paramValueSize
509
      , &item.strLen_or_Ind
510
      );
511
    if (ret < 0)
512
    {
513
      throw SQLException(SQL_HANDLE_STMT, this->preparedStatement, "Failed to bind parameter", p);
514
    }
515
  }
516
}
517
518
void ODBCAppender::ODBCAppenderPriv::setParameterValues(const spi::LoggingEventPtr& event, Pool& p)
519
{
520
  for (auto& item : this->parameterValue)
521
  {
522
    if (!item.paramValue || item.paramValueSize <= 0)
523
      ;
524
    else if (SQL_C_WCHAR == item.paramType)
525
    {
526
      LogString sbuf;
527
      item.converter->format(event, sbuf, p);
528
#if LOG4CXX_LOGCHAR_IS_WCHAR_T
529
      std::wstring& tmp = sbuf;
530
#else
531
      std::wstring tmp;
532
      Transcoder::encode(sbuf, tmp);
533
#endif
534
      auto dst = (wchar_t*)item.paramValue;
535
      auto charCount = std::min(size_t(item.paramMaxCharCount), tmp.size());
536
      auto copySize = std::min(size_t(item.paramValueSize - 1), charCount * sizeof(wchar_t));
537
      std::memcpy(dst, tmp.data(), copySize);
538
      dst[copySize / sizeof(wchar_t)] = 0;
539
    }
540
    else if (SQL_C_CHAR == item.paramType)
541
    {
542
      LogString sbuf;
543
      item.converter->format(event, sbuf, p);
544
#if LOG4CXX_LOGCHAR_IS_UTF8
545
      std::string& tmp = sbuf;
546
#else
547
      std::string tmp;
548
      Transcoder::encode(sbuf, tmp);
549
#endif
550
      auto dst = (char*)item.paramValue;
551
      auto sz = std::min(size_t(item.paramMaxCharCount), tmp.size());
552
      auto copySize = std::min(size_t(item.paramValueSize - 1), sz * sizeof(char));
553
      std::memcpy(dst, tmp.data(), copySize);
554
      dst[copySize] = 0;
555
    }
556
    else if (SQL_C_TYPE_TIMESTAMP == item.paramType)
557
    {
558
      apr_time_exp_t exploded;
559
      apr_status_t stat = this->timeZone->explode(&exploded, event->getTimeStamp());
560
      if (stat == APR_SUCCESS)
561
      {
562
        auto dst = (SQL_TIMESTAMP_STRUCT*)item.paramValue;
563
        dst->year = 1900 + exploded.tm_year;
564
        dst->month = 1 + exploded.tm_mon;
565
        dst->day = exploded.tm_mday;
566
        dst->hour = exploded.tm_hour;
567
        dst->minute = exploded.tm_min;
568
        dst->second = exploded.tm_sec;
569
        // Prevent '[ODBC SQL Server Driver]Datetime field overflow' by rounding to the target field precision
570
        int roundingExponent = 6 - (int)item.paramMaxCharCount;
571
        if (0 < roundingExponent)
572
        {
573
          int roundingDivisor = (int)std::pow(10, roundingExponent);
574
          dst->fraction = 1000 * roundingDivisor * ((exploded.tm_usec + roundingDivisor / 2) / roundingDivisor);
575
        }
576
        else
577
          dst->fraction = 1000 * exploded.tm_usec;
578
      }
579
    }
580
  }
581
}
582
#endif
583
584
void ODBCAppender::flushBuffer(Pool& p)
585
0
{
586
0
  for (auto& logEvent : _priv->buffer)
587
0
  {
588
0
    if (_priv->parameterValue.empty())
589
0
      _priv->errorHandler->error(LOG4CXX_STR("ODBCAppender column mappings not defined"));
590
#if LOG4CXX_HAVE_ODBC
591
    else try
592
    {
593
      if (0 == _priv->preparedStatement)
594
        _priv->setPreparedStatement(getConnection(p), p);
595
      _priv->setParameterValues(logEvent, p);
596
      auto ret = SQLExecute(_priv->preparedStatement);
597
      if (ret < 0)
598
      {
599
        throw SQLException(SQL_HANDLE_STMT, _priv->preparedStatement, "Failed to execute prepared statement", p);
600
      }
601
    }
602
    catch (SQLException& e)
603
    {
604
      _priv->errorHandler->error(LOG4CXX_STR("Failed to execute sql"), e,
605
        ErrorCode::FLUSH_FAILURE);
606
    }
607
#endif
608
0
  }
609
610
  // clear the buffer of reported events
611
0
  _priv->buffer.clear();
612
0
}
613
614
void ODBCAppender::setSql(const LogString& s)
615
0
{
616
0
    _priv->sqlStatement = s;
617
0
}
618
619
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR_T || defined(WIN32) || defined(_WIN32)
620
void ODBCAppender::encode(wchar_t** dest, const LogString& src, Pool& p)
621
0
{
622
0
  *dest = Transcoder::wencode(src, p);
623
0
}
624
#endif
625
626
void ODBCAppender::encode(unsigned short** dest,
627
  const LogString& src, Pool& p)
628
0
{
629
  //  worst case double number of characters from UTF-8 or wchar_t
630
0
  *dest = (unsigned short*)
631
0
    p.palloc((src.size() + 1) * 2 * sizeof(unsigned short));
632
0
  unsigned short* current = *dest;
633
634
0
  for (LogString::const_iterator i = src.begin();
635
0
    i != src.end();)
636
0
  {
637
0
    unsigned int sv = Transcoder::decode(src, i);
638
639
0
    if (sv < 0x10000)
640
0
    {
641
0
      *current++ = (unsigned short) sv;
642
0
    }
643
0
    else
644
0
    {
645
0
      unsigned char u = (unsigned char) (sv >> 16);
646
0
      unsigned char w = (unsigned char) (u - 1);
647
0
      unsigned short hs = (0xD800 + ((w & 0xF) << 6) + ((sv & 0xFFFF) >> 10));
648
0
      unsigned short ls = (0xDC00 + (sv & 0x3FF));
649
0
      *current++ = (unsigned short) hs;
650
0
      *current++ = (unsigned short) ls;
651
0
    }
652
0
  }
653
654
0
  *current = 0;
655
0
}
656
657
const LogString& ODBCAppender::getSql() const
658
0
{
659
0
  return _priv->sqlStatement;
660
0
}
661
662
void ODBCAppender::setUser(const LogString& user)
663
0
{
664
0
  _priv->databaseUser = user;
665
0
}
666
667
void ODBCAppender::setURL(const LogString& url)
668
0
{
669
0
  _priv->databaseURL = url;
670
0
}
671
672
void ODBCAppender::setPassword(const LogString& password)
673
0
{
674
0
  _priv->databasePassword = password;
675
0
}
676
677
void ODBCAppender::setBufferSize(size_t newBufferSize)
678
0
{
679
0
  _priv->bufferSize = newBufferSize;
680
0
}
681
682
const LogString& ODBCAppender::getUser() const
683
0
{
684
0
  return _priv->databaseUser;
685
0
}
686
687
const LogString& ODBCAppender::getURL() const
688
0
{
689
0
  return _priv->databaseURL;
690
0
}
691
692
const LogString& ODBCAppender::getPassword() const
693
0
{
694
0
  return _priv->databasePassword;
695
0
}
696
697
size_t ODBCAppender::getBufferSize() const
698
0
{
699
0
  return _priv->bufferSize;
700
0
}
701