1 /* $Id: dbapi_driver_utils.cpp 626333 2021-02-25 18:42:08Z ivanov $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Sergey Sikorskiy
27 *
28 * File Description: Small utility classes common to all drivers.
29 *
30 */
31
32 #include <ncbi_pch.hpp>
33
34 #include <dbapi/driver/impl/dbapi_driver_utils.hpp>
35 #include <dbapi/driver/impl/dbapi_impl_context.hpp>
36 #include <dbapi/driver/impl/dbapi_impl_connection.hpp>
37 #include <dbapi/driver/util/parameters.hpp>
38 #include <dbapi/error_codes.hpp>
39
40
41 #ifdef NCBI_OS_MSWIN
42 #include <sqlext.h>
43 #else
44 #include <dbapi/driver/odbc/unix_odbc/sqlext.h>
45 #endif
46
47 #include <stdio.h>
48
49 #define NCBI_USE_ERRCODE_X Dbapi_DrvrUtil
50
51 BEGIN_NCBI_SCOPE
52
53 ////////////////////////////////////////////////////////////////////////////////
CMsgHandlerGuard(I_DriverContext & conn)54 CMsgHandlerGuard::CMsgHandlerGuard(I_DriverContext& conn)
55 : m_Conn(conn)
56 {
57 m_Conn.PushCntxMsgHandler(&m_Handler);
58 m_Conn.PushDefConnMsgHandler(&m_Handler);
59 }
60
~CMsgHandlerGuard(void)61 CMsgHandlerGuard::~CMsgHandlerGuard(void)
62 {
63 m_Conn.PopDefConnMsgHandler(&m_Handler);
64 m_Conn.PopCntxMsgHandler(&m_Handler);
65 }
66
67
68 namespace impl
69 {
70
GetValidUTF8Len(const CTempString & ts)71 SIZE_TYPE GetValidUTF8Len(const CTempString& ts)
72 {
73 SIZE_TYPE len = ts.size();
74 if (CUtf8::GetValidBytesCount(ts) != len) {
75 return len; // totally invalid, assume not UTF-8 after all
76 }
77 for (SIZE_TYPE n = len; n > 0 && (ts[n - 1] & 0x80) != 0; --n) {
78 if ((ts[n - 1] & 0xC0) == 0xC0) { // start byte
79 SIZE_TYPE needed;
80 CUtf8::DecodeFirst(ts[n - 1], needed);
81 if (n + needed > len) {
82 return n - 1;
83 }
84 break;
85 }
86 }
87 return len;
88 }
89
binary_to_hex_string(char * buffer,size_t buffer_size,const void * value,size_t value_size,TBinaryToHexFlags flags)90 size_t binary_to_hex_string(char* buffer, size_t buffer_size,
91 const void* value, size_t value_size,
92 TBinaryToHexFlags flags)
93 {
94 static const char s_HexDigits[] = "0123456789ABCDEF";
95
96 const unsigned char* c = (const unsigned char*) value;
97 size_t i = 0, margin = 0;
98 if ((flags & fB2H_NoFinalNul) == 0) {
99 margin += 1;
100 }
101 if ((flags & fB2H_NoPrefix) == 0) {
102 margin += 2;
103 }
104 if (value_size * 2 + margin > buffer_size) {
105 return 0;
106 }
107 if ((flags & fB2H_NoPrefix) == 0) {
108 buffer[i++] = '0';
109 buffer[i++] = 'x';
110 }
111 for (size_t j = 0; j < value_size; j++) {
112 buffer[i++] = s_HexDigits[c[j] >> 4];
113 buffer[i++] = s_HexDigits[c[j] & 0x0F];
114 }
115 if ((flags & fB2H_NoFinalNul) == 0) {
116 buffer[i + 1] = '\0';
117 }
118 return i;
119 }
120
121 ////////////////////////////////////////////////////////////////////////////////
CDBBindedParams(CDB_Params & bindings,EOwnership ownership)122 CDBBindedParams::CDBBindedParams(CDB_Params& bindings, EOwnership ownership)
123 : m_Bindings(&bindings, ownership)
124 {
125 }
126
127
GetNum(void) const128 unsigned int CDBBindedParams::GetNum(void) const
129 {
130 return m_Bindings->NofParams();
131 }
132
133 const string&
GetName(const CDBParamVariant & param,CDBParamVariant::ENameFormat format) const134 CDBBindedParams::GetName(
135 const CDBParamVariant& param,
136 CDBParamVariant::ENameFormat format) const
137 {
138 if (param.IsPositional()) {
139 return m_Bindings->GetParamName(param.GetPosition());
140 } else {
141 return param.GetName(); // XXX - confirm presence?
142 }
143 }
144
GetIndex(const CDBParamVariant & param) const145 unsigned int CDBBindedParams::GetIndex(const CDBParamVariant& param) const
146 {
147 if (param.IsPositional()) {
148 return param.GetPosition(); // XXX - check range?
149 } else {
150 return m_Bindings->GetParamNum(param.GetName());
151 }
152 }
153
GetMaxSize(const CDBParamVariant & param) const154 size_t CDBBindedParams::GetMaxSize(const CDBParamVariant& param) const
155 {
156 DATABASE_DRIVER_ERROR( "Methods GetMaxSize is not implemented yet.", 122002 );
157 return 0;
158 }
159
GetDataType(const CDBParamVariant & param) const160 EDB_Type CDBBindedParams::GetDataType(const CDBParamVariant& param) const
161 {
162 const CDB_Object* v = GetValue(param);
163 return v == NULL ? eDB_UnsupportedType : v->GetType();
164 }
165
GetValue(const CDBParamVariant & param) const166 const CDB_Object* CDBBindedParams::GetValue(const CDBParamVariant& param) const
167 {
168 try {
169 int i = GetIndex(param);
170 return m_Bindings->GetParam(i);
171 } catch (exception&) {
172 return NULL;
173 }
174 }
175
GetDirection(const CDBParamVariant & param) const176 CDBParams::EDirection CDBBindedParams::GetDirection(const CDBParamVariant& param) const
177 {
178 DATABASE_DRIVER_ERROR( "Methods GetDirection is not implemented yet.", 122002 );
179 return CDBParams::eIn;
180 }
181
Bind(const CDBParamVariant & param,CDB_Object * value,bool out_param)182 CDBParams& CDBBindedParams::Bind(
183 const CDBParamVariant& param,
184 CDB_Object* value,
185 bool out_param
186 )
187 {
188 if (param.IsPositional()) {
189 unsigned int pos = param.GetPosition();
190
191 m_Bindings->BindParam(
192 pos,
193 kEmptyStr,
194 value,
195 out_param
196 );
197 } else {
198 m_Bindings->BindParam(
199 CDB_Params::kNoParamNumber,
200 param.GetName(),
201 value,
202 out_param
203 );
204 }
205
206 return *this;
207 }
208
Set(const CDBParamVariant & param,CDB_Object * value,bool out_param)209 CDBParams& CDBBindedParams::Set(
210 const CDBParamVariant& param,
211 CDB_Object* value,
212 bool out_param
213 )
214 {
215 if (param.IsPositional()) {
216 unsigned int pos = param.GetPosition();
217
218 m_Bindings->SetParam(
219 pos,
220 kEmptyStr,
221 value,
222 out_param
223 );
224 } else {
225 m_Bindings->SetParam(
226 CDB_Params::kNoParamNumber,
227 param.GetName(),
228 value,
229 out_param
230 );
231 }
232
233 return *this;
234 }
235
SemiShallowClone(void) const236 CDBParams* CDBBindedParams::SemiShallowClone(void) const
237 {
238 unique_ptr<impl::CDB_Params> p(m_Bindings->SemiShallowClone());
239 return new CDBBindedParams(*p.release(), eTakeOwnership);
240 }
241
242 ////////////////////////////////////////////////////////////////////////////////
SInfo(void)243 CCachedRowInfo::SInfo::SInfo(void)
244 : m_MaxSize(0)
245 , m_DataType(eDB_UnsupportedType)
246 , m_Direction(eOut)
247 {
248 }
249
SInfo(const string & name,size_t max_size,EDB_Type data_type,EDirection direction)250 CCachedRowInfo::SInfo::SInfo(const string& name,
251 size_t max_size,
252 EDB_Type data_type,
253 EDirection direction
254 )
255 : m_Name(name)
256 , m_MaxSize(max_size)
257 , m_DataType(data_type)
258 , m_Direction(direction)
259 {
260 }
261
262 ////////////////////////////////////////////////////////////////////////////////
CCachedRowInfo(impl::CDB_Params & bindings)263 CCachedRowInfo::CCachedRowInfo(impl::CDB_Params& bindings)
264 : CDBBindedParams(bindings)
265 , m_Initialized(false)
266 {
267 }
268
~CCachedRowInfo(void)269 CCachedRowInfo::~CCachedRowInfo(void)
270 {
271 return;
272 }
273
274 unsigned int
GetNum(void) const275 CCachedRowInfo::GetNum(void) const
276 {
277 if (!IsInitialized()) {
278 Initialize();
279 }
280
281 unsigned int num = GetNumInternal(); // For debugging purposes ...
282 return num;
283 }
284
FindParamPosInternal(const string & name) const285 unsigned int CCachedRowInfo::FindParamPosInternal(const string& name) const
286 {
287 if (!IsInitialized()) {
288 Initialize();
289 }
290
291 const size_t param_num = m_Info.size();
292
293 for (unsigned int i = 0; i < param_num; ++i) {
294 if (m_Info[i].m_Name == name) {
295 return i;
296 }
297 }
298
299 DATABASE_DRIVER_ERROR("Invalid parameter name " + name, 20001);
300 return 0;
301 }
302
303 const string&
GetName(const CDBParamVariant & param,CDBParamVariant::ENameFormat format) const304 CCachedRowInfo::GetName(
305 const CDBParamVariant& param,
306 CDBParamVariant::ENameFormat format) const
307 {
308 if (!IsInitialized()) {
309 Initialize();
310 }
311
312 if (param.IsPositional()) {
313 unsigned int num = param.GetPosition();
314
315 if (num < GetNumInternal()) {
316 return m_Info[num].m_Name;
317 }
318 } else {
319 return m_Info[FindParamPosInternal(param.GetName(format))].m_Name;
320 }
321
322 return kEmptyStr;
323 }
324
325
326 unsigned int
GetIndex(const CDBParamVariant & param) const327 CCachedRowInfo::GetIndex(const CDBParamVariant& param) const
328 {
329 if (!IsInitialized()) {
330 Initialize();
331 }
332
333 if (param.IsPositional()) {
334 return param.GetPosition();
335 } else {
336 return FindParamPosInternal(param.GetName());
337 }
338
339 DATABASE_DRIVER_ERROR("Parameter name not found: " + param.GetName(), 1);
340
341 return 0;
342 }
343
344
345 size_t
GetMaxSize(const CDBParamVariant & param) const346 CCachedRowInfo::GetMaxSize(const CDBParamVariant& param) const
347 {
348 if (!IsInitialized()) {
349 Initialize();
350 }
351
352 if (param.IsPositional()) {
353 unsigned int num = param.GetPosition();
354
355 if (num < GetNumInternal()) {
356 return m_Info[num].m_MaxSize;
357 }
358 } else {
359 return m_Info[FindParamPosInternal(param.GetName())].m_MaxSize;
360 }
361
362 return 0;
363 }
364
365 EDB_Type
GetDataType(const CDBParamVariant & param) const366 CCachedRowInfo::GetDataType(const CDBParamVariant& param) const
367 {
368 if (!IsInitialized()) {
369 Initialize();
370 }
371
372 if (param.IsPositional()) {
373 unsigned int num = param.GetPosition();
374
375 if (num < GetNumInternal()) {
376 return m_Info[num].m_DataType;
377 }
378 } else {
379 return m_Info[FindParamPosInternal(param.GetName())].m_DataType;
380 }
381
382 return eDB_UnsupportedType;
383 }
384
385 CDBParams::EDirection
GetDirection(const CDBParamVariant & param) const386 CCachedRowInfo::GetDirection(const CDBParamVariant& param) const
387 {
388 if (!IsInitialized()) {
389 Initialize();
390 }
391
392 if (param.IsPositional()) {
393 unsigned int num = param.GetPosition();
394
395 if (num < GetNumInternal()) {
396 return m_Info[num].m_Direction;
397 }
398 } else {
399 return m_Info[FindParamPosInternal(param.GetName())].m_Direction;
400 }
401
402 return eOut;
403 }
404
405
406 ////////////////////////////////////////////////////////////////////////////////
CRowInfo_SP_SQL_Server(const string & sp_name,impl::CConnection & conn,impl::CDB_Params & bindings)407 CRowInfo_SP_SQL_Server::CRowInfo_SP_SQL_Server(
408 const string& sp_name,
409 impl::CConnection& conn,
410 impl::CDB_Params& bindings
411 )
412 : CCachedRowInfo(bindings)
413 , m_SPName(sp_name)
414 , m_Conn(conn)
415 {
416 }
417
418
~CRowInfo_SP_SQL_Server(void)419 CRowInfo_SP_SQL_Server::~CRowInfo_SP_SQL_Server(void)
420 {
421 }
422
423 void
Initialize(void) const424 CRowInfo_SP_SQL_Server::Initialize(void) const
425 {
426 impl::CConnection& conn = GetCConnection();
427 unsigned int step = 0;
428 CDBConnParams::EServerType server_type = conn.GetServerType();
429
430 if (server_type == CDBConnParams::eUnknown) {
431 server_type = conn.CalculateServerType(server_type);
432 ++step;
433 }
434
435 while (step++ < 3) {
436 if (server_type == CDBConnParams::eSybaseSQLServer
437 || server_type == CDBConnParams::eMSSqlServer)
438 {
439 string sql;
440 string db_name;
441 string db_owner;
442 string sp_name;
443 unique_ptr<CDB_LangCmd> cmd;
444
445 {
446 vector<string> arr_param;
447
448 NStr::Split(GetSPName(), ".", arr_param);
449 size_t pos = 0;
450
451 switch (arr_param.size()) {
452 case 3:
453 db_name = arr_param[pos++];
454 case 2:
455 db_owner = arr_param[pos++];
456 case 1:
457 sp_name = arr_param[pos++];
458 break;
459 default:
460 DATABASE_DRIVER_ERROR("Invalid format of stored procedure's name: " + GetSPName(), 1);
461 }
462 }
463
464 if (db_name.empty()) {
465 sql =
466 "SELECT '' from sysobjects WHERE name = @name \n"
467 "UNION \n"
468 "SELECT 'master' from master..sysobjects WHERE name = @name \n"
469 ;
470
471 if (server_type == CDBConnParams::eSybaseSQLServer) {
472 sql +=
473 "UNION \n"
474 "SELECT 'sybsystemprocs' from sybsystemprocs..sysobjects WHERE name = @name \n"
475 "UNION \n"
476 "SELECT 'sybsystemdb' from sybsystemdb..sysobjects WHERE name = @name"
477 ;
478 }
479
480 CMsgHandlerGuard guard(conn);
481 cmd.reset(conn.LangCmd(sql));
482 CDB_VarChar sp_name_value(sp_name);
483
484 try {
485 cmd->GetBindParams().Bind("@name", &sp_name_value);
486 cmd->Send();
487
488 while (cmd->HasMoreResults()) {
489 unique_ptr<CDB_Result> res(cmd->Result());
490
491 if (res.get() != NULL && res->ResultType() == eDB_RowResult ) {
492 CDB_VarChar db_name_value;
493
494 while (res->Fetch()) {
495 res->GetItem(&db_name_value);
496
497 if (!db_name_value.IsNULL()) {
498 db_name = db_name_value.AsString();
499 }
500 }
501 }
502 }
503 } catch (const CDB_Exception&) {
504 // Something is wrong. Probably we do not have enough permissios.
505 // We assume that the object is located in the current database. What
506 // else can we do?
507
508 // Probably, this method was supplied with a wrong
509 // server_type value;
510 if (step < 2) {
511 server_type = conn.CalculateServerType(CDBConnParams::eUnknown);
512 ++step;
513 continue;
514 }
515 }
516 }
517
518 // unique_ptr<CDB_RPCCmd> sp(conn.RPC("sp_sproc_columns"));
519 // We cannot use CDB_RPCCmd here because of recursion ...
520 sql = "exec " + db_name + "." + db_owner + ".sp_sproc_columns @procedure_name";
521 cmd.reset(conn.LangCmd(sql));
522 CDB_VarChar name_value(sp_name);
523
524 try {
525 cmd->GetBindParams().Bind("@procedure_name", &name_value);
526 cmd->Send();
527
528 while (cmd->HasMoreResults()) {
529 unique_ptr<CDB_Result> res(cmd->Result());
530
531 if (res.get() != NULL && res->ResultType() == eDB_RowResult ) {
532 CDB_VarChar column_name;
533 CDB_SmallInt column_type;
534 CDB_SmallInt data_type;
535 CDB_Int data_len = 0;
536
537 while (res->Fetch()) {
538 res->SkipItem();
539 res->SkipItem();
540 res->SkipItem();
541 res->GetItem(&column_name);
542 res->GetItem(&column_type);
543 res->GetItem(&data_type);
544 res->SkipItem();
545 res->SkipItem();
546 res->GetItem(&data_len);
547
548 // Decode data_type
549 EDB_Type edb_data_type(eDB_UnsupportedType);
550 switch (data_type.Value()) {
551 case SQL_LONGVARCHAR:
552 edb_data_type = eDB_VarChar;
553 break;
554 case SQL_BINARY:
555 edb_data_type = eDB_Binary;
556 break;
557 case SQL_VARBINARY:
558 edb_data_type = eDB_VarBinary;
559 break;
560 case SQL_LONGVARBINARY:
561 edb_data_type = eDB_Binary;
562 break;
563 case SQL_BIGINT:
564 edb_data_type = eDB_BigInt;
565 break;
566 case SQL_TINYINT:
567 edb_data_type = eDB_TinyInt;
568 break;
569 case SQL_BIT:
570 edb_data_type = eDB_Bit;
571 break;
572 // case SQL_GUID:
573 case -9:
574 edb_data_type = eDB_VarChar;
575 break;
576 case SQL_CHAR:
577 edb_data_type = eDB_Char;
578 break;
579 case SQL_NUMERIC:
580 case SQL_DECIMAL:
581 edb_data_type = eDB_Numeric;
582 break;
583 case SQL_INTEGER:
584 edb_data_type = eDB_Int;
585 break;
586 case SQL_SMALLINT:
587 edb_data_type = eDB_SmallInt;
588 break;
589 case SQL_FLOAT:
590 case SQL_REAL:
591 edb_data_type = eDB_Float;
592 break;
593 case SQL_DOUBLE:
594 edb_data_type = eDB_Double;
595 break;
596 case SQL_DATETIME:
597 case SQL_TIME:
598 case SQL_TIMESTAMP:
599 edb_data_type = eDB_DateTime;
600 break;
601 case SQL_VARCHAR:
602 edb_data_type = eDB_VarChar;
603 break;
604
605 // case SQL_TYPE_DATE:
606 // case SQL_TYPE_TIME:
607 // case SQL_TYPE_TIMESTAMP:
608 default:
609 edb_data_type = eDB_UnsupportedType;
610 }
611
612 EDirection direction = CDBParams::eIn;
613
614 if (column_type.Value() == 2 /*SQL_PARAM_TYPE_OUTPUT*/ ||
615 column_type.Value() == 4 /*SQL_PARAM_OUTPUT*/ ||
616 column_type.Value() == 5 /*SQL_RETURN_VALUE*/ )
617 {
618 direction = CDBParams::eOut;
619 }
620
621 Add(column_name.AsString(),
622 size_t(data_len.Value()),
623 edb_data_type,
624 direction
625 );
626 }
627 } // if ...
628 } // while HasMoreresults ...
629
630 // Break the loop, Everything seems to be fine. ...
631 break;
632 } catch (const CDB_Exception&) {
633 // Something is wrong ...
634 // We may not have permissions to run stored procedures ...
635
636 // Probably, this method was supplied with a wrong
637 // server_type value;
638 if (step < 2) {
639 server_type = conn.CalculateServerType(CDBConnParams::eUnknown);
640 ++step;
641 }
642 }
643 } // if server_type
644 }
645
646 SetInitialized();
647 }
648
649 ////////////////////////////////////////////////////////////////////////////////
CMsgHandlerGuard(impl::CConnection & conn)650 CMsgHandlerGuard::CMsgHandlerGuard(impl::CConnection& conn)
651 : m_Conn(conn)
652 {
653 m_Conn.PushMsgHandler(&m_Handler);
654 }
655
~CMsgHandlerGuard(void)656 CMsgHandlerGuard::~CMsgHandlerGuard(void)
657 {
658 m_Conn.PopMsgHandler(&m_Handler);
659 }
660
661
662 }
663
664 END_NCBI_SCOPE
665
666
667