1 /* $Id: cursor.cpp 620233 2020-11-18 14:47:29Z ucko $
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:  Vladimir Soussov
27  *
28  * File Description:  CTLib cursor command
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 #include <dbapi/driver/ctlib/interfaces.hpp>
34 #include <dbapi/error_codes.hpp>
35 
36 #include <stdio.h>
37 
38 
39 #define NCBI_USE_ERRCODE_X   Dbapi_CTlib_Cmds
40 
41 #undef NCBI_DATABASE_THROW
42 #undef NCBI_DATABASE_RETHROW
43 
44 #define NCBI_DATABASE_THROW(ex_class, message, err_code, severity) \
45     NCBI_DATABASE_THROW_ANNOTATED(ex_class, message, err_code, severity, \
46         GetDbgInfo(), GetConnection(), GetLastParams())
47 #define NCBI_DATABASE_RETHROW(prev_ex, ex_class, message, err_code, severity) \
48     NCBI_DATABASE_RETHROW_ANNOTATED(prev_ex, ex_class, message, err_code, \
49         severity, GetDbgInfo(), GetConnection(), GetLastParams())
50 
51 BEGIN_NCBI_SCOPE
52 
53 #ifdef FTDS_IN_USE
54 namespace NCBI_NS_FTDS_CTLIB
55 {
56 #endif
57 
58 
59 /////////////////////////////////////////////////////////////////////////////
60 //
61 //  CTL_CursorCmd::
62 //
63 
CTL_CursorCmd(CTL_Connection & conn,const string & cursor_name,const string & query,unsigned int fetch_size)64 CTL_CursorCmd::CTL_CursorCmd(CTL_Connection& conn,
65                              const string& cursor_name,
66                              const string& query,
67                              unsigned int fetch_size
68                              )
69 : CTL_Cmd(conn, cursor_name, query)
70 , m_FetchSize(fetch_size)
71 {
72     string extra_msg = "Cursor Name: \"" + cursor_name + "\"; SQL Command: \""+
73         query + "\"";
74     SetExecCntxInfo(extra_msg);
75 }
76 
77 
78 CS_RETCODE
CheckSFB(CS_RETCODE rc,const char * msg,unsigned int msg_num)79 CTL_CursorCmd::CheckSFB(CS_RETCODE rc, const char* msg, unsigned int msg_num)
80 {
81     try {
82         rc = Check(rc);
83     } catch (...) {
84         SetHasFailed();
85         throw;
86     }
87 
88     switch (rc) {
89     case CS_SUCCEED:
90         break;
91     case CS_FAIL:
92         SetHasFailed();
93         DATABASE_DRIVER_ERROR( msg, msg_num );
94 #ifdef CS_BUSY
95     case CS_BUSY:
96         DATABASE_DRIVER_ERROR( "the connection is busy", 122002 );
97 #endif
98     }
99 
100     return rc;
101 }
102 
103 
104 CS_RETCODE
CheckSFBCP(CS_RETCODE rc,const char * msg,unsigned int msg_num)105 CTL_CursorCmd::CheckSFBCP(CS_RETCODE rc, const char* msg, unsigned int msg_num)
106 {
107     try {
108         rc = Check(rc);
109     } catch (...) {
110         SetHasFailed();
111         throw;
112     }
113 
114     switch (rc) {
115     case CS_SUCCEED:
116         break;
117     case CS_FAIL:
118         SetHasFailed();
119         DATABASE_DRIVER_ERROR( msg, msg_num );
120 #ifdef CS_BUSY
121     case CS_BUSY:
122         DATABASE_DRIVER_ERROR( "the connection is busy", 122002 );
123 #endif
124     case CS_CANCELED:
125         DATABASE_DRIVER_ERROR( "command was canceled", 122008 );
126     case CS_PENDING:
127         DATABASE_DRIVER_ERROR( "connection has another request pending", 122007 );
128     }
129 
130     return rc;
131 }
132 
133 
134 bool
ProcessResults(void)135 CTL_CursorCmd::ProcessResults(void)
136 {
137     // process the results
138     for (;;) {
139         CS_INT res_type;
140 
141         if (CheckSFBCP(ct_results(x_GetSybaseCmd(), &res_type),
142                        "ct_result failed", 122045) == CS_END_RESULTS) {
143             return true;
144         }
145 
146         if (ProcessResultInternal(res_type)) {
147             continue;
148         }
149 
150         switch ( res_type ) {
151         case CS_CMD_SUCCEED:
152         case CS_CMD_DONE: // done with this command
153             continue;
154         case CS_CMD_FAIL: // the command has failed
155             SetHasFailed();
156             while(Check(ct_results(x_GetSybaseCmd(), &res_type)) == CS_SUCCEED)
157             {
158                 continue;
159             }
160             DATABASE_DRIVER_WARNING( "The server encountered an error while "
161                                "executing a command", 122049 );
162         default:
163             continue;
164         }
165     }
166 
167     return false;
168 }
169 
170 
171 CDB_Result*
OpenCursor()172 CTL_CursorCmd::OpenCursor()
173 {
174     // need to close it first
175     CloseCursor();
176 
177     CheckIsDead();
178 
179     if (!CursorIsDeclared()) {
180         SetHasFailed(false);
181 
182         CheckSFB(ct_cursor(x_GetSybaseCmd(), CS_CURSOR_DECLARE,
183                            const_cast<char*>(GetCmdName().data()),
184                            GetCmdName().size(),
185                            const_cast<char*>(GetQuery().data()),
186                            GetQuery().size(),
187                            CS_UNUSED),
188                  "ct_cursor(DECLARE) failed", 122001);
189 
190         if (GetBindParamsImpl().NofParams() > 0) {
191             // we do have the parameters
192             // check if query is a select statement or a function call
193             if (GetQuery().find("select") != string::npos  ||
194                 GetQuery().find("SELECT") != string::npos) {
195                 // this is a select
196                 SetHasFailed(!x_AssignParams(true));
197                 CHECK_DRIVER_ERROR( HasFailed(), "Cannot declare the params." +
198                                     GetDbgInfo(), 122003 );
199             }
200         }
201 
202         if (m_FetchSize > 1) {
203             CheckSFB(ct_cursor(x_GetSybaseCmd(), CS_CURSOR_ROWS, 0, CS_UNUSED,
204                                0, CS_UNUSED, (CS_INT) m_FetchSize),
205                      "ct_cursor(ROWS) failed", 122004);
206         }
207 
208         // Execute command ...
209         // Current inplementation of FreeTDS ctlib won't send CS_CURSOR_DECLARE
210         // command. So, error processing can be done only after CS_CURSOR_OPEN
211         // was sent.
212         {
213             // send this command
214             CheckSFBCP(ct_send(x_GetSybaseCmd()), "ct_send failed", 122006);
215 
216             // Check results of send ...
217             ProcessResults();
218         }
219 
220         SetCursorDeclared();
221     }
222 
223     SetHasFailed(false);
224 
225     // open cursor
226     CheckSFB(ct_cursor(x_GetSybaseCmd(), CS_CURSOR_OPEN, 0, CS_UNUSED, 0, CS_UNUSED,
227                        CursorIsDeclared() ? CS_RESTORE_OPEN : CS_UNUSED),
228              "ct_cursor(open) failed", 122005);
229 
230     if (GetBindParamsImpl().NofParams() > 0) {
231         // we do have parameters
232         SetHasFailed(!x_AssignParams(false));
233         CHECK_DRIVER_ERROR( HasFailed(), "Cannot assign the params." + GetDbgInfo(), 122003 );
234     }
235 
236     // send this command
237     CheckSFBCP(ct_send(x_GetSybaseCmd()), "ct_send failed", 122006);
238 
239     // Process results ....
240     for (;;) {
241         CS_INT res_type;
242 
243         try {
244             if (CheckSFBCP(ct_results(x_GetSybaseCmd(), &res_type),
245                         "ct_result failed", 122013) == CS_END_RESULTS) {
246                 return NULL;
247             }
248         } catch (...) {
249             // We have to fech out all pending  results ...
250             while (ct_results(x_GetSybaseCmd(), &res_type) == CS_SUCCEED) {
251                 continue;
252             }
253             throw;
254         }
255 
256         switch ( res_type ) {
257         case CS_CMD_SUCCEED:
258         case CS_CMD_DONE:
259             // done with this command -- check the number of affected rows
260             GetRowCount(&m_RowCount);
261 
262             continue;
263         case CS_CMD_FAIL:
264             // the command has failed -- check the number of affected rows
265             GetRowCount(&m_RowCount);
266             SetHasFailed();
267             while (Check(ct_results(x_GetSybaseCmd(), &res_type)) == CS_SUCCEED) {
268                 continue;
269             }
270             DATABASE_DRIVER_WARNING( "The server encountered an error while "
271                                "executing a command", 122016 );
272         case CS_CURSOR_RESULT:
273             // Cursor can be set open only after processing of ct_send, which does
274             // actual job.
275             SetCursorOpen();
276 
277             SetResult(MakeCursorResult());
278             break;
279         default:
280             continue;
281         }
282 
283         return Create_Result(static_cast<impl::CResult&>(GetResult()));
284     }
285 }
286 
287 
Update(const string & table_name,const string & upd_query)288 bool CTL_CursorCmd::Update(const string& table_name, const string& upd_query)
289 {
290     if (!CursorIsOpen()) {
291         return false;
292     }
293 
294     CheckIsDead();
295 
296     CheckSFB(ct_cursor(x_GetSybaseCmd(), CS_CURSOR_UPDATE,
297                        const_cast<char*>(table_name.data()), table_name.size(),
298                        const_cast<char*>(upd_query.data()),  upd_query.size(),
299                        CS_UNUSED),
300              "ct_cursor(update) failed", 122030);
301 
302     // send this command
303     CheckSFBCP(ct_send(x_GetSybaseCmd()), "ct_send failed", 122032);
304 
305     // process results
306     return ProcessResults();
307 }
308 
x_GetBlobDescriptor(unsigned int item_num)309 I_BlobDescriptor* CTL_CursorCmd::x_GetBlobDescriptor(unsigned int item_num)
310 {
311     if(!CursorIsOpen() || !HaveResult()) {
312         return 0;
313     }
314 
315     CheckIsDead();
316 
317     while ( static_cast<unsigned int>(GetResult().CurrentItemNo()) < item_num ) {
318         if(!GetResult().SkipItem()) return 0;
319     }
320 
321     unique_ptr<I_BlobDescriptor> desc(GetResult().GetBlobDescriptor(item_num));
322     if (desc.get() != NULL) {
323         CTL_BlobDescriptor& ctl_desc = static_cast<CTL_BlobDescriptor&>(*desc);
324         auto last_dot = strrchr(ctl_desc.m_Desc.name, '.');
325         if (last_dot == NULL) {
326             return desc.release();
327         }
328         string table(ctl_desc.m_Desc.name, last_dot), column(last_dot + 1);
329         auto &conn = GetConnection();
330         if (conn.x_IsLegacyBlobColumnType(table, column)) {
331             conn.CompleteBlobDescriptor(*desc, GetCmdName(), item_num);
332         } else if (desc->DescriptorType() == CTL_BLOB_DESCRIPTOR_TYPE_MAGNUM) {
333             desc.reset(new CTL_CursorBlobDescriptor
334                        (static_cast<CTL_CursorResult&>(GetResult()),
335                         table, column, ctl_desc.m_Desc.datatype));
336         }
337     }
338     return desc.release();
339 }
340 
UpdateBlob(unsigned int item_num,CDB_Stream & data,bool log_it)341 bool CTL_CursorCmd::UpdateBlob(unsigned int item_num, CDB_Stream& data,
342                                bool log_it)
343 {
344     I_BlobDescriptor* desc= x_GetBlobDescriptor(item_num);
345     unique_ptr<I_BlobDescriptor> d_guard(desc);
346 
347     return (desc) ? x_SendData(*desc, data, log_it) : false;
348 }
349 
SendDataCmd(unsigned int item_num,size_t size,bool log_it,bool dump_results)350 CDB_SendDataCmd* CTL_CursorCmd::SendDataCmd(unsigned int item_num, size_t size,
351                                             bool log_it,
352                                             bool dump_results)
353 {
354     I_BlobDescriptor* desc= x_GetBlobDescriptor(item_num);
355     unique_ptr<I_BlobDescriptor> d_guard(desc);
356 
357     return (desc) ? ConnSendDataCmd(*desc, size, log_it, dump_results) : 0;
358 }
359 
Delete(const string & table_name)360 bool CTL_CursorCmd::Delete(const string& table_name)
361 {
362     if (!CursorIsOpen()) {
363         return false;
364     }
365 
366     CheckIsDead();
367 
368     CheckSFB(ct_cursor(x_GetSybaseCmd(), CS_CURSOR_DELETE,
369                        const_cast<char*>(table_name.data()), table_name.size(),
370                        0, CS_UNUSED, CS_UNUSED),
371              "ct_cursor(delete) failed", 122040);
372 
373     // send this command
374     CheckSFBCP(ct_send(x_GetSybaseCmd()), "ct_send failed", 122042);
375 
376     // process the results
377     return ProcessResults();
378 }
379 
380 
RowCount() const381 int CTL_CursorCmd::RowCount() const
382 {
383     return m_RowCount;
384 }
385 
386 
CloseCursor(void)387 bool CTL_CursorCmd::CloseCursor(void)
388 {
389     if (!CursorIsOpen()) {
390         return false;
391     }
392 
393     DeleteResult();
394 
395     if (IsDead()) {
396         SetCursorOpen(false);
397         return true;
398     }
399 
400     CheckSFB(ct_cursor(x_GetSybaseCmd(),
401                        CS_CURSOR_CLOSE,
402                        0,
403                        CS_UNUSED,
404                        0,
405                        CS_UNUSED,
406                        CS_UNUSED),
407              "ct_cursor(close) failed", 122020);
408 
409     // send this command
410     CheckSFBCP(ct_send(x_GetSybaseCmd()), "ct_send failed", 122022);
411 
412     // Process results ...
413     bool result = ProcessResults();
414 
415     // An exception can be thrown in ProcessResults, so, we set the flag after
416     // calling of ProcessResults.
417     SetCursorOpen(!result);
418 
419     return result;
420 }
421 
422 
423 void
CloseForever(void)424 CTL_CursorCmd::CloseForever(void)
425 {
426     if (x_GetSybaseCmd()) {
427 
428         // ????
429         DetachInterface();
430 
431         CloseCursor();
432 
433         if (CursorIsDeclared()  &&  !IsDead()) {
434             // deallocate the cursor
435             switch ( Check(ct_cursor(x_GetSybaseCmd(), CS_CURSOR_DEALLOC,
436                                0, CS_UNUSED, 0, CS_UNUSED, CS_UNUSED)) ) {
437             case CS_SUCCEED:
438                 break;
439             case CS_FAIL:
440                 // SetHasFailed();
441                 //throw CDB_ClientEx(eDiag_Fatal, 122050, "::~CTL_CursorCmd",
442                 //                   "ct_cursor(dealloc) failed");
443 #ifdef CS_BUSY
444             case CS_BUSY:
445                 //throw CDB_ClientEx(eDiag_Error, 122051, "::~CTL_CursorCmd",
446                 //                   "the connection is busy");
447 #endif
448                 DropSybaseCmd();
449                 return;
450             }
451 
452             // send this command
453             switch ( Check(ct_send(x_GetSybaseCmd())) ) {
454             case CS_SUCCEED:
455                 break;
456             case CS_FAIL:
457                 // SetHasFailed();
458                 // throw CDB_ClientEx(eDiag_Error, 122052, "::~CTL_CursorCmd",
459                 //                   "ct_send failed");
460             case CS_CANCELED:
461                 // throw CDB_ClientEx(eDiag_Error, 122053, "::~CTL_CursorCmd",
462                 //                   "command was canceled");
463 #ifdef CS_BUSY
464             case CS_BUSY:
465 #endif
466             case CS_PENDING:
467                 // throw CDB_ClientEx(eDiag_Error, 122054, "::~CTL_CursorCmd",
468                 //                   "connection has another request pending");
469                 DropSybaseCmd();
470                 return;
471             }
472 
473             // process the results
474             try {
475                 ProcessResults();
476             } catch(CDB_ClientEx const&)
477             {
478                 // Just ignore ...
479                 _ASSERT(false);
480             }
481 
482         }
483 
484         DropSybaseCmd();
485     }
486 }
487 
488 
~CTL_CursorCmd()489 CTL_CursorCmd::~CTL_CursorCmd()
490 {
491     try {
492         DetachInterface();
493 
494         DropCmd(*this);
495 
496         CloseForever();
497     }
498     NCBI_CATCH_ALL_X( 2, NCBI_CURRENT_FUNCTION )
499 }
500 
501 
x_AssignParams(bool declare_only)502 bool CTL_CursorCmd::x_AssignParams(bool declare_only)
503 {
504     CS_DATAFMT param_fmt;
505     memset(&param_fmt, 0, sizeof(param_fmt));
506     param_fmt.status  = CS_INPUTVALUE;
507 
508     for (unsigned int i = 0;  i < GetBindParamsImpl().NofParams();  i++) {
509         if (GetBindParamsImpl().GetParamStatus(i) == 0) {
510             continue;
511         }
512 
513         CDB_Object& param = *GetBindParamsImpl().GetParam(i);
514         const string& param_name = GetBindParamsImpl().GetParamName(i);
515 
516         if ( !AssignCmdParam(param, param_name, param_fmt, declare_only) )
517         {
518             return false;
519         }
520     }
521 
522     GetBindParamsImpl().LockBinding();
523     return true;
524 }
525 
526 
527 /////////////////////////////////////////////////////////////////////////////
528 //
529 //  CTL_CursorCmdExpl::
530 //
531 
CTL_CursorCmdExpl(CTL_Connection & conn,const string & cursor_name,const string & query,unsigned int fetch_size)532 CTL_CursorCmdExpl::CTL_CursorCmdExpl(CTL_Connection& conn,
533                                      const string& cursor_name,
534                                      const string& query,
535                                      unsigned int fetch_size)
536     : CTL_Cmd(conn, cursor_name, query),
537       m_LCmd(nullptr),
538       m_Res(nullptr)
539 {
540     string extra_msg = "Cursor Name: \"" + cursor_name + "\"; SQL Command: \"" + query + "\"";
541     SetExecCntxInfo(extra_msg);
542 }
543 
544 
for_update_of(const string & q)545 static bool for_update_of(const string& q)
546 {
547     if((q.find("update") == string::npos) &&
548        (q.find("UPDATE") == string::npos))
549         return false;
550 
551     if((q.find("for update") != string::npos) ||
552        (q.find("FOR UPDATE") != string::npos))
553         return true;
554 
555     // TODO: add more logic here to find "for update" clause
556     return false;
557 }
558 
OpenCursor()559 CDB_Result* CTL_CursorCmdExpl::OpenCursor()
560 {
561     const bool connected_to_MSSQLServer =
562         GetConnection().GetServerType() == CDBConnParams::eMSSqlServer;
563 
564     // need to close it first
565     CloseCursor();
566 
567     SetHasFailed(false);
568 
569     // declare the cursor
570     SetHasFailed(!x_AssignParams());
571     CHECK_DRIVER_ERROR(
572         HasFailed(),
573         "Cannot assign params." + GetDbgInfo(),
574         122503 );
575 
576 
577     m_LCmd.reset(0);
578 
579     string buff;
580     if ( connected_to_MSSQLServer ) {
581         string cur_feat;
582 
583         if(for_update_of(GetCombinedQuery())) {
584             cur_feat = " cursor FORWARD_ONLY SCROLL_LOCKS for ";
585         } else {
586             cur_feat = " cursor FORWARD_ONLY for ";
587         }
588 
589         buff = "declare " + GetCmdName() + cur_feat + GetCombinedQuery();
590     } else {
591         // Sybase ...
592 
593         buff = "declare " + GetCmdName() + " cursor for " + GetCombinedQuery();
594     }
595 
596     try {
597         unique_ptr<CDB_LangCmd> cmd(GetConnection().LangCmd(buff));
598 
599         cmd->Send();
600         cmd->DumpResults();
601     } catch ( const CDB_Exception& e ) {
602         DATABASE_DRIVER_ERROR_EX( e, "Failed to declare cursor." + GetDbgInfo(), 122501 );
603     }
604 
605     SetCursorDeclared();
606 
607     // open the cursor
608     buff = "open " + GetCmdName();
609 
610     try {
611         unique_ptr<CDB_LangCmd> cmd(GetConnection().LangCmd(buff));
612 
613         cmd->Send();
614         cmd->DumpResults();
615     } catch ( const CDB_Exception& e ) {
616         DATABASE_DRIVER_ERROR_EX( e, "Failed to open cursor." + GetDbgInfo(), 122502 );
617     }
618 
619     SetCursorOpen();
620 
621     buff = "fetch " + GetCmdName();
622     m_LCmd.reset(GetConnection().xLangCmd(buff));
623     m_Res.reset(new CTL_CursorResultExpl(m_LCmd.get(), GetCmdName()));
624 
625     return Create_Result(*GetResultSet());
626 }
627 
628 
Update(const string &,const string & upd_query)629 bool CTL_CursorCmdExpl::Update(const string&, const string& upd_query)
630 {
631     if (!CursorIsOpen())
632         return false;
633 
634     try {
635         while(m_LCmd->HasMoreResults()) {
636             unique_ptr<CDB_Result> r(m_LCmd->Result());
637         }
638 
639         string buff = upd_query + " where current of " + GetCmdName();
640         const unique_ptr<CDB_LangCmd> cmd(GetConnection().LangCmd(buff));
641         cmd->Send();
642         cmd->DumpResults();
643 #if 0
644         while (cmd->HasMoreResults()) {
645             CDB_Result* r = cmd->Result();
646             if (r) {
647                 while (r->Fetch())
648                     ;
649                 delete r;
650             }
651         }
652 #endif
653     } catch ( const CDB_Exception& e ) {
654         DATABASE_DRIVER_ERROR_EX( e, "Update failed." + GetDbgInfo(), 122507 );
655     }
656 
657     return true;
658 }
659 
x_GetBlobDescriptor(unsigned int item_num)660 I_BlobDescriptor* CTL_CursorCmdExpl::x_GetBlobDescriptor(unsigned int item_num)
661 {
662     if(!CursorIsOpen() || !m_Res.get() || !m_LCmd.get()) {
663         return 0;
664     }
665     CheckIsDead();
666     while(static_cast<unsigned int>(m_Res->CurrentItemNo()) < item_num) {
667         if(!m_Res->SkipItem()) return 0;
668     }
669 
670     unique_ptr<I_BlobDescriptor> desc(m_Res->GetBlobDescriptor(item_num));
671     // if (desc.get() != NULL) {
672     //     GetConnection().CompleteBlobDescriptor(*desc, GetCmdName(), item_num);
673     // }
674     if (desc.get() != NULL
675         &&  desc->DescriptorType() == CTL_BLOB_DESCRIPTOR_TYPE_MAGNUM) {
676         CTL_BlobDescriptor* dsc = static_cast<CTL_BlobDescriptor*>(desc.get());
677         if (dsc->m_Desc.textptrlen <= 0
678             ||  memcmp(dsc->m_Desc.textptr, "dummy textptr\0\0", 16) == 0) {
679             string table, column;
680             NStr::SplitInTwo(dsc->m_Desc.name, ".", table, column);
681             desc.reset(new CTL_CursorBlobDescriptor(*m_Res, table, column,
682                                                   dsc->m_Desc.datatype));
683         }
684     }
685     return desc.release();
686 }
687 
UpdateBlob(unsigned int item_num,CDB_Stream & data,bool log_it)688 bool CTL_CursorCmdExpl::UpdateBlob(unsigned int item_num, CDB_Stream& data,
689                                    bool log_it)
690 {
691     I_BlobDescriptor* desc= x_GetBlobDescriptor(item_num);
692     unique_ptr<I_BlobDescriptor> d_guard(desc);
693 
694     if(desc) {
695         while(m_LCmd->HasMoreResults()) {
696             CDB_Result* r= m_LCmd->Result();
697             if(r) delete r;
698         }
699 
700         return GetConnection().x_SendData(*desc, data, log_it);
701     }
702     return false;
703 }
704 
SendDataCmd(unsigned int item_num,size_t size,bool log_it,bool dump_results)705 CDB_SendDataCmd* CTL_CursorCmdExpl::SendDataCmd(unsigned int item_num, size_t size,
706                                                 bool log_it,
707                                                 bool dump_results)
708 {
709     I_BlobDescriptor* desc= x_GetBlobDescriptor(item_num);
710     unique_ptr<I_BlobDescriptor> d_guard(desc);
711 
712     if(desc) {
713         m_LCmd->DumpResults();
714 #if 0
715         while(m_LCmd->HasMoreResults()) {
716             CDB_Result* r= m_LCmd->Result();
717             if(r) delete r;
718         }
719 #endif
720 
721         return GetConnection().SendDataCmd(*desc, size, log_it, dump_results);
722     }
723     return 0;
724 }
725 
Delete(const string & table_name)726 bool CTL_CursorCmdExpl::Delete(const string& table_name)
727 {
728     if (!CursorIsOpen())
729         return false;
730 
731     CDB_LangCmd* cmd = 0;
732 
733     try {
734         while(m_LCmd->HasMoreResults()) {
735             CDB_Result* r= m_LCmd->Result();
736             if(r) delete r;
737         }
738 
739         string buff = "delete " + table_name + " where current of " + GetCmdName();
740         cmd = GetConnection().LangCmd(buff);
741         cmd->Send();
742         cmd->DumpResults();
743 #if 0
744         while (cmd->HasMoreResults()) {
745             CDB_Result* r = cmd->Result();
746             if (r) {
747                 while (r->Fetch())
748                     ;
749                 delete r;
750             }
751         }
752 #endif
753         delete cmd;
754     } catch ( const CDB_Exception& e ) {
755         if (cmd)
756             delete cmd;
757         DATABASE_DRIVER_ERROR_EX( e, "Update failed." + GetDbgInfo(), 122506 );
758     }
759 
760     return true;
761 }
762 
763 
RowCount() const764 int CTL_CursorCmdExpl::RowCount() const
765 {
766     return m_RowCount;
767 }
768 
769 
CloseCursor()770 bool CTL_CursorCmdExpl::CloseCursor()
771 {
772     if (!CursorIsOpen())
773         return false;
774 
775     m_Res.reset(0);
776 
777     m_LCmd.reset(0);
778 
779     if (CursorIsOpen()) {
780         string buff = "close " + GetCmdName();
781         try {
782             m_LCmd.reset(GetConnection().xLangCmd(buff));
783             m_LCmd->Send();
784             m_LCmd->DumpResults();
785 #if 0
786             while (m_LCmd->HasMoreResults()) {
787                 CDB_Result* r = m_LCmd->Result();
788                 if (r) {
789                     while (r->Fetch())
790                         ;
791                     delete r;
792                 }
793             }
794 #endif
795             m_LCmd.reset(0);
796         } catch ( const CDB_Exception& e ) {
797             m_LCmd.reset(0);
798             DATABASE_DRIVER_ERROR_EX( e, "Failed to close cursor." + GetDbgInfo(), 122504 );
799         }
800 
801         SetCursorOpen(false);
802     }
803 
804     if (CursorIsDeclared()) {
805         string buff;
806         if (GetConnection().GetServerType() == CDBConnParams::eMSSqlServer)
807             buff = "deallocate ";
808         else
809             buff = "deallocate cursor ";
810         buff += GetCmdName();
811 
812         try {
813             m_LCmd.reset(GetConnection().xLangCmd(buff));
814             m_LCmd->Send();
815             m_LCmd->DumpResults();
816 #if 0
817             while (m_LCmd->HasMoreResults()) {
818                 CDB_Result* r = m_LCmd->Result();
819                 if (r) {
820                     while (r->Fetch())
821                         ;
822                     delete r;
823                 }
824             }
825 #endif
826             m_LCmd.reset(0);
827         } catch ( const CDB_Exception& e) {
828             m_LCmd.reset(0);
829             DATABASE_DRIVER_ERROR_EX( e, "Failed to deallocate cursor." + GetDbgInfo(), 122505 );
830         }
831 
832         SetCursorDeclared(false);
833     }
834 
835     return true;
836 }
837 
838 
~CTL_CursorCmdExpl()839 CTL_CursorCmdExpl::~CTL_CursorCmdExpl()
840 {
841     try {
842         DetachInterface();
843 
844         GetConnection().DropCmd(*this);
845 
846         CloseCursor();
847     }
848     NCBI_CATCH_ALL_X( 2, NCBI_CURRENT_FUNCTION )
849 }
850 
851 
x_AssignParams()852 bool CTL_CursorCmdExpl::x_AssignParams()
853 {
854     m_CombinedQuery = GetQuery();
855 
856     for (unsigned int n = 0; n < GetBindParamsImpl().NofParams(); n++) {
857         const string& name = GetBindParamsImpl().GetParamName(n);
858         if (name.empty())
859             continue;
860         CDB_Object& param = *GetBindParamsImpl().GetParam(n);
861         char val_buffer[16*1024];
862 
863         if (!param.IsNULL()) {
864             switch (param.GetType()) {
865             case eDB_Bit:
866                 DATABASE_DRIVER_ERROR("Bit data type is not supported", 10005);
867                 break;
868             case eDB_Int: {
869                 CDB_Int& val = dynamic_cast<CDB_Int&> (param);
870                 sprintf(val_buffer, "%d", val.Value());
871                 break;
872             }
873             case eDB_SmallInt: {
874                 CDB_SmallInt& val = dynamic_cast<CDB_SmallInt&> (param);
875                 sprintf(val_buffer, "%d", (int) val.Value());
876                 break;
877             }
878             case eDB_TinyInt: {
879                 CDB_TinyInt& val = dynamic_cast<CDB_TinyInt&> (param);
880                 sprintf(val_buffer, "%d", (int) val.Value());
881                 break;
882             }
883             case eDB_BigInt: {
884 				// May have problems similar to Test_Procedure2.
885                 CDB_BigInt& val = dynamic_cast<CDB_BigInt&> (param);
886                 string s8 = NStr::Int8ToString(val.Value());
887                 s8.copy(val_buffer, s8.size());
888                 val_buffer[s8.size()] = '\0';
889                 break;
890             }
891             case eDB_Char:
892             case eDB_VarChar:
893             case eDB_LongChar: {
894                 CDB_String& val = dynamic_cast<CDB_String&> (param);
895                 const string& s = val.AsString(); // NB: 255 bytes at most
896                 string::const_iterator c = s.begin();
897                 size_t i = 0;
898                 val_buffer[i++] = '\'';
899                 while (c != s.end()  &&  i < sizeof(val_buffer) - 2) {
900                     if (*c == '\'')
901                         val_buffer[i++] = '\'';
902                     val_buffer[i++] = *c++;
903                 }
904                 if (c != s.end()) return false;
905                 val_buffer[i++] = '\'';
906                 val_buffer[i] = '\0';
907                 break;
908             }
909             case eDB_Binary: {
910                 CDB_Binary& val = dynamic_cast<CDB_Binary&> (param);
911                 impl::binary_to_hex_string(val_buffer, sizeof(val_buffer),
912                                            val.Value(), val.Size());
913                 break;
914             }
915             case eDB_VarBinary: {
916                 CDB_VarBinary& val = dynamic_cast<CDB_VarBinary&> (param);
917                 impl::binary_to_hex_string(val_buffer, sizeof(val_buffer),
918                                            val.Value(), val.Size());
919                 break;
920             }
921             case eDB_LongBinary: {
922                 CDB_LongBinary& val = dynamic_cast<CDB_LongBinary&> (param);
923                 if (impl::binary_to_hex_string(val_buffer, sizeof(val_buffer),
924                                                val.Value(), val.DataSize())
925                     == 0) {
926                     return false;
927                 }
928                 break;
929             }
930             case eDB_Float: {
931                 CDB_Float& val = dynamic_cast<CDB_Float&> (param);
932                 sprintf(val_buffer, "%E", (double) val.Value());
933                 break;
934             }
935             case eDB_Double: {
936                 CDB_Double& val = dynamic_cast<CDB_Double&> (param);
937                 sprintf(val_buffer, "%E", val.Value());
938                 break;
939             }
940             case eDB_SmallDateTime: {
941                 CDB_SmallDateTime& val =
942                     dynamic_cast<CDB_SmallDateTime&> (param);
943                 string t = val.Value().AsString("M/D/Y h:m");
944                 sprintf(val_buffer, "'%s'", t.c_str());
945                 break;
946             }
947             case eDB_DateTime: {
948                 CDB_DateTime& val =
949                     dynamic_cast<CDB_DateTime&> (param);
950                 string t = val.Value().AsString("M/D/Y h:m:s");
951                 sprintf(val_buffer, "'%s:%.3d'", t.c_str(),
952             (int)(val.Value().NanoSecond()/1000000));
953                 break;
954             }
955             case eDB_BigDateTime: {
956                 CDB_BigDateTime& val =
957                     dynamic_cast<CDB_BigDateTime&> (param);
958                 CTime lt = val.GetCTime().GetLocalTime();
959                 lt.SetNanoSecond(lt.NanoSecond() / 100 * 100);
960                 string t = lt.AsString(CDB_BigDateTime::GetTimeFormat
961                                        (GetConnection().GetDateTimeSyntax(),
962                                         val.GetSQLType()));
963                 sprintf(val_buffer, "'%s'", t.c_str());
964                 break;
965             }
966             default:
967                 return false;
968             }
969         } else
970             strcpy(val_buffer, "NULL");
971 
972         // substitute the param
973         m_CombinedQuery = impl::g_SubstituteParam(m_CombinedQuery, name, val_buffer);
974     }
975 
976     return true;
977 }
978 
979 
980 #ifdef FTDS_IN_USE
981 } // namespace NCBI_NS_FTDS_CTLIB
982 #endif
983 
984 END_NCBI_SCOPE
985 
986 
987