1 /* $Id: bcp.cpp 635724 2021-08-09 15:03:07Z 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:  Vladimir Soussov
27  *
28  * File Description:  CTLib bcp-in command
29  *
30  */
31 
32 
33 #include <ncbi_pch.hpp>
34 #include <dbapi/driver/ctlib/interfaces.hpp>
35 #include <dbapi/driver/util/numeric_convert.hpp>
36 #include <dbapi/error_codes.hpp>
37 #include <string.h>
38 
39 
40 #define NCBI_USE_ERRCODE_X   Dbapi_CTlib_Cmds
41 
42 #undef NCBI_DATABASE_THROW
43 #define NCBI_DATABASE_THROW(ex_class, message, err_code, severity) \
44     NCBI_DATABASE_THROW_ANNOTATED(ex_class, message, err_code, severity, \
45         GetDbgInfo(), GetConnection(), GetLastParams())
46 // No use of NCBI_DATABASE_RETHROW or DATABASE_DRIVER_*_EX here.
47 
48 BEGIN_NCBI_SCOPE
49 
50 #ifdef FTDS_IN_USE
51 #  if NCBI_FTDS_VERSION > 91 /* defined(CS_BIGINT_TYPE) */
52 #    define NCBI_CS_BIGINT_TYPE CS_BIGINT_TYPE
53 #  else
54 #    define NCBI_CS_BIGINT_TYPE CS_LONG_TYPE
55 #  endif
56 
57 namespace NCBI_NS_FTDS_CTLIB
58 {
59 #endif
60 
61 
62 ///////////////////////////////////////////////////////////////////////////
63 //
64 //  CTL_BCPInCmd::
65 //
66 
CTL_BCPInCmd(CTL_Connection & conn,const string & table_name)67 CTL_BCPInCmd::CTL_BCPInCmd(CTL_Connection& conn,
68                            const string& table_name)
69 : CTL_CmdBase(conn, table_name)
70 , m_RowCount(0)
71 {
72     CheckSF(
73         blk_alloc(
74             GetConnection().GetNativeConnection().GetNativeHandle(),
75             GetConnection().GetBLKVersion(),
76             &m_Cmd
77             ),
78         "blk_alloc failed", 110004
79         );
80 
81     SetExecCntxInfo("BCP table name: " + table_name);
82 }
83 
84 
85 CS_RETCODE
CheckSF(CS_RETCODE rc,const char * msg,unsigned int msg_num)86 CTL_BCPInCmd::CheckSF(CS_RETCODE rc, const char* msg, unsigned int msg_num)
87 {
88     switch (Check(rc)) {
89     case CS_SUCCEED:
90         break;
91     case CS_FAIL:
92         SetHasFailed();
93         DATABASE_DRIVER_ERROR( msg, msg_num );
94     }
95 
96     return rc;
97 }
98 
99 
100 
101 CS_RETCODE
CheckSFB(CS_RETCODE rc,const char * msg,unsigned int msg_num)102 CTL_BCPInCmd::CheckSFB(CS_RETCODE rc, const char* msg, unsigned int msg_num)
103 {
104     switch (Check(rc)) {
105     case CS_SUCCEED:
106         break;
107     case CS_FAIL:
108         SetHasFailed();
109         DATABASE_DRIVER_ERROR( msg, msg_num );
110 #ifdef CS_BUSY
111     case CS_BUSY:
112         DATABASE_DRIVER_ERROR( "the connection is busy", 122002 );
113 #endif
114     }
115 
116     return rc;
117 }
118 
119 
120 CS_RETCODE
CheckSentSFB(CS_RETCODE rc,const char * msg,unsigned int msg_num)121 CTL_BCPInCmd::CheckSentSFB(CS_RETCODE rc, const char* msg, unsigned int msg_num)
122 {
123     switch (Check(rc)) {
124     case CS_SUCCEED:
125         SetWasSent(false);
126         break;
127     case CS_FAIL:
128         SetHasFailed();
129         DATABASE_DRIVER_ERROR( msg, msg_num );
130 #ifdef CS_BUSY
131     case CS_BUSY:
132         SetWasSent(false);
133         // DATABASE_DRIVER_ERROR( "the connection is busy", 122002 );
134 #endif
135     }
136 
137     return rc;
138 }
139 
140 
141 
Bind(unsigned int column_num,CDB_Object * pVal)142 bool CTL_BCPInCmd::Bind(unsigned int column_num, CDB_Object* pVal)
143 {
144     return GetBindParamsImpl().BindParam(column_num, kEmptyStr, pVal);
145 }
146 
147 
x_IsUnicodeClientAPI(void) const148 bool CTL_BCPInCmd::x_IsUnicodeClientAPI(void) const
149 {
150     switch (GetConnection().GetCTLibContext().GetTDSVersion()) {
151     case 70:
152     case 80:
153         return true;
154     };
155 
156     return false;
157 }
158 
159 
160 CTempString
x_GetStringValue(unsigned int i)161 CTL_BCPInCmd::x_GetStringValue(unsigned int i)
162 {
163     CDB_String& value
164         = static_cast<CDB_String&>(*GetBindParamsImpl().GetParam(i));
165     CTempString ts;
166 
167 // #if defined(HAVE_WSTRING)
168 //     if (x_IsUnicodeClientAPI()) {
169 //         const wstring& ws = value.AsWString(eEncoding_UTF8);
170 //         ts.assign((const char*)ws.data(), ws.size() * sizeof(wchar_t));
171 //     }
172 // #endif
173 
174     value.GetBulkInsertionData(&ts);
175 #ifdef USE_STRUCT_CS_VARCHAR
176     static const auto kMax = numeric_limits<decltype(CS_VARCHAR::len)>::max();
177     if (ts.size() > kMax) {
178         string msg = FORMAT("Value for column " << (i + 1)
179                             << " is too wide for [N]VARCHAR: " << ts.size()
180                             << " > " << kMax);
181         DATABASE_DRIVER_ERROR(msg, 123004);
182     }
183 #endif
184     SBcpBind& b = GetBind()[i];
185     b.varchar.SetValue(ts);
186     return b.varchar.GetValue();
187 }
188 
189 
x_AssignParams()190 bool CTL_BCPInCmd::x_AssignParams()
191 {
192     CS_DATAFMT param_fmt;
193     memset(&param_fmt, 0, sizeof(param_fmt));
194     param_fmt.format = CS_FMT_UNUSED;
195     param_fmt.count  = 1;
196 
197     for (unsigned int i = 0;  i < GetBindParamsImpl().NofParams();  i++) {
198 
199         if (GetBindParamsImpl().GetParamStatus(i) == 0)
200             continue;
201 
202         CDB_Object& param = *GetBindParamsImpl().GetParam(i);
203         SBcpBind& bind = GetBind()[i];
204         bind.indicator = param.IsNULL() ? -1 : 0;
205         bind.datalen   = (bind.indicator == 0) ? CS_UNUSED : 0;
206 
207         CS_RETCODE ret_code;
208 
209         switch ( param.GetType() ) {
210         case eDB_Bit: {
211             CDB_Bit& par = dynamic_cast<CDB_Bit&> (param);
212             param_fmt.datatype = CS_BIT_TYPE;
213             CS_BOOL value = (CS_BOOL) par.Value();
214             memcpy(bind.buffer, &value, sizeof(CS_BOOL));
215             bind.datalen = bind.indicator + 1;
216             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
217                                       (CS_VOID*) bind.buffer,
218                                       &bind.datalen,
219                                       &bind.indicator));
220             break;
221         }
222         case eDB_Int: {
223             CDB_Int& par = dynamic_cast<CDB_Int&> (param);
224             param_fmt.datatype = CS_INT_TYPE;
225             CS_INT value = (CS_INT) par.Value();
226             memcpy(bind.buffer, &value, sizeof(CS_INT));
227             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
228                                       (CS_VOID*) bind.buffer,
229                                       &bind.datalen,
230                                       &bind.indicator));
231             break;
232         }
233         case eDB_SmallInt: {
234             CDB_SmallInt& par = dynamic_cast<CDB_SmallInt&> (param);
235             param_fmt.datatype = CS_SMALLINT_TYPE;
236             CS_SMALLINT value = (CS_SMALLINT) par.Value();
237             memcpy(bind.buffer, &value, sizeof(CS_SMALLINT));
238             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
239                                       (CS_VOID*) bind.buffer,
240                                       &bind.datalen,
241                                       &bind.indicator));
242             break;
243         }
244         case eDB_TinyInt: {
245             CDB_TinyInt& par = dynamic_cast<CDB_TinyInt&> (param);
246             param_fmt.datatype = CS_TINYINT_TYPE;
247             CS_TINYINT value = (CS_TINYINT) par.Value();
248             memcpy(bind.buffer, &value, sizeof(CS_TINYINT));
249             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
250                                       (CS_VOID*) bind.buffer,
251                                       &bind.datalen,
252                                       &bind.indicator));
253             break;
254         }
255         case eDB_BigInt: {
256             CDB_BigInt& par = dynamic_cast<CDB_BigInt&> (param);
257 
258 #ifdef FTDS_IN_USE
259             param_fmt.datatype = NCBI_CS_BIGINT_TYPE;
260             Int8 value = par.Value();
261             memcpy(bind.buffer, &value, sizeof(value));
262 #else
263             // Old code ...
264             param_fmt.datatype = CS_NUMERIC_TYPE;
265             CS_NUMERIC value;
266             Int8 val8 = par.Value();
267             memset(&value, 0, sizeof(value));
268             value.precision = 20;
269             if (longlong_to_numeric(val8, 20, value.array) == 0)
270                 return false;
271             param_fmt.scale     = 0;
272             param_fmt.precision = 20;
273             memcpy(bind.buffer, &value, sizeof(CS_NUMERIC));
274             bind.datalen = sizeof(CS_NUMERIC);
275 #endif
276 
277             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
278                                       (CS_VOID*) bind.buffer,
279                                       &bind.datalen,
280                                       &bind.indicator));
281             break;
282         }
283         case eDB_Char:
284         case eDB_VarChar: {
285             CDB_String& par = dynamic_cast<CDB_String&> (param);
286             param_fmt.datatype  = NCBI_CS_STRING_TYPE;
287             param_fmt.maxlength = (CS_INT) par.Size() + 1;
288             CTempString ts = x_GetStringValue(i);
289             bind.datalen   =
290                 (bind.indicator == -1) ? 0 : (CS_INT) ts.size();
291             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
292                                       (CS_VOID*) ts.data(),
293                                       &bind.datalen,
294                                       &bind.indicator));
295             break;
296         }
297         case eDB_LongChar: {
298             CDB_LongChar& par = dynamic_cast<CDB_LongChar&> (param);
299             CTempString data;
300             par.GetBulkInsertionData(&data);
301             param_fmt.datatype  = CS_LONGCHAR_TYPE;
302             param_fmt.maxlength = (CS_INT) par.Size() + 1;
303             bind.datalen   =
304                 (bind.indicator == -1) ? 0 : (CS_INT) data.size();
305             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
306                                       par.IsNULL()? (CS_VOID*)bind.buffer :
307                                       (CS_VOID*) data.data(),
308                                       &bind.datalen,
309                                       &bind.indicator));
310             break;
311         }
312         case eDB_Binary: {
313             CDB_Binary& par = dynamic_cast<CDB_Binary&> (param);
314             param_fmt.datatype  = CS_BINARY_TYPE;
315             param_fmt.maxlength = (CS_INT) par.Size() + 1;
316             bind.datalen   =
317                 (bind.indicator == -1) ? 0 : (CS_INT) par.Size();
318             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
319                                       par.IsNULL()? (CS_VOID*)bind.buffer :
320                                         (CS_VOID*) par.Value(),
321                                       &bind.datalen,
322                                       &bind.indicator));
323             break;
324         }
325         case eDB_LongBinary: {
326             CDB_LongBinary& par = dynamic_cast<CDB_LongBinary&> (param);
327             param_fmt.datatype  = CS_LONGBINARY_TYPE;
328             param_fmt.maxlength = (CS_INT) par.Size();
329             bind.datalen   =
330                 (bind.indicator == -1) ? 0 : (CS_INT) par.DataSize();
331             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
332                         // par.Value() may return NULL. But NULL has a special
333                         // meaning for this parameter. So, it is replaced a by
334                         // a fake pointer (bind.buffer).
335                                       par.IsNULL()? (CS_VOID*)bind.buffer :
336                                         (CS_VOID*) par.Value(),
337                                       &bind.datalen,
338                                       &bind.indicator));
339             break;
340         }
341         case eDB_VarBinary: {
342             CDB_VarBinary& par = dynamic_cast<CDB_VarBinary&> (param);
343             param_fmt.datatype  = CS_BINARY_TYPE;
344             param_fmt.maxlength = (CS_INT) par.Size() + 1;
345             bind.datalen   = (CS_INT) par.Size();
346             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
347                                       par.IsNULL()? (CS_VOID*)bind.buffer :
348                                         (CS_VOID*) par.Value(),
349                                       &bind.datalen,
350                                       &bind.indicator));
351             break;
352         }
353         case eDB_Float: {
354             CDB_Float& par = dynamic_cast<CDB_Float&> (param);
355             param_fmt.datatype = CS_REAL_TYPE;
356             CS_REAL value = (CS_REAL) par.Value();
357             memcpy(bind.buffer, &value, sizeof(CS_REAL));
358             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
359                                       (CS_VOID*) bind.buffer,
360                                       &bind.datalen,
361                                       &bind.indicator));
362             break;
363         }
364         case eDB_Double: {
365             CDB_Double& par = dynamic_cast<CDB_Double&> (param);
366             param_fmt.datatype = CS_FLOAT_TYPE;
367             CS_FLOAT value = (CS_FLOAT) par.Value();
368             memcpy(bind.buffer, &value, sizeof(CS_FLOAT));
369             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
370                                       (CS_VOID*) bind.buffer,
371                                       &bind.datalen,
372                                       &bind.indicator));
373             break;
374         }
375         case eDB_SmallDateTime: {
376             CDB_SmallDateTime& par = dynamic_cast<CDB_SmallDateTime&> (param);
377             param_fmt.datatype = CS_DATETIME4_TYPE;
378 
379             CS_DATETIME4 dt;
380             if (param.IsNULL()) {
381                 dt.days    = 0;
382                 dt.minutes = 0;
383             } else {
384                 dt.days    = par.GetDays();
385                 dt.minutes = par.GetMinutes();
386             }
387 
388             memcpy(bind.buffer, &dt, sizeof(CS_DATETIME4));
389             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
390                                       (CS_VOID*) bind.buffer,
391                                       &bind.datalen,
392                                       &bind.indicator));
393             break;
394         }
395         case eDB_DateTime: {
396             CDB_DateTime& par = dynamic_cast<CDB_DateTime&> (param);
397             param_fmt.datatype = CS_DATETIME_TYPE;
398 
399             CS_DATETIME dt;
400             if (param.IsNULL()) {
401                 dt.dtdays = 0;
402                 dt.dttime = 0;
403             } else {
404                 dt.dtdays = par.GetDays();
405                 dt.dttime = par.Get300Secs();
406             }
407 
408             _ASSERT(sizeof(SBcpBind::buffer) >= sizeof(CS_DATETIME));
409             memcpy(bind.buffer, &dt, sizeof(CS_DATETIME));
410             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
411                                       (CS_VOID*) bind.buffer,
412                                       &bind.datalen,
413                                       &bind.indicator));
414             break;
415         }
416         case eDB_BigDateTime: {
417             CDB_BigDateTime& par = dynamic_cast<CDB_BigDateTime&> (param);
418             auto syntax = GetConnection().GetDateTimeSyntax();
419             bool need_date = true, need_time = true;
420             if (syntax == CDB_BigDateTime::eSyntax_Sybase) {
421                 // Sybase claims to use classic DATETIME when working
422                 // with clients that don't advertise support for newer
423                 // types, but expects BCP-ed data to use new formats
424                 // when the corresponding columns do.
425                 switch (par.GetSQLType()) {
426                 case CDB_BigDateTime::eDate:
427                     need_time = false;
428 #ifdef CS_DATE_TYPE
429                     param_fmt.datatype = CS_DATE_TYPE;
430                     bind.datalen       = sizeof(CS_DATE_TYPE);
431 #else
432                     param_fmt.datatype = CS_DATETIME_TYPE;
433                     bind.datalen       = 4; // 8?  Might not work either way.
434 #endif
435                     break;
436                 case CDB_BigDateTime::eTime:
437                     need_date = false;
438 #ifdef CS_BIGTIME_TYPE
439                     param_fmt.datatype = CS_BIGTIME_TYPE;
440                     bind.datalen       = sizeof(CS_BIGTIME_TYPE);
441 #else
442                     param_fmt.datatype = CS_DATETIME_TYPE;
443                     bind.datalen       = 8;
444 #endif
445                     break;
446                 case CDB_BigDateTime::eDateTime:
447                 case CDB_BigDateTime::eDateTimeOffset:
448 #ifdef CS_BIGTIME_TYPE
449                     param_fmt.datatype = CS_BIGDATETIME_TYPE;
450                     bind.datalen       = sizeof(CS_BIGDATETIME_TYPE);
451 #else
452                     param_fmt.datatype = CS_DATETIME_TYPE;
453                     bind.datalen       = 8;
454 #endif
455                     break;
456                 }
457 
458 #ifdef CS_DATE_TYPE
459                 CS_DATE    days = 0;
460 #else
461                 CS_INT     days = 0;
462 #endif
463 #ifdef CS_BIGTIME_TYPE
464                 CS_BIGTIME us   = 0;
465 #else
466                 Uint8      us   = 0;
467 #endif
468                 if (param.IsNULL()) {
469                     bind.datalen = 0;
470                 } else {
471 #  ifndef FTDS_IN_USE
472                     bind.datalen = CS_UNUSED;
473 #  endif
474                     CTime t = par.GetCTime().GetLocalTime();
475                     if (need_date) {
476                         days = t.DiffWholeDays(CTime(1900, 1, 1));
477                     }
478                     if (need_time) {
479                         us = (((t.Hour() * 60 + t.Minute()) * 60 + t.Second())
480                               * NCBI_CONST_UINT8(1000000) + t.MicroSecond());
481                     }
482                 }
483 
484                 if ( !need_time ) {
485                     memcpy(bind.buffer, &days, sizeof(days));
486                 } else if ( !need_date ) {
487                     memcpy(bind.buffer, &us, sizeof(us));
488                 } else {
489                     static const Uint8 kMicrosecPerDay
490                         = NCBI_CONST_UINT8(86400000000);
491 #ifdef CS_BIGDATETIME_TYPE
492                     CS_BIGDATETIME dt
493 #else
494                     Uint8 dt
495 #endif
496                         = (days + 693961) * kMicrosecPerDay + us;
497                     memcpy(bind.buffer, &dt, sizeof(dt));
498                 }
499             } else {
500                 const CTime& t  = par.GetCTime();
501                 CS_DATETIME  dt = { 0, 0 };
502                 param_fmt.datatype = CS_DATETIME_TYPE;
503                 if ( !param.IsNULL() ) {
504                     TDBTimeI dbt = t.GetTimeDBI();
505                     if (CTime().SetTimeDBI(dbt) == t) {
506                         dt.dtdays = dbt.days;
507                         dt.dttime = dbt.time;
508                     } else {
509                         param_fmt.datatype = CS_CHAR_TYPE;
510                     }
511                 }
512 
513                 if (param_fmt.datatype == CS_CHAR_TYPE) {
514                     string s = (t.GetLocalTime()
515                                 .AsString(CDB_BigDateTime::GetTimeFormat
516                                           (syntax, par.GetSQLType())));
517                     if (syntax == CDB_BigDateTime::eSyntax_Microsoft
518                         &&  GetConnection().m_TDSVersion > CS_TDS_50
519 #ifdef CS_TDS_73
520                         &&  GetConnection().m_TDSVersion < CS_TDS_73
521 #endif
522                         ) {
523                         param_fmt.maxlength
524                             = sizeof(TCharUCS2) * (s.size() + 1);
525                         _ASSERT((size_t) param_fmt.maxlength
526                                 <= sizeof(SBcpBind::buffer));
527                         TStringUCS2 ws = CUtf8::AsBasicString<TCharUCS2>(s);
528                         memcpy(bind.buffer, ws.c_str(), param_fmt.maxlength);
529                         bind.datalen = sizeof(TCharUCS2) * s.size();
530                     } else {
531                         _ASSERT(s.size() < sizeof(SBcpBind::buffer));
532                         memcpy(bind.buffer, s.c_str(), s.size() + 1);
533                         bind.datalen = s.size();
534                         param_fmt.maxlength = s.size() + 1;
535                     }
536                 } else {
537                     memcpy(bind.buffer, &dt, sizeof(CS_DATETIME));
538                 }
539             }
540             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
541                                       (CS_VOID*) bind.buffer,
542                                       &bind.datalen,
543                                       &bind.indicator));
544             break;
545         }
546         case eDB_Text:
547         case eDB_VarCharMax: {
548             CDB_Stream& par = dynamic_cast<CDB_Stream&> (param);
549             param_fmt.datatype  = CS_TEXT_TYPE;
550             param_fmt.maxlength = (CS_INT) par.Size();
551             bind.datalen   = (CS_INT) par.Size();
552             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
553                                       0, &bind.datalen,
554                                       &bind.indicator));
555             break;
556         }
557         case eDB_Image:
558         case eDB_VarBinaryMax: {
559             CDB_Stream& par = dynamic_cast<CDB_Stream&> (param);
560             param_fmt.datatype  = CS_IMAGE_TYPE;
561             param_fmt.maxlength = (CS_INT) par.Size();
562             bind.datalen   = (CS_INT) par.Size();
563             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
564                                       0, &bind.datalen,
565                                       &bind.indicator));
566             break;
567         }
568         case eDB_Numeric: {
569             CDB_Numeric& par = dynamic_cast<CDB_Numeric&> (param);
570             param_fmt.datatype = CS_NUMERIC_TYPE;
571 
572             CS_NUMERIC numeric;
573             if ( !param.IsNULL() ) {
574                 numeric.precision = par.Precision();
575                 numeric.scale     = par.Scale();
576                 memcpy(numeric.array, par.RawData(), sizeof(numeric.array));
577 
578                 param_fmt.precision = numeric.precision;
579                 param_fmt.scale     = numeric.scale;
580 
581                 // cutoffs per
582                 // http://msdn.microsoft.com/en-us/library/ms187746.aspx
583                 int precision = par.Precision();
584                 if (precision < 10) {
585                     bind.datalen = 5;
586                 } else if (precision < 20) {
587                     bind.datalen = 9;
588                 } else if (precision < 29) {
589                     bind.datalen = 13;
590                 } else {
591                     bind.datalen = 17;
592                 }
593 
594                 memcpy(bind.buffer, &numeric, sizeof(CS_NUMERIC));
595             }
596             ret_code = Check(blk_bind(x_GetSybaseCmd(), i + 1, &param_fmt,
597                                       (CS_VOID*) bind.buffer,
598                                       &bind.datalen,
599                                       &bind.indicator));
600 
601             break;
602         }
603 
604         default:
605             return false;
606         }
607 
608         if (ret_code != CS_SUCCEED) {
609             return false;
610         }
611     }
612 
613     GetBindParamsImpl().LockBinding();
614     return true;
615 }
616 
617 
618 #if 0 // defined(HAVE_WSTRING)
619 static
620 string MakeUCS2LE(const wstring& str)
621 {
622     string result;
623 
624 #if defined(WORDS_BIGENDIAN)
625     if (!str.empty()) {
626         result.resize(str.size() * 2);
627         for(wstring::size_type i = 0; i < str.size(); ++i) {
628             wchar_t chracter = str.at(i);
629 
630             result.at(i * 2) = (chracter & 0x000000FF);
631             result.at(i * 2 + 1) = (chracter & 0x0000FF00);
632         }
633     }
634 #else
635     result.assign((const char*)str.data(), str.size() * sizeof(wchar_t));
636 #endif
637 
638     return result;
639 }
640 #endif
641 
642 
Send(void)643 bool CTL_BCPInCmd::Send(void)
644 {
645     unsigned int i;
646     CS_INT       datalen = 0;
647     size_t       len = 0;
648     char         buff[2048];
649 
650     CheckIsDead();
651 
652     if ( !WasSent() ) {
653         // we need to init the bcp
654         CheckSFB(blk_init(x_GetSybaseCmd(), CS_BLK_IN,
655                           (CS_CHAR*) GetQuery().data(), GetQuery().size()),
656                  "blk_init failed", 123001);
657 
658         SetWasSent();
659 
660         // check what needs to be default
661         CS_DATAFMT fmt;
662 
663         for (i = 0;  i < GetBindParamsImpl().NofParams();  i++) {
664             if (GetBindParamsImpl().GetParamStatus(i) != 0) {
665                 continue;
666             }
667 
668 
669             SetHasFailed((Check(blk_describe(x_GetSybaseCmd(),
670                                              i + 1,
671                                              &fmt)) != CS_SUCCEED));
672             CHECK_DRIVER_ERROR(
673                 HasFailed(),
674                 "blk_describe failed (check the number of "
675                 "columns in a table)." + GetDbgInfo(),
676                 123002 );
677         }
678     }
679 
680 
681     SetHasFailed(!x_AssignParams());
682     CHECK_DRIVER_ERROR( HasFailed(), "Cannot assign the params." + GetDbgInfo(), 123004 );
683 
684     switch ( Check(blk_rowxfer(x_GetSybaseCmd())) ) {
685     case CS_BLK_HAS_TEXT:
686         for (i = 0;  i < GetBindParamsImpl().NofParams();  i++) {
687             if (GetBindParamsImpl().GetParamStatus(i) == 0)
688                 continue;
689 
690             CDB_Object& param = *GetBindParamsImpl().GetParam(i);
691 
692             if (param.IsNULL()) {
693                 continue;
694             }
695             else if (CDB_Object::IsBlobType(param.GetType())) {
696                 CDB_Stream& par = dynamic_cast<CDB_Stream&> (param);
697                 datalen = (CS_INT) par.Size();
698 
699                 do {
700                     len = par.Read(buff, sizeof(buff));
701 
702                     SetHasFailed((Check(blk_textxfer(x_GetSybaseCmd(),
703                                                     (CS_BYTE*) buff,
704                                                     (CS_INT) len,
705                                                     0)
706                                         ) == CS_FAIL));
707 
708                     CHECK_DRIVER_ERROR(
709                         HasFailed(),
710                         "blk_textxfer failed for the BLOB field."
711                         + GetDbgInfo(),
712                         123005
713                         );
714 
715                     datalen -= (CS_INT) len;
716                 } while (datalen > 0);
717             }
718         }
719     case CS_SUCCEED:
720         ++m_RowCount;
721         return true;
722     default:
723         SetHasFailed();
724         CHECK_DRIVER_ERROR( HasFailed(), "blk_rowxfer failed." + GetDbgInfo(), 123007 );
725     }
726 
727     return false;
728 }
729 
730 
Cancel()731 bool CTL_BCPInCmd::Cancel()
732 {
733 #ifndef FTDS_IN_USE
734     DATABASE_DRIVER_ERROR("Cancelling is not available in ctlib.", 125000);
735 #endif
736 
737     if(WasSent()) {
738         if (IsDead()) {
739             SetWasSent(false);
740             return true;
741         }
742 
743         CS_INT outrow = 0;
744 
745         size_t was_timeout = GetConnection().PrepareToCancel();
746         try {
747             bool result = (CheckSentSFB(blk_done(x_GetSybaseCmd(), CS_BLK_CANCEL, &outrow),
748                                         "blk_done failed", 123020) == CS_SUCCEED);
749             GetConnection().CancelFinished(was_timeout);
750             return result;
751         }
752         catch (CDB_Exception&) {
753             GetConnection().CancelFinished(was_timeout);
754             throw;
755         }
756     }
757 
758     return true;
759 }
760 
CommitBCPTrans(void)761 bool CTL_BCPInCmd::CommitBCPTrans(void)
762 {
763     if(!WasSent()) return false;
764 
765     CheckIsDead();
766 
767     CS_INT outrow = 0;
768 
769     switch( Check(blk_done(x_GetSybaseCmd(), CS_BLK_BATCH, &outrow)) ) {
770     case CS_SUCCEED:
771         return (outrow > 0);
772     case CS_FAIL:
773         SetHasFailed();
774         DATABASE_DRIVER_ERROR( "blk_done failed." + GetDbgInfo(), 123020 );
775     default:
776         return false;
777     }
778 }
779 
780 
EndBCP(void)781 bool CTL_BCPInCmd::EndBCP(void)
782 {
783     if(!WasSent()) return false;
784 
785     CheckIsDead();
786 
787     CS_INT outrow = 0;
788 
789     if (CheckSentSFB(blk_done(x_GetSybaseCmd(), CS_BLK_ALL, &outrow),
790                      "blk_done failed", 123020) == CS_SUCCEED) {
791         return (outrow > 0);
792     }
793 
794     return false;
795 }
796 
797 
RowCount(void) const798 int CTL_BCPInCmd::RowCount(void) const
799 {
800     return m_RowCount;
801 }
802 
803 
~CTL_BCPInCmd()804 CTL_BCPInCmd::~CTL_BCPInCmd()
805 {
806     try {
807         DetachInterface();
808 
809         DropCmd(*this);
810 
811         Close();
812 
813         if (!IsDead()) {
814             Check(blk_drop(x_GetSybaseCmd()));
815         }
816     }
817     NCBI_CATCH_ALL_X( 1, NCBI_CURRENT_FUNCTION )
818 }
819 
820 
821 void
Close(void)822 CTL_BCPInCmd::Close(void)
823 {
824     if (x_GetSybaseCmd()) {
825         // ????
826         DetachInterface();
827 
828         try {
829 
830 #ifdef FTDS_IN_USE
831             SetDead(!Cancel());
832 #else
833             if (WasSent()) {
834                 SetDead(!EndBCP());
835             }
836 #endif
837 
838         } catch (...) {
839             SetDead();
840             throw;
841         }
842     }
843 }
844 
845 
SetHints(CTempString hints)846 void CTL_BCPInCmd::SetHints(CTempString hints)
847 {
848 #if defined(FTDS_IN_USE)  &&  defined(blk_sethints)
849     m_Hints.clear();
850     if (Check(blk_sethints(x_GetSybaseCmd(), (CS_CHAR*)hints.data(), CS_INT(hints.size()))) == CS_FAIL) {
851         DATABASE_DRIVER_ERROR("blk_sethints failed." + GetDbgInfo(), 123018);
852     }
853 #else
854     _ASSERT(false);
855 #endif
856 }
857 
858 
x_BlkSetHints(void)859 void CTL_BCPInCmd::x_BlkSetHints(void)
860 {
861 #if defined(FTDS_IN_USE)  &&  defined(blk_sethints)
862     string hints;
863     ITERATE(THintsMap, it, m_Hints) {
864         if (!hints.empty())
865             hints += ",";
866         hints += it->second;
867     }
868     if (Check(blk_sethints(x_GetSybaseCmd(), (CS_CHAR*)hints.data(), CS_INT(hints.size()))) == CS_FAIL) {
869         DATABASE_DRIVER_ERROR("blk_sethints failed." + GetDbgInfo(), 123019);
870     }
871 #endif
872 }
873 
874 
AddHint(CDB_BCPInCmd::EBCP_Hints hint,unsigned int value)875 void CTL_BCPInCmd::AddHint(CDB_BCPInCmd::EBCP_Hints hint, unsigned int value)
876 {
877 #ifdef FTDS_IN_USE
878     string str_hint;
879     bool need_value = false;
880     switch (hint) {
881     case CDB_BCPInCmd::eRowsPerBatch:
882         str_hint = "ROWS_PER_BATCH";
883         need_value = true;
884         break;
885     case CDB_BCPInCmd::eKilobytesPerBatch:
886         str_hint = "KILOBYTES_PER_BATCH";
887         need_value = true;
888         break;
889     case CDB_BCPInCmd::eTabLock:
890         str_hint = "TABLOCK";
891         break;
892     case CDB_BCPInCmd::eCheckConstraints:
893         str_hint = "CHECK_CONSTRAINTS";
894         break;
895     case CDB_BCPInCmd::eFireTriggers:
896         str_hint = "FIRE_TRIGGERS";
897         break;
898     default:
899         DATABASE_DRIVER_ERROR("Wrong hint type in AddHint." + GetDbgInfo(), 123015);
900     }
901     if (need_value) {
902         if (value == 0) {
903             DATABASE_DRIVER_ERROR("Value in AddHint should not be 0."
904                                   + GetDbgInfo(), 123016);
905         }
906         str_hint += "=";
907         str_hint += NStr::IntToString(value);
908     }
909     else if (value != 0) {
910         DATABASE_DRIVER_ERROR("Cannot set value for a given hint type ("
911                               + NStr::IntToString(hint) + ")."
912                               + GetDbgInfo(), 123016);
913     }
914     m_Hints[hint] = str_hint;
915 
916     x_BlkSetHints();
917 #else
918     _ASSERT(false);
919 #endif
920 }
921 
AddOrderHint(CTempString columns)922 void CTL_BCPInCmd::AddOrderHint(CTempString columns)
923 {
924 #ifdef FTDS_IN_USE
925     string str_hint = "ORDER (";
926     str_hint += columns;
927     str_hint += ")";
928     m_Hints[CDB_BCPInCmd::eOrder] = str_hint;
929 
930     x_BlkSetHints();
931 #else
932     _ASSERT(false);
933 #endif
934 }
935 
936 
937 #ifdef FTDS_IN_USE
938 } // namespace NCBI_NS_FTDS_CTLIB
939 #endif
940 
941 END_NCBI_SCOPE
942 
943