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