1 // Copyright (c) 1999-2018 David Muse
2 // See the file COPYING for more information
3
4 #include <sqlrelay/sqlrserver.h>
5 #include <rudiments/environment.h>
6 #include <rudiments/stringbuffer.h>
7 #include <rudiments/charstring.h>
8 #include <rudiments/character.h>
9 #include <rudiments/bytestring.h>
10 #include <rudiments/stdio.h>
11 #include <rudiments/snooze.h>
12
13 #include <datatypes.h>
14 #include <defines.h>
15 #include <config.h>
16
17 extern "C" {
18 #include <ctpublic.h>
19 }
20
21 #ifdef HAVE_FREETDS_H
22 #include <tdsver.h>
23 #endif
24
25 #ifndef HAVE_FREETDS_FUNCTION_DEFINITIONS
26 // in freetds prior to 0.61, the cs_ functions are not defined in any header
27 // file. C allows that... C++ does not, so here they are
28 extern "C" {
29
30 extern CS_INT cs_ctx_alloc(CS_INT,CS_CONTEXT **);
31 extern CS_INT ct_init(CS_CONTEXT *,CS_INT);
32 extern CS_INT ct_callback(CS_CONTEXT *,CS_CONNECTION *,
33 CS_INT,CS_INT,CS_VOID *);
34 extern CS_INT cs_config(CS_CONTEXT *,CS_INT,CS_INT,CS_VOID *,CS_INT,CS_INT *);
35 extern CS_INT ct_con_alloc(CS_CONTEXT *,CS_CONNECTION **);
36 extern CS_INT ct_con_props(CS_CONNECTION *,CS_INT,CS_INT,CS_VOID *,CS_INT,CS_INT *);
37 extern CS_INT cs_loc_alloc(CS_CONTEXT *,CS_LOCALE **);
38 extern CS_INT cs_locale(CS_CONTEXT *,CS_INT,CS_LOCALE *,CS_INT,CS_CHAR *,CS_INT,CS_INT *);
39 extern CS_INT ct_connect(CS_CONNECTION *,CS_CHAR *,CS_INT);
40 extern CS_INT ct_close(CS_CONNECTION *,CS_INT);
41 extern CS_INT ct_con_drop(CS_CONNECTION *);
42 extern CS_INT ct_exit(CS_CONTEXT *,CS_INT);
43 extern CS_INT cs_ctx_drop(CS_CONTEXT *);
44 extern CS_INT ct_cmd_alloc(CS_CONNECTION *,CS_COMMAND **);
45 extern CS_INT ct_command(CS_COMMAND *,CS_INT,CS_CHAR *,CS_INT,CS_INT);
46 extern CS_INT ct_send(CS_COMMAND *);
47 extern CS_INT ct_results(CS_COMMAND *,CS_INT *);
48 extern CS_INT ct_res_info(CS_COMMAND *,CS_INT,CS_VOID *,CS_INT,CS_INT *);
49 extern CS_INT ct_describe(CS_COMMAND *,CS_INT,CS_DATAFMT *);
50 extern CS_INT ct_bind(CS_COMMAND *,CS_INT,CS_DATAFMT *,CS_VOID *,CS_INT *,CS_SMALLINT *);
51 extern CS_INT ct_fetch(CS_COMMAND *,CS_INT,CS_INT,CS_INT,CS_INT *);
52 extern CS_INT ct_cmd_drop(CS_COMMAND *);
53 extern CS_INT cs_convert(CS_CONTEXT *,CS_DATAFMT *,CS_VOID *,CS_DATAFMT *,CS_VOID *,CS_INT *);
54 extern CS_INT cs_loc_drop(CS_CONTEXT *,CS_LOCALE *);
55 extern CS_INT ct_cancel(CS_CONNECTION *,CS_COMMAND *,CS_INT);
56 extern CS_INT ct_dynamic(CS_COMMAND *,CS_INT,CS_CHAR *,CS_INT,CS_CHAR *,CS_INT);
57
58 }
59 #endif
60
61 // this is here in case freetds ever supports cursors
62 //#define FREETDS_SUPPORTS_CURSORS
63
64 // some versions of freetds don't define this
65 #ifndef CS_UNSUPPORTED
66 #define CS_UNSUPPORTED -10
67 #endif
68
69 struct datebind {
70 int16_t *year;
71 int16_t *month;
72 int16_t *day;
73 int16_t *hour;
74 int16_t *minute;
75 int16_t *second;
76 int32_t *microsecond;
77 const char **tz;
78 bool *isnegative;
79 };
80
81 class freetdsconnection;
82
83 class SQLRSERVER_DLLSPEC freetdscursor : public sqlrservercursor {
84 friend class freetdsconnection;
85 private:
86 freetdscursor(sqlrserverconnection *conn,
87 uint16_t id);
88 ~freetdscursor();
89 void allocateResultSetBuffers(int32_t columncount);
90 void deallocateResultSetBuffers();
91 bool open();
92 bool close();
93 bool prepareQuery(const char *query,
94 uint32_t length);
95 bool supportsNativeBinds(const char *query,
96 uint32_t length);
97 void encodeBlob(stringbuffer *buffer,
98 const char *data,
99 uint32_t datasize);
100 #ifdef FREETDS_SUPPORTS_CURSORS
101 bool inputBind(const char *variable,
102 uint16_t variablesize,
103 const char *value,
104 uint32_t valuesize,
105 int16_t *isnull);
106 bool inputBind(const char *variable,
107 uint16_t variablesize,
108 int64_t *value);
109 bool inputBind(const char *variable,
110 uint16_t variablesize,
111 double *value,
112 uint32_t precision,
113 uint32_t scale);
114 bool inputBind(const char *variable,
115 uint16_t variablesize,
116 int64_t year,
117 int16_t month,
118 int16_t day,
119 int16_t hour,
120 int16_t minute,
121 int16_t second,
122 int32_t microsecond,
123 const char *tz,
124 bool isnegative,
125 char *buffer,
126 uint16_t buffersize,
127 int16_t *isnull);
128 bool outputBind(const char *variable,
129 uint16_t variablesize,
130 char *value,
131 uint32_t valuesize,
132 int16_t *isnull);
133 bool outputBind(const char *variable,
134 uint16_t variablesize,
135 int64_t *value,
136 int16_t *isnull);
137 bool outputBind(const char *variable,
138 uint16_t variablesize,
139 double *value,
140 uint32_t *precision,
141 uint32_t *scale,
142 int16_t *isnull);
143 bool outputBind(const char *variable,
144 uint16_t variablesize,
145 int16_t *year,
146 int16_t *month,
147 int16_t *day,
148 int16_t *hour,
149 int16_t *minute,
150 int16_t *second,
151 int32_t *microsecond,
152 const char **tz,
153 bool *isnegative,
154 char *buffer,
155 uint16_t buffersize,
156 int16_t *isnull);
157 #endif
158 bool executeQuery(const char *query,
159 uint32_t length);
160 bool knowsAffectedRows();
161 uint64_t affectedRows();
162 uint32_t colCount();
163 const char *getColumnName(uint32_t col);
164 uint16_t getColumnType(uint32_t col);
165 uint32_t getColumnLength(uint32_t col);
166 uint32_t getColumnPrecision(uint32_t col);
167 uint32_t getColumnScale(uint32_t col);
168 uint16_t getColumnIsNullable(uint32_t col);
169 uint16_t getColumnIsPartOfKey(uint32_t col);
170 uint16_t getColumnIsUnsigned(uint32_t col);
171 uint16_t getColumnIsBinary(uint32_t col);
172 uint16_t getColumnIsAutoIncrement(uint32_t col);
173 bool ignoreDateDdMmParameter(uint32_t col,
174 const char *data,
175 uint32_t size);
176 bool noRowsToReturn();
177 bool skipRow(bool *error);
178 bool fetchRow(bool *error);
179 void getField(uint32_t col,
180 const char **field,
181 uint64_t *fieldlength,
182 bool *blob,
183 bool *null);
184 void nextRow();
185 void closeResultSet();
186 void discardResults();
187 void discardCursor();
188
189 char *cursorname;
190 size_t cursornamelength;
191
192 void checkRePrepare();
193
194 uint32_t majorversion;
195 uint32_t minorversion;
196 uint32_t patchlevel;
197
198 CS_COMMAND *languagecmd;
199 CS_COMMAND *cursorcmd;
200 CS_COMMAND *cmd;
201 CS_INT results;
202 CS_INT resultstype;
203 CS_INT ncols;
204 bool knowsaffectedrows;
205 CS_INT affectedrows;
206
207 CS_INT rowsread;
208 CS_INT row;
209 CS_INT maxrow;
210 CS_INT totalrows;
211
212 CS_DATAFMT *parameter;
213 uint16_t paramindex;
214 CS_INT *outbindtype;
215 char **outbindstrings;
216 uint32_t *outbindstringlengths;
217 int64_t **outbindints;
218 double **outbinddoubles;
219 datebind *outbinddates;
220 uint16_t outbindindex;
221
222 int32_t columncount;
223 CS_DATAFMT templatecolumn;
224 CS_DATAFMT *column;
225 char **data;
226 CS_INT **datalength;
227 CS_SMALLINT **nullindicator;
228
229 char *query;
230 uint32_t length;
231 bool prepared;
232 bool clean;
233
234 freetdsconnection *freetdsconn;
235 };
236
237
238 class SQLRSERVER_DLLSPEC freetdsconnection : public sqlrserverconnection {
239 friend class freetdscursor;
240 public:
241 freetdsconnection(sqlrservercontroller *cont);
242 ~freetdsconnection();
243 private:
244 void handleConnectString();
245 bool logIn(const char **error, const char **warning);
246 const char *logInError(const char *error, uint16_t stage);
247 sqlrservercursor *newCursor(uint16_t id);
248 void deleteCursor(sqlrservercursor *curs);
249 void logOut();
250 const char *identify();
251 const char *dbVersion();
252 const char *dbHostNameQuery();
253 const char *getDatabaseListQuery(bool wild);
254 const char *getTableListQuery(bool wild,
255 uint16_t objecttypes);
256 const char *getColumnListQuery(
257 const char *table, bool wild);
258 const char *selectDatabaseQuery();
259 const char *getCurrentDatabaseQuery();
260 const char *getLastInsertIdQuery();
261 const char *getNoopQuery();
262 const char *bindFormat();
263 const char *beginTransactionQuery();
264 const char *tempTableDropPrefix();
265 bool commit();
266 bool rollback();
267 void errorMessage(char *errorbuffer,
268 uint32_t errorbufferlength,
269 uint32_t *errorlength,
270 int64_t *errorcode,
271 bool *liveconnection);
272
273 CS_CONTEXT *context;
274 CS_LOCALE *locale;
275 CS_CONNECTION *dbconn;
276
277 const char *sybase;
278 const char *freetds;
279 const char *lang;
280 const char *server;
281 const char *db;
282 const char *charset;
283 const char *language;
284 const char *hostname;
285 const char *packetsize;
286
287 const char *identity;
288
289 bool dbused;
290
291 char *dbversion;
292
293 bool sybasedb;
294
295 static stringbuffer errorstring;
296 static int64_t errorcode;
297 static bool liveconnection;
298
299 static CS_RETCODE csMessageCallback(CS_CONTEXT *ctxt,
300 CS_CLIENTMSG *msgp);
301 static CS_RETCODE clientMessageCallback(CS_CONTEXT *ctxt,
302 CS_CONNECTION *cnn,
303 CS_CLIENTMSG *msgp);
304 static CS_RETCODE serverMessageCallback(CS_CONTEXT *ctxt,
305 CS_CONNECTION *cnn,
306 CS_SERVERMSG *msgp);
307
308 stringbuffer loginerror;
309 };
310
311 stringbuffer freetdsconnection::errorstring;
312 int64_t freetdsconnection::errorcode;
313 bool freetdsconnection::liveconnection;
314
freetdsconnection(sqlrservercontroller * cont)315 freetdsconnection::freetdsconnection(sqlrservercontroller *cont) :
316 sqlrserverconnection(cont) {
317 dbused=false;
318 dbversion=NULL;
319 sybasedb=true;
320
321 identity=NULL;
322 }
323
~freetdsconnection()324 freetdsconnection::~freetdsconnection() {
325 delete[] dbversion;
326 }
327
handleConnectString()328 void freetdsconnection::handleConnectString() {
329
330 sqlrserverconnection::handleConnectString();
331
332 sybase=cont->getConnectStringValue("sybase");
333 freetds=cont->getConnectStringValue("freetds");
334 lang=cont->getConnectStringValue("lang");
335 server=cont->getConnectStringValue("server");
336 db=cont->getConnectStringValue("db");
337 charset=cont->getConnectStringValue("charset");
338 language=cont->getConnectStringValue("language");
339 hostname=cont->getConnectStringValue("hostname");
340 packetsize=cont->getConnectStringValue("packetsize");
341
342 // freetds doesn't currently support array fetches
343 cont->setFetchAtOnce(1);
344
345 if (cont->getMaxColumnCount()==1) {
346 // if max column count is set to 1 then force it
347 // to 2 so the db version detection doesn't crash
348 cont->setMaxColumnCount(2);
349 }
350
351 identity=cont->getConnectStringValue("identity");
352 }
353
logIn(const char ** error,const char ** warning)354 bool freetdsconnection::logIn(const char **error, const char **warning) {
355
356 // set sybase
357 if (!charstring::isNullOrEmpty(sybase) &&
358 !environment::setValue("SYBASE",sybase)) {
359 *error=logInError(
360 "Failed to set SYBASE environment variable.",1);
361 return false;
362 }
363
364 // set freetds
365 if (!charstring::isNullOrEmpty(freetds)) {
366 if (!environment::setValue("FREETDS",freetds)) {
367 *error=logInError(
368 "Failed to set FREETDS "
369 "environment variable.",1);
370 return false;
371 }
372 if (!environment::setValue("FREETDSCONF",freetds)) {
373 *error=logInError(
374 "Failed to set FREETDSCONF "
375 "environment variable.",1);
376 return false;
377 }
378 }
379
380 // set lang
381 if (!charstring::isNullOrEmpty(lang) &&
382 !environment::setValue("LANG",lang)) {
383 *error=logInError(
384 "Failed to set LANG environment variable.",1);
385 return false;
386 }
387
388 // set server
389 if (!charstring::isNullOrEmpty(server) &&
390 !environment::setValue("DSQUERY",server)) {
391 *error=logInError(
392 "Failed to set DSQUERY environment variable.",2);
393 return false;
394 }
395
396 // allocate a context
397 context=(CS_CONTEXT *)NULL;
398 if (cs_ctx_alloc(CS_VERSION_100,&context)!=CS_SUCCEED) {
399 *error=logInError(
400 "Failed to allocate a context structure",2);
401 return false;
402 }
403 // init the context
404 if (ct_init(context,CS_VERSION_100)!=CS_SUCCEED) {
405 *error=logInError(
406 "Failed to initialize a context structure",3);
407 return false;
408 }
409
410
411 // configure the error handling callbacks
412 if (cs_config(context,CS_SET,CS_MESSAGE_CB,
413 (CS_VOID *)freetdsconnection::csMessageCallback,CS_UNUSED,
414 (CS_INT *)NULL)
415 !=CS_SUCCEED) {
416 *error=logInError(
417 "Failed to set a cslib error message callback",4);
418 return false;
419 }
420 if (ct_callback(context,NULL,CS_SET,CS_CLIENTMSG_CB,
421 (CS_VOID *)freetdsconnection::clientMessageCallback)
422 !=CS_SUCCEED) {
423 *error=logInError(
424 "Failed to set a client error message callback",4);
425 return false;
426 }
427 if (ct_callback(context,NULL,CS_SET,CS_SERVERMSG_CB,
428 (CS_VOID *)freetdsconnection::serverMessageCallback)
429 !=CS_SUCCEED) {
430 *error=logInError(
431 "Failed to set a server error message callback",4);
432 return false;
433 }
434
435
436 // allocate a connection
437 if (ct_con_alloc(context,&dbconn)!=CS_SUCCEED) {
438 *error=logInError(
439 "Failed to allocate a connection structure",4);
440 return false;
441 }
442
443
444 // set the user to use
445 const char *user=cont->getUser();
446 if (ct_con_props(dbconn,CS_SET,CS_USERNAME,
447 (CS_VOID *)((!charstring::isNullOrEmpty(user))?user:""),
448 CS_NULLTERM,(CS_INT *)NULL)!=CS_SUCCEED) {
449 *error=logInError("Failed to set the user",5);
450 return false;
451 }
452
453
454 // set the password to use
455 const char *password=cont->getPassword();
456 if (ct_con_props(dbconn,CS_SET,CS_PASSWORD,
457 (CS_VOID *)((!charstring::isNullOrEmpty(password))?password:""),
458 CS_NULLTERM,(CS_INT *)NULL)!=CS_SUCCEED) {
459 *error=logInError("Failed to set the password",5);
460 return false;
461 }
462
463 // set application name
464 if (ct_con_props(dbconn,CS_SET,CS_APPNAME,
465 (CS_VOID *)"sqlrelay",
466 CS_NULLTERM,(CS_INT *)NULL)!=CS_SUCCEED) {
467 *error=logInError("Failed to set the application name",5);
468 return false;
469 }
470
471 // set hostname
472 if (!charstring::isNullOrEmpty(hostname) &&
473 ct_con_props(dbconn,CS_SET,CS_HOSTNAME,
474 (CS_VOID *)hostname,
475 CS_NULLTERM,(CS_INT *)NULL)!=CS_SUCCEED) {
476 *error=logInError("Failed to set the hostname",5);
477 return false;
478 }
479
480 // set packetsize
481 uint16_t ps=charstring::toInteger(packetsize);
482 if (!charstring::isNullOrEmpty(packetsize) &&
483 ct_con_props(dbconn,CS_SET,CS_PACKETSIZE,
484 (CS_VOID *)&ps,sizeof(ps),
485 (CS_INT *)NULL)!=CS_SUCCEED) {
486 *error=logInError("Failed to set the packetsize",5);
487 return false;
488 }
489
490 #ifdef CS_SEC_ENCRYPTION
491 CS_INT enc=CS_TRUE;
492 if (ct_con_props(dbconn,CS_SET,CS_SEC_ENCRYPTION,
493 (CS_VOID *)&enc,CS_UNUSED,
494 (CS_INT *)NULL)!=CS_SUCCEED) {
495 *error=logInError("Failed to enable password encryption",5);
496 return false;
497 }
498 #endif
499
500 // init locale
501 locale=NULL;
502 if (cs_loc_alloc(context,&locale)!=CS_SUCCEED) {
503 *error=logInError("Failed to allocate a locale structure",5);
504 return false;
505 }
506 if (cs_locale(context,CS_SET,locale,CS_LC_ALL,(CS_CHAR *)NULL,
507 CS_UNUSED,(CS_INT *)NULL)!=CS_SUCCEED) {
508 *error=logInError("Failed to initialize a locale structure",6);
509 return false;
510 }
511
512 // set language
513 if (!charstring::isNullOrEmpty(language) &&
514 cs_locale(context,CS_SET,locale,CS_SYB_LANG,
515 (CS_CHAR *)language,CS_NULLTERM,(CS_INT *)NULL)!=
516 CS_SUCCEED) {
517 *error=logInError("Failed to set the language",6);
518 return false;
519 }
520
521 // set charset
522 if (!charstring::isNullOrEmpty(charset) &&
523 cs_locale(context,CS_SET,locale,CS_SYB_CHARSET,
524 (CS_CHAR *)charset,CS_NULLTERM,(CS_INT *)NULL)!=
525 CS_SUCCEED) {
526 *error=logInError("Failed to set the charset",6);
527 return false;
528 }
529
530 // set locale
531 if (ct_con_props(dbconn,CS_SET,CS_LOC_PROP,(CS_VOID *)locale,
532 CS_UNUSED,(CS_INT *)NULL)!=CS_SUCCEED) {
533 *error=logInError("Failed to set the locale",6);
534 return false;
535 }
536
537 // connect to the database
538 if (ct_connect(dbconn,(CS_CHAR *)NULL,(CS_INT)0)!=CS_SUCCEED) {
539 *error=logInError("Failed to connect to the database",6);
540 return false;
541 }
542
543 // If the password has expired then the db may allow the login
544 // but every query will fail. "ping" the db here to see if we get
545 // that error or not.
546 bool retval=true;
547 CS_COMMAND *cmd;
548 if (ct_cmd_alloc(dbconn,&cmd)!=CS_SUCCEED) {
549 *error=logInError("Failed to allocate ping command",6);
550 return false;
551 }
552 if (ct_command(cmd,CS_LANG_CMD,(CS_CHAR *)"select 1",8,
553 CS_UNUSED)!=CS_SUCCEED) {
554 *error=logInError("Failed to create ping command",6);
555 return false;
556 }
557 if (ct_send(cmd)!=CS_SUCCEED) {
558 *error=logInError("Failed to send ping command",6);
559 return false;
560 }
561 CS_INT resultstype;
562 if (ct_results(cmd,&resultstype)==CS_FAIL || resultstype==CS_CMD_FAIL) {
563 *error=logInError(NULL,6);
564 retval=false;
565 }
566 ct_cancel(NULL,cmd,CS_CANCEL_ALL);
567 ct_cmd_drop(cmd);
568
569 return retval;
570 }
571
logInError(const char * error,uint16_t stage)572 const char *freetdsconnection::logInError(const char *error, uint16_t stage) {
573
574 loginerror.clear();
575 if (error) {
576 loginerror.append(error)->append(": ");
577 }
578 if (errorstring.getStringLength()) {
579 loginerror.append(errorstring.getString());
580 }
581
582 if (stage>5) {
583 cs_loc_drop(context,locale);
584 }
585 if (stage>4) {
586 ct_con_drop(dbconn);
587 }
588 if (stage>3) {
589 ct_exit(context,CS_UNUSED);
590 }
591 if (stage>2) {
592 cs_ctx_drop(context);
593 }
594
595 return loginerror.getString();
596 }
597
newCursor(uint16_t id)598 sqlrservercursor *freetdsconnection::newCursor(uint16_t id) {
599 return (sqlrservercursor *)new freetdscursor(
600 (sqlrserverconnection *)this,id);
601 }
602
deleteCursor(sqlrservercursor * curs)603 void freetdsconnection::deleteCursor(sqlrservercursor *curs) {
604 delete (freetdscursor *)curs;
605 }
606
logOut()607 void freetdsconnection::logOut() {
608
609 cs_loc_drop(context,locale);
610 ct_close(dbconn,CS_UNUSED);
611 ct_con_drop(dbconn);
612 ct_exit(context,CS_UNUSED);
613 cs_ctx_drop(context);
614 }
615
identify()616 const char *freetdsconnection::identify() {
617 return (identity)?identity:"freetds";
618 }
619
dbVersion()620 const char *freetdsconnection::dbVersion() {
621 return dbversion;
622 }
623
dbHostNameQuery()624 const char *freetdsconnection::dbHostNameQuery() {
625 return "select asehostname()";
626 }
627
getDatabaseListQuery(bool wild)628 const char *freetdsconnection::getDatabaseListQuery(bool wild) {
629 if (sybasedb) {
630 return "select '' as db, NULL";
631 } else {
632 return "select "
633 " distinct catalog_name, "
634 " NULL "
635 "from "
636 " information_schema.schemata "
637 "order by "
638 " catalog_name";
639 }
640 }
641
getTableListQuery(bool wild,uint16_t objecttypes)642 const char *freetdsconnection::getTableListQuery(bool wild,
643 uint16_t objecttypes) {
644 if (sybasedb) {
645 return (wild)?
646 "select "
647 " NULL as table_cat, "
648 " NULL as table_schem, "
649 " name as table_name, "
650 " 'TABLE' as table_type, "
651 " NULL as remarks, "
652 " NULL as extra "
653 "from "
654 " sysobjects "
655 "where "
656 " loginame is not NULL "
657 " and "
658 " type in ('U','V') "
659 " and "
660 " name like '%s' "
661 "order by "
662 " name":
663
664 "select "
665 " NULL, "
666 " NULL, "
667 " name, "
668 " 'TABLE', "
669 " NULL "
670 "from "
671 " sysobjects "
672 "where "
673 " loginame is not NULL "
674 " and "
675 " type in ('U','V') "
676 "order by "
677 " name";
678 } else {
679 return sqlrserverconnection::getTableListQuery(
680 wild,objecttypes);
681 }
682 }
683
getColumnListQuery(const char * table,bool wild)684 const char *freetdsconnection::getColumnListQuery(
685 const char *table, bool wild) {
686 if (sybasedb) {
687 return (wild)?
688 "select "
689 " syscolumns.name, "
690 " systypes.name as type, "
691 " syscolumns.length, "
692 " syscolumns.prec, "
693 " syscolumns.scale, "
694 " (syscolumns.status&8)/8 as nullable, "
695 " '' as primarykey, "
696 " '' column_default, "
697 " '' as extra, "
698 " NULL "
699 "from "
700 " sysobjects, "
701 " syscolumns, "
702 " systypes "
703 "where "
704 " sysobjects.type in ('S','U','V') "
705 " and "
706 " sysobjects.name='%s' "
707 " and "
708 " syscolumns.id=sysobjects.id "
709 " and "
710 " syscolumns.name like '%s' "
711 " and "
712 " systypes.usertype=syscolumns.usertype "
713 "order by "
714 " syscolumns.colid":
715
716 "select "
717 " syscolumns.name, "
718 " systypes.name as type, "
719 " syscolumns.length, "
720 " syscolumns.prec, "
721 " syscolumns.scale, "
722 " (syscolumns.status&8)/8 as nullable, "
723 " '' as primarykey, "
724 " '' column_default, "
725 " '' as extra, "
726 " NULL "
727 "from "
728 " sysobjects, "
729 " syscolumns, "
730 " systypes "
731 "where "
732 " sysobjects.type in ('S','U','V') "
733 " and "
734 " sysobjects.name='%s' "
735 " and "
736 " syscolumns.id=sysobjects.id "
737 " and "
738 " systypes.usertype=syscolumns.usertype "
739 "order by "
740 " syscolumns.colid";
741 } else {
742 if (table && table[0]=='#') {
743 if (wild) {
744 return
745 "select "
746 " column_name, "
747 " data_type, "
748 " character_maximum_length, "
749 " numeric_precision, "
750 " numeric_scale, "
751 " is_nullable, "
752 " '' as primarykey, "
753 " column_default, "
754 " '' as extra, "
755 " NULL "
756 "from "
757 " tempdb.information_schema.columns "
758 "where "
759 " table_name like '%s____%%' "
760 " and "
761 " column_name like '%s' "
762 "order by "
763 " ordinal_position";
764 } else {
765 return
766 "select "
767 " column_name, "
768 " data_type, "
769 " character_maximum_length, "
770 " numeric_precision, "
771 " numeric_scale, "
772 " is_nullable, "
773 " '' as primarykey, "
774 " column_default, "
775 " '' as extra, "
776 " NULL "
777 "from "
778 " tempdb.information_schema.columns "
779 "where "
780 " table_name like '%s____%%' "
781 "order by "
782 " ordinal_position";
783 }
784 } else {
785 if (wild) {
786 return
787 "select "
788 " column_name, "
789 " data_type, "
790 " character_maximum_length, "
791 " numeric_precision, "
792 " numeric_scale, "
793 " is_nullable, "
794 " '' as primarykey, "
795 " column_default, "
796 " '' as extra, "
797 " NULL "
798 "from "
799 " information_schema.columns "
800 "where "
801 " table_name='%s' "
802 " and "
803 " column_name like '%s' "
804 "order by "
805 " ordinal_position";
806 } else {
807 return
808 "select "
809 " column_name, "
810 " data_type, "
811 " character_maximum_length, "
812 " numeric_precision, "
813 " numeric_scale, "
814 " is_nullable, "
815 " '' as primarykey, "
816 " column_default, "
817 " '' as extra, "
818 " NULL "
819 "from "
820 " information_schema.columns "
821 "where "
822 " table_name='%s' "
823 "order by "
824 " ordinal_position";
825 }
826 }
827 }
828 }
829
selectDatabaseQuery()830 const char *freetdsconnection::selectDatabaseQuery() {
831 return "use %s";
832 }
833
getCurrentDatabaseQuery()834 const char *freetdsconnection::getCurrentDatabaseQuery() {
835 return "select db_name()";
836 }
837
getLastInsertIdQuery()838 const char *freetdsconnection::getLastInsertIdQuery() {
839 return "select @@identity";
840 }
841
getNoopQuery()842 const char *freetdsconnection::getNoopQuery() {
843 return "waitfor delay '0:0'";
844 }
845
bindFormat()846 const char *freetdsconnection::bindFormat() {
847 return "@*";
848 }
849
beginTransactionQuery()850 const char *freetdsconnection::beginTransactionQuery() {
851 return "BEGIN TRANSACTION";
852 }
853
freetdscursor(sqlrserverconnection * conn,uint16_t id)854 freetdscursor::freetdscursor(sqlrserverconnection *conn, uint16_t id) :
855 sqlrservercursor(conn,id) {
856
857 #if defined(VERSION_NO)
858 char *versionstring=charstring::duplicate(VERSION_NO);
859 #elif defined(TDS_VERSION_NO)
860 char *versionstring=charstring::duplicate(TDS_VERSION_NO);
861 #else
862 char *versionstring=new char[1024];
863 CS_INT outlen;
864 if (ct_config(NULL,CS_GET,CS_VER_STRING,
865 (void *)versionstring,1023,&outlen)==CS_SUCCEED) {
866 versionstring[outlen]='\0';
867 } else {
868 versionstring=charstring::copy(versionstring,"freetds v0.00.0");
869 }
870 #endif
871
872 char *v=charstring::findFirst(versionstring,'v');
873 if (v) {
874 *v='\0';
875 majorversion=charstring::toInteger(v+1);
876 char *firstdot=charstring::findFirst(v+1,'.');
877 if (firstdot) {
878 *firstdot='\0';
879 minorversion=charstring::toInteger(firstdot+1);
880 char *seconddot=
881 charstring::findFirst(firstdot+1,'.');
882 if (seconddot) {
883 *seconddot='\0';
884 patchlevel=charstring::toInteger(seconddot+1);
885 } else {
886 patchlevel=0;
887 }
888 } else {
889 minorversion=0;
890 patchlevel=0;
891 }
892 } else {
893 majorversion=0;
894 minorversion=0;
895 patchlevel=0;
896 }
897 delete[] versionstring;
898
899 // Affected row count is generally supported in versions >= 0.53 but
900 // appears to be broken in 0.61.
901 knowsaffectedrows=(majorversion>0 ||
902 (minorversion>=53 && minorversion!=61));
903
904 prepared=false;
905 freetdsconn=(freetdsconnection *)conn;
906 cmd=NULL;
907 languagecmd=NULL;
908 cursorcmd=NULL;
909
910 cursornamelength=charstring::integerLength(id);
911 cursorname=charstring::parseNumber(id);
912
913 uint16_t maxbindcount=conn->cont->getConfig()->getMaxBindCount();
914 parameter=new CS_DATAFMT[maxbindcount];
915 outbindtype=new CS_INT[maxbindcount];
916 outbindstrings=new char *[maxbindcount];
917 outbindstringlengths=new uint32_t[maxbindcount];
918 outbindints=new int64_t *[maxbindcount];
919 outbinddoubles=new double *[maxbindcount];
920 outbinddates=new datebind[maxbindcount];
921
922 // replace the regular expressions used to detect creation of a
923 // temporary table
924 setCreateTempTablePattern("^(create|CREATE)[ \r\n]"
925 "+(table|TABLE)[ \r\n]+#");
926
927 allocateResultSetBuffers(conn->cont->getMaxColumnCount());
928
929 // define a template column-bind definition...
930 // get the field as a null terminated character string
931 // no longer than MAX_ITEM_BUFFER_SIZE, override some
932 templatecolumn.datatype=CS_CHAR_TYPE;
933 templatecolumn.format=CS_FMT_NULLTERM;
934 templatecolumn.maxlength=conn->cont->getMaxFieldLength();
935 templatecolumn.scale=CS_UNUSED;
936 templatecolumn.precision=CS_UNUSED;
937 templatecolumn.status=CS_UNUSED;
938 templatecolumn.count=conn->cont->getFetchAtOnce();
939 templatecolumn.usertype=CS_UNUSED;
940 templatecolumn.locale=NULL;
941 }
942
~freetdscursor()943 freetdscursor::~freetdscursor() {
944 close();
945 delete[] cursorname;
946 delete[] parameter;
947 delete[] outbindtype;
948 delete[] outbindstrings;
949 delete[] outbindstringlengths;
950 delete[] outbindints;
951 delete[] outbinddoubles;
952 delete[] outbinddates;
953
954 deallocateResultSetBuffers();
955 }
956
allocateResultSetBuffers(int32_t columncount)957 void freetdscursor::allocateResultSetBuffers(int32_t columncount) {
958
959 if (!columncount) {
960 this->columncount=0;
961 column=NULL;
962 data=NULL;
963 datalength=NULL;
964 nullindicator=NULL;
965 } else {
966 this->columncount=columncount;
967 column=new CS_DATAFMT[columncount];
968 data=new char *[columncount];
969 datalength=new CS_INT *[columncount];
970 nullindicator=new CS_SMALLINT *[columncount];
971 uint32_t fetchatonce=conn->cont->getFetchAtOnce();
972 uint32_t maxfieldlength=conn->cont->getMaxFieldLength();
973 for (int32_t i=0; i<columncount; i++) {
974 data[i]=new char[fetchatonce*maxfieldlength];
975 datalength[i]=new CS_INT[fetchatonce];
976 nullindicator[i]=new CS_SMALLINT[fetchatonce];
977 }
978 }
979 }
980
deallocateResultSetBuffers()981 void freetdscursor::deallocateResultSetBuffers() {
982 if (columncount) {
983 delete[] column;
984 for (int32_t i=0; i<columncount; i++) {
985 delete[] data[i];
986 delete[] datalength[i];
987 delete[] nullindicator[i];
988 }
989 delete[] data;
990 delete[] datalength;
991 delete[] nullindicator;
992 columncount=0;
993 }
994 }
995
open()996 bool freetdscursor::open() {
997
998 clean=true;
999
1000 if (ct_cmd_alloc(freetdsconn->dbconn,&languagecmd)!=CS_SUCCEED) {
1001 return false;
1002 }
1003 if (ct_cmd_alloc(freetdsconn->dbconn,&cursorcmd)!=CS_SUCCEED) {
1004 return false;
1005 }
1006 cmd=NULL;
1007
1008 // switch to the correct database
1009 // (only do this once per connection)
1010 bool retval=true;
1011 if (!charstring::isNullOrEmpty(freetdsconn->db) &&
1012 !freetdsconn->dbused) {
1013 uint32_t len=charstring::length(freetdsconn->db)+4;
1014 char *query=new char[len+1];
1015 charstring::printf(query,len+1,"use %s",freetdsconn->db);
1016 if (!(prepareQuery(query,len) && executeQuery(query,len))) {
1017 char err[2048];
1018 uint32_t errlen;
1019 int64_t errn;
1020 bool live;
1021 errorMessage(err,sizeof(err),&errlen,&errn,&live);
1022 stderror.printf("%s\n",err);
1023 retval=false;
1024 } else {
1025 freetdsconn->dbused=true;
1026 }
1027 closeResultSet();
1028 delete[] query;
1029 }
1030
1031 if (!freetdsconn->dbversion) {
1032
1033 // try the various queries that might return the version
1034 const char *query[]={
1035 "select @@version",
1036 "sp_version installmaster",
1037 NULL
1038 };
1039 CS_INT index[]={
1040 0,1,0
1041 };
1042
1043 for (uint32_t i=0; query[i] && !freetdsconn->dbversion; i++) {
1044
1045 const char *q=query[i];
1046 int32_t len=charstring::length(q);
1047 bool error=false;
1048
1049 if (prepareQuery(q,len) &&
1050 executeQuery(q,len) &&
1051 fetchRow(&error)) {
1052 freetdsconn->dbversion=
1053 charstring::duplicate(data[index[i]]);
1054 }
1055
1056 closeResultSet();
1057 }
1058
1059 // fall back to unknown
1060 if (!freetdsconn->dbversion) {
1061 freetdsconn->dbversion=
1062 charstring::duplicate("unknown");
1063 }
1064
1065 // set sybasedb too, it's sybase if it's not microsoft
1066 freetdsconn->sybasedb=!charstring::contains(
1067 freetdsconn->dbversion,"Microsoft");
1068 }
1069 return retval;
1070 }
1071
close()1072 bool freetdscursor::close() {
1073
1074 bool retval=true;
1075 if (languagecmd) {
1076 retval=(ct_cmd_drop(languagecmd)==CS_SUCCEED);
1077 languagecmd=NULL;
1078 }
1079 if (cursorcmd) {
1080 retval=(retval && (ct_cmd_drop(cursorcmd)==CS_SUCCEED));
1081 cursorcmd=NULL;
1082 }
1083 cmd=NULL;
1084 return retval;
1085 }
1086
prepareQuery(const char * query,uint32_t length)1087 bool freetdscursor::prepareQuery(const char *query, uint32_t length) {
1088
1089 // if the client aborts while a query is in the middle of running,
1090 // commit or rollback will be called, potentially before closeResultSet
1091 // is called and, since we're really only using 1 cursor, it will fail
1092 // unless closeResultSet gets called, so just to make sure, we'll call
1093 // it here
1094 closeResultSet();
1095
1096 // initialize column count
1097 ncols=0;
1098
1099 clean=true;
1100
1101 this->query=(char *)query;
1102 this->length=length;
1103
1104 paramindex=0;
1105 outbindindex=0;
1106
1107 if ((!charstring::compare(query,"select",6) ||
1108 !charstring::compare(query,"SELECT",6)) &&
1109 character::isWhitespace(query[6])) {
1110
1111 // initiate a cursor command
1112 cmd=cursorcmd;
1113 #ifdef FREETDS_SUPPORTS_CURSORS
1114 if (ct_cursor(cursorcmd,
1115 CS_CURSOR_DECLARE,
1116 (CS_CHAR *)cursorname,
1117 (CS_INT)cursornamelength,
1118 (CS_CHAR *)query,
1119 length,
1120 //CS_READ_ONLY)!=CS_SUCCEED) {
1121 CS_UNUSED)!=CS_SUCCEED) {
1122 return false;
1123 }
1124 #endif
1125
1126 } else if ((!charstring::compare(query,"exec",4) ||
1127 !charstring::compare(query,"EXEC",4)) &&
1128 character::isWhitespace(query[4])) {
1129
1130 // initiate an rpc command
1131 cmd=languagecmd;
1132 #ifdef FREETDS_SUPPORTS_CURSORS
1133 if (ct_command(languagecmd,
1134 CS_RPC_CMD,
1135 (CS_CHAR *)query+5,
1136 length-5,
1137 CS_UNUSED)!=CS_SUCCEED) {
1138 return false;
1139 }
1140 #endif
1141
1142 } else if ((!charstring::compare(query,"execute",7) ||
1143 !charstring::compare(query,"EXECUTE",7)) &&
1144 character::isWhitespace(query[7])) {
1145
1146 // initiate an rpc command
1147 cmd=languagecmd;
1148 #ifdef FREETDS_SUPPORTS_CURSORS
1149 if (ct_command(languagecmd,
1150 CS_RPC_CMD,
1151 (CS_CHAR *)query+8,
1152 length-8,
1153 CS_UNUSED)!=CS_SUCCEED) {
1154 return false;
1155 }
1156 #endif
1157
1158 } else {
1159
1160 // initiate a language command
1161 cmd=languagecmd;
1162 #ifdef FREETDS_SUPPORTS_CURSORS
1163 if (ct_command(languagecmd,
1164 CS_LANG_CMD,
1165 (CS_CHAR *)query,
1166 length,
1167 CS_UNUSED)!=CS_SUCCEED) {
1168 return false;
1169 }
1170 #endif
1171 }
1172
1173 clean=false;
1174 prepared=true;
1175 return true;
1176 }
1177
supportsNativeBinds(const char * query,uint32_t length)1178 bool freetdscursor::supportsNativeBinds(const char *query, uint32_t length) {
1179 #ifdef FREETDS_SUPPORTS_CURSORS
1180 return true;
1181 #else
1182 return false;
1183 #endif
1184 }
1185
encodeBlob(stringbuffer * buffer,const char * data,uint32_t datasize)1186 void freetdscursor::encodeBlob(stringbuffer *buffer,
1187 const char *data, uint32_t datasize) {
1188
1189 // sybase/mssqlserver wants each byte of blob data to be converted to
1190 // two hex characters and the whole thing to start with 0x
1191 // eg: hello - > 0x68656C6C6F
1192
1193 buffer->append("0x");
1194 for (uint32_t i=0; i<datasize; i++) {
1195 buffer->append(conn->cont->asciiToHex(data[i]));
1196 }
1197 }
1198
checkRePrepare()1199 void freetdscursor::checkRePrepare() {
1200
1201 // Sybase doesn't allow you to rebind and re-execute when using
1202 // ct_command. You have to re-prepare too. I'll make this transparent
1203 // to the user.
1204 if (!prepared) {
1205 prepareQuery(query,length);
1206 }
1207 }
1208
1209 #ifdef FREETDS_SUPPORTS_CURSORS
inputBind(const char * variable,uint16_t variablesize,const char * value,uint32_t valuesize,int16_t * isnull)1210 bool freetdscursor::inputBind(const char *variable,
1211 uint16_t variablesize,
1212 const char *value,
1213 uint32_t valuesize,
1214 int16_t *isnull) {
1215
1216 checkRePrepare();
1217
1218 (CS_VOID)bytestring::zero(¶meter[paramindex],
1219 sizeof(parameter[paramindex]));
1220 if (charstring::isInteger(variable+1,variablesize-1)) {
1221 parameter[paramindex].name[0]='\0';
1222 parameter[paramindex].namelen=0;
1223 } else {
1224 charstring::copy(parameter[paramindex].name,variable);
1225 parameter[paramindex].namelen=variablesize;
1226 }
1227 parameter[paramindex].datatype=CS_CHAR_TYPE;
1228 parameter[paramindex].maxlength=CS_UNUSED;
1229 parameter[paramindex].status=CS_INPUTVALUE;
1230 parameter[paramindex].locale=NULL;
1231 if (ct_param(cmd,¶meter[paramindex],
1232 (CS_VOID *)value,valuesize,0)!=CS_SUCCEED) {
1233 return false;
1234 }
1235 paramindex++;
1236 return true;
1237 }
1238
inputBind(const char * variable,uint16_t variablesize,int64_t * value)1239 bool freetdscursor::inputBind(const char *variable,
1240 uint16_t variablesize,
1241 int64_t *value) {
1242
1243 checkRePrepare();
1244
1245 (CS_VOID)bytestring::zero(¶meter[paramindex],
1246 sizeof(parameter[paramindex]));
1247 if (charstring::isInteger(variable+1,variablesize-1)) {
1248 parameter[paramindex].name[0]='\0';
1249 parameter[paramindex].namelen=0;
1250 } else {
1251 charstring::copy(parameter[paramindex].name,variable);
1252 parameter[paramindex].namelen=variablesize;
1253 }
1254 parameter[paramindex].datatype=CS_INT_TYPE;
1255 parameter[paramindex].maxlength=CS_UNUSED;
1256 parameter[paramindex].status=CS_INPUTVALUE;
1257 parameter[paramindex].locale=NULL;
1258 if (ct_param(cmd,¶meter[paramindex],
1259 (CS_VOID *)value,sizeof(int64_t),0)!=CS_SUCCEED) {
1260 return false;
1261 }
1262 paramindex++;
1263 return true;
1264 }
1265
inputBind(const char * variable,uint16_t variablesize,double * value,uint32_t precision,uint32_t scale)1266 bool freetdscursor::inputBind(const char *variable,
1267 uint16_t variablesize,
1268 double *value,
1269 uint32_t precision,
1270 uint32_t scale) {
1271
1272 checkRePrepare();
1273
1274 (CS_VOID)bytestring::zero(¶meter[paramindex],
1275 sizeof(parameter[paramindex]));
1276 if (charstring::isInteger(variable+1,variablesize-1)) {
1277 parameter[paramindex].name[0]='\0';
1278 parameter[paramindex].namelen=0;
1279 } else {
1280 charstring::copy(parameter[paramindex].name,variable);
1281 parameter[paramindex].namelen=variablesize;
1282 }
1283 parameter[paramindex].datatype=CS_FLOAT_TYPE;
1284 parameter[paramindex].maxlength=CS_UNUSED;
1285 parameter[paramindex].status=CS_INPUTVALUE;
1286 parameter[paramindex].precision=precision;
1287 parameter[paramindex].scale=scale;
1288 parameter[paramindex].locale=NULL;
1289 if (ct_param(cmd,¶meter[paramindex],
1290 (CS_VOID *)value,sizeof(double),0)!=CS_SUCCEED) {
1291 return false;
1292 }
1293 paramindex++;
1294 return true;
1295 }
1296
1297 static const char *monthname[]={
1298 "Jan","Feb","Mar","Apr","May","Jun",
1299 "Jul","Aug","Sep","Oct","Nov","Dec",
1300 NULL
1301 };
1302
inputBind(const char * variable,uint16_t variablesize,int64_t year,int16_t month,int16_t day,int16_t hour,int16_t minute,int16_t second,int32_t microsecond,const char * tz,bool isnegative,char * buffer,uint16_t buffersize,int16_t * isnull)1303 bool freetdscursor::inputBind(const char *variable,
1304 uint16_t variablesize,
1305 int64_t year,
1306 int16_t month,
1307 int16_t day,
1308 int16_t hour,
1309 int16_t minute,
1310 int16_t second,
1311 int32_t microsecond,
1312 const char *tz,
1313 bool isnegative,
1314 char *buffer,
1315 uint16_t buffersize,
1316 int16_t *isnull) {
1317
1318 checkRePrepare();
1319
1320 // Sybase requires this format: "Jan 2 2012 4:5:3:000PM"
1321 if (month<1) {
1322 month=1;
1323 }
1324 if (month>12) {
1325 month=12;
1326 }
1327 const char *ampm="AM";
1328 if (hour==0) {
1329 hour=12;
1330 } else if (hour==12) {
1331 ampm="PM";
1332 } else if (hour>12) {
1333 hour=hour-12;
1334 ampm="PM";
1335 }
1336 charstring::copy(buffer,monthname[month-1]);
1337 charstring::append(buffer," ");
1338 charstring::append(buffer,(int64_t)day);
1339 charstring::append(buffer," ");
1340 charstring::append(buffer,(int64_t)year);
1341 charstring::append(buffer," ");
1342 charstring::append(buffer,(int64_t)hour);
1343 charstring::append(buffer,":");
1344 charstring::append(buffer,(int64_t)minute);
1345 charstring::append(buffer,":");
1346 charstring::append(buffer,(int64_t)second);
1347 charstring::append(buffer,":");
1348 charstring::append(buffer,(int64_t)microsecond);
1349 charstring::append(buffer,ampm);
1350 return inputBind(variable,variablesize,
1351 buffer,charstring::length(buffer),isnull);
1352 }
1353
outputBind(const char * variable,uint16_t variablesize,char * value,uint32_t valuesize,int16_t * isnull)1354 bool freetdscursor::outputBind(const char *variable,
1355 uint16_t variablesize,
1356 char *value,
1357 uint32_t valuesize,
1358 int16_t *isnull) {
1359
1360 checkRePrepare();
1361
1362 outbindtype[outbindindex]=CS_CHAR_TYPE;
1363 outbindstrings[outbindindex]=value;
1364 outbindstringlengths[outbindindex]=valuesize;
1365 outbindindex++;
1366
1367 (CS_VOID)bytestring::zero(¶meter[paramindex],
1368 sizeof(parameter[paramindex]));
1369 if (charstring::isInteger(variable+1,variablesize-1)) {
1370 parameter[paramindex].name[0]='\0';
1371 parameter[paramindex].namelen=0;
1372 } else {
1373 charstring::copy(parameter[paramindex].name,variable);
1374 parameter[paramindex].namelen=variablesize;
1375 }
1376 parameter[paramindex].datatype=CS_CHAR_TYPE;
1377 parameter[paramindex].maxlength=valuesize;
1378 parameter[paramindex].status=CS_RETURN;
1379 parameter[paramindex].locale=NULL;
1380 if (ct_param(cmd,¶meter[paramindex],
1381 (CS_VOID *)NULL,0,
1382 (CS_SMALLINT)*isnull)!=CS_SUCCEED) {
1383 return false;
1384 }
1385 paramindex++;
1386 return true;
1387 }
1388
outputBind(const char * variable,uint16_t variablesize,int64_t * value,int16_t * isnull)1389 bool freetdscursor::outputBind(const char *variable,
1390 uint16_t variablesize,
1391 int64_t *value,
1392 int16_t *isnull) {
1393
1394 checkRePrepare();
1395
1396 outbindtype[outbindindex]=CS_INT_TYPE;
1397 outbindints[outbindindex]=value;
1398 outbindindex++;
1399
1400 (CS_VOID)bytestring::zero(¶meter[paramindex],
1401 sizeof(parameter[paramindex]));
1402 if (charstring::isInteger(variable+1,variablesize-1)) {
1403 parameter[paramindex].name[0]='\0';
1404 parameter[paramindex].namelen=0;
1405 } else {
1406 charstring::copy(parameter[paramindex].name,variable);
1407 parameter[paramindex].namelen=variablesize;
1408 }
1409 parameter[paramindex].datatype=CS_INT_TYPE;
1410 parameter[paramindex].maxlength=CS_UNUSED;
1411 parameter[paramindex].status=CS_RETURN;
1412 parameter[paramindex].locale=NULL;
1413 if (ct_param(cmd,¶meter[paramindex],
1414 (CS_VOID *)NULL,0,
1415 (CS_SMALLINT)*isnull)!=CS_SUCCEED) {
1416 return false;
1417 }
1418 paramindex++;
1419 return true;
1420 }
1421
outputBind(const char * variable,uint16_t variablesize,double * value,uint32_t * precision,uint32_t * scale,int16_t * isnull)1422 bool freetdscursor::outputBind(const char *variable,
1423 uint16_t variablesize,
1424 double *value,
1425 uint32_t *precision,
1426 uint32_t *scale,
1427 int16_t *isnull) {
1428
1429 checkRePrepare();
1430
1431 outbindtype[outbindindex]=CS_FLOAT_TYPE;
1432 outbinddoubles[outbindindex]=value;
1433 outbindindex++;
1434
1435 (CS_VOID)bytestring::zero(¶meter[paramindex],
1436 sizeof(parameter[paramindex]));
1437 if (charstring::isInteger(variable+1,variablesize-1)) {
1438 parameter[paramindex].name[0]='\0';
1439 parameter[paramindex].namelen=0;
1440 } else {
1441 charstring::copy(parameter[paramindex].name,variable);
1442 parameter[paramindex].namelen=variablesize;
1443 }
1444 parameter[paramindex].datatype=CS_FLOAT_TYPE;
1445 parameter[paramindex].maxlength=CS_UNUSED;
1446 parameter[paramindex].status=CS_RETURN;
1447 parameter[paramindex].locale=NULL;
1448 if (ct_param(cmd,¶meter[paramindex],
1449 (CS_VOID *)NULL,0,
1450 (CS_SMALLINT)*isnull)!=CS_SUCCEED) {
1451 return false;
1452 }
1453 paramindex++;
1454 return true;
1455 }
1456
outputBind(const char * variable,uint16_t variablesize,int16_t * year,int16_t * month,int16_t * day,int16_t * hour,int16_t * minute,int16_t * second,int32_t * microsecond,const char ** tz,bool * isnegative,char * buffer,uint16_t buffersize,int16_t * isnull)1457 bool freetdscursor::outputBind(const char *variable,
1458 uint16_t variablesize,
1459 int16_t *year,
1460 int16_t *month,
1461 int16_t *day,
1462 int16_t *hour,
1463 int16_t *minute,
1464 int16_t *second,
1465 int32_t *microsecond,
1466 const char **tz,
1467 bool *isnegative,
1468 char *buffer,
1469 uint16_t buffersize,
1470 int16_t *isnull) {
1471
1472 checkRePrepare();
1473
1474 outbindtype[outbindindex]=CS_DATETIME_TYPE;
1475 outbinddates[outbindindex].year=year;
1476 outbinddates[outbindindex].month=month;
1477 outbinddates[outbindindex].day=day;
1478 outbinddates[outbindindex].hour=hour;
1479 outbinddates[outbindindex].minute=minute;
1480 outbinddates[outbindindex].second=second;
1481 outbinddates[outbindindex].microsecond=microsecond;
1482 outbinddates[outbindindex].tz=tz;
1483 outbinddates[outbindindex].isnegative=isnegative;
1484 outbindindex++;
1485
1486 bytestring::zero(¶meter[paramindex],sizeof(parameter[paramindex]));
1487 if (charstring::isInteger(variable+1,variablesize-1)) {
1488 parameter[paramindex].name[0]='\0';
1489 parameter[paramindex].namelen=0;
1490 } else {
1491 charstring::copy(parameter[paramindex].name,variable);
1492 parameter[paramindex].namelen=variablesize;
1493 }
1494 parameter[paramindex].datatype=CS_DATETIME_TYPE;
1495 parameter[paramindex].maxlength=CS_UNUSED;
1496 parameter[paramindex].status=CS_RETURN;
1497 parameter[paramindex].locale=NULL;
1498 if (ct_param(cmd,¶meter[paramindex],
1499 (CS_VOID *)NULL,0,
1500 (CS_SMALLINT)*isnull)!=CS_SUCCEED) {
1501 return false;
1502 }
1503 paramindex++;
1504 return true;
1505 }
1506 #endif
1507
executeQuery(const char * query,uint32_t length)1508 bool freetdscursor::executeQuery(const char *query, uint32_t length) {
1509
1510 // initialize results (We use CS_UNSUPPORTED so that if the query
1511 // fails to execute, discardResults won't attempt to cancel any
1512 // non-existent result sets. Doing that crashes FreeTDS.)
1513 results=CS_UNSUPPORTED;
1514
1515 // clear out any errors
1516 freetdsconn->errorcode=0;
1517 freetdsconn->liveconnection=true;
1518
1519 if (ct_command(cmd,CS_LANG_CMD,
1520 (CS_CHAR *)query,length,
1521 CS_UNUSED)!=CS_SUCCEED) {
1522 return false;
1523 }
1524 clean=false;
1525
1526 // initialize row counts
1527 affectedrows=0;
1528 row=0;
1529 maxrow=0;
1530 totalrows=0;
1531
1532 #ifdef FREETDS_SUPPORTS_CURSORS
1533 if (cmd==cursorcmd) {
1534 if (ct_cursor(cursorcmd,CS_CURSOR_ROWS,
1535 NULL,CS_UNUSED,
1536 NULL,CS_UNUSED,
1537 (CS_INT)conn->cont->getFetchAtOnce())!=
1538 CS_SUCCEED) {
1539 return false;
1540 }
1541 if (ct_cursor(cursorcmd,CS_CURSOR_OPEN,
1542 NULL,CS_UNUSED,
1543 NULL,CS_UNUSED,
1544 CS_UNUSED)!=CS_SUCCEED) {
1545 return false;
1546 }
1547 }
1548 #endif
1549
1550 if (ct_send(cmd)!=CS_SUCCEED) {
1551 closeResultSet();
1552 return false;
1553 }
1554
1555 for (;;) {
1556
1557 results=ct_results(cmd,&resultstype);
1558
1559 // handle the end of all result sets
1560 if (results==CS_END_RESULTS) {
1561 break;
1562 }
1563
1564 // handle failed queries
1565 if (results==CS_FAIL || resultstype==CS_CMD_FAIL) {
1566 closeResultSet();
1567 return false;
1568 }
1569
1570 // Queries can generate multiple result sets.
1571 //
1572 // A DML/DDL query will just send a CS_CMD_SUCCEED.
1573 //
1574 // If we're not using cursors, then selects will also just
1575 // send a single CS_ROW_RESULT.
1576 //
1577 // But...
1578 //
1579 // If a cursor is used to execute a select, then each
1580 // ct_cursor() call generates a results set, and then the
1581 // ct_send also generates a CS_ROW_RESULT result set.
1582 //
1583 // RPC queries (EXEC some-stored-procedures or direct
1584 // TransactSQL may generate a series of result sets including
1585 // CS_CMD_SUCCEED, CS_CMD_DONE, CS_STATUS_RESULT, CS_ROW_RESULT
1586 // or CS_COMPUTE_RESULT result sets, in any combination or
1587 // order.
1588 //
1589 // Currently SQL Relay only supports 1 result set per query, so
1590 // for a given query, we only really care about one result set,
1591 // the CS_PARAM_RESULT, CS_ROW_RESULT, CS_CURSOR_RESULT, or
1592 // CS_COMPUTE_RESULT. We'll grab whichever of those we get
1593 // first, and ignore the rest.
1594
1595 if (resultstype==CS_CMD_SUCCEED) {
1596 // If we got CS_CMD_SUCCEED, then try to get the
1597 // affected row count. The query might have been
1598 // DML/DDL, or this could be one of the result sets of
1599 // a stored procedure or direct TransactSQL. We need
1600 // to do this here because we're going to cancel this
1601 // result set below.
1602 affectedrows=0;
1603 if (knowsaffectedrows) {
1604 if (ct_res_info(cmd,CS_ROW_COUNT,
1605 (CS_VOID *)&affectedrows,
1606 CS_UNUSED,(CS_INT *)NULL)!=CS_SUCCEED) {
1607 return false;
1608 }
1609 }
1610 } else if (resultstype==CS_PARAM_RESULT ||
1611 resultstype==CS_ROW_RESULT ||
1612 resultstype==CS_CURSOR_RESULT ||
1613 resultstype==CS_COMPUTE_RESULT) {
1614 break;
1615 }
1616
1617 // the result set was a type that we want to ignore
1618 if (ct_cancel(NULL,cmd,CS_CANCEL_CURRENT)==CS_FAIL) {
1619 freetdsconn->liveconnection=false;
1620 // FIXME: call ct_close(CS_FORCE_CLOSE)?
1621 return false;
1622 }
1623 }
1624
1625 checkForTempTable(query,length);
1626
1627 // reset the prepared flag
1628 prepared=false;
1629
1630 // For queries which return rows or parameters (output bind variables),
1631 // get the column count and bind columns.
1632 bool moneycolumn=false;
1633 if (resultstype==CS_ROW_RESULT ||
1634 resultstype==CS_CURSOR_RESULT ||
1635 resultstype==CS_COMPUTE_RESULT ||
1636 resultstype==CS_PARAM_RESULT) {
1637
1638 // get the column count
1639 if (ct_res_info(cmd,CS_NUMDATA,(CS_VOID *)&ncols,
1640 CS_UNUSED,(CS_INT *)NULL)!=CS_SUCCEED) {
1641 return false;
1642 }
1643
1644 // allocate buffers and limit column count if necessary
1645 uint32_t maxcolumncount=conn->cont->getMaxColumnCount();
1646 if (!maxcolumncount) {
1647 // see note in discardResults for
1648 // why we're doing this here
1649 deallocateResultSetBuffers();
1650 allocateResultSetBuffers(ncols);
1651 } else if ((uint32_t)ncols>maxcolumncount) {
1652 ncols=maxcolumncount;
1653 }
1654
1655 // bind columns
1656 for (CS_INT i=0; i<ncols; i++) {
1657
1658 // dealing with money columns cause freetds < 0.53 to
1659 // crash, take care of that here...
1660 if (majorversion==0 && minorversion<53
1661 && !moneycolumn) {
1662 CS_DATAFMT moneytest;
1663 ct_describe(cmd,i+1,&moneytest);
1664 if (moneytest.datatype==CS_MONEY_TYPE ||
1665 moneytest.datatype==CS_MONEY4_TYPE) {
1666 moneycolumn=true;
1667 freetdsconn->errorstring.clear();
1668 freetdsconn->errorstring.append(
1669 "FreeTDS versions prior to "
1670 "0.53 do not support MONEY "
1671 "or SMALLMONEY datatypes. "
1672 "Please upgrade SQL Relay to "
1673 "a version compiled against "
1674 "FreeTDS >= 0.53 ");
1675 }
1676 }
1677
1678 // reset the column-bind
1679 column[i]=templatecolumn;
1680
1681 // actually...
1682 // if we're getting the output bind variables of a
1683 // stored procedure that returns dates, then use the
1684 // datetime type instead...
1685 if (resultstype==CS_PARAM_RESULT &&
1686 outbindtype[i]==CS_DATETIME_TYPE) {
1687 column[i].datatype=CS_DATETIME_TYPE;
1688 column[i].format=CS_FMT_UNUSED;
1689 column[i].maxlength=sizeof(CS_DATETIME);
1690 }
1691
1692 // bind the columns for the fetches
1693 if (ct_bind(cmd,i+1,
1694 &column[i],
1695 (CS_VOID *)data[i],
1696 datalength[i],
1697 nullindicator[i])!=CS_SUCCEED) {
1698 break;
1699 }
1700
1701 // describe the columns
1702 if (conn->cont->getSendColumnInfo()==SEND_COLUMN_INFO) {
1703 if (ct_describe(cmd,i+1,
1704 &column[i])!=CS_SUCCEED) {
1705 break;
1706 }
1707 }
1708 }
1709
1710 }
1711
1712 // If we got a moneycolumn (and version<0.53) then cancel the
1713 // result set. Otherwise FreeTDS will spew "unknown marker"
1714 // errors to the screen when closeResultSet() is called.
1715 if (moneycolumn) {
1716 if (ct_cancel(NULL,cmd,CS_CANCEL_CURRENT)==CS_FAIL) {
1717 freetdsconn->liveconnection=false;
1718 // FIXME: call ct_close(CS_FORCE_CLOSE)?
1719 return false;
1720 }
1721 return false;
1722 }
1723
1724
1725 // if we're doing an rpc query, the result set should be a single
1726 // row of output parameter results, fetch it and populate the output
1727 // bind variables...
1728 if (resultstype==CS_PARAM_RESULT) {
1729
1730 if (ct_fetch(cmd,
1731 CS_UNUSED,
1732 CS_UNUSED,
1733 CS_UNUSED,
1734 &rowsread)!=CS_SUCCEED || !rowsread) {
1735 return false;
1736 }
1737
1738 // copy data into output bind values
1739 CS_INT maxindex=outbindindex;
1740 if (ncols<outbindindex) {
1741 // this shouldn't happen...
1742 maxindex=ncols;
1743 }
1744 for (CS_INT i=0; i<maxindex; i++) {
1745 if (outbindtype[i]==CS_CHAR_TYPE) {
1746 CS_INT length=outbindstringlengths[i];
1747 if (datalength[i][0]<length) {
1748 length=datalength[i][0];
1749 }
1750 bytestring::copy(outbindstrings[i],
1751 data[i],length);
1752 } else if (outbindtype[i]==CS_INT_TYPE) {
1753 *outbindints[i]=
1754 charstring::toInteger(data[i]);
1755 } else if (outbindtype[i]==CS_FLOAT_TYPE) {
1756 *outbinddoubles[i]=
1757 charstring::toFloatC(data[i]);
1758 } else if (outbindtype[i]==CS_DATETIME_TYPE) {
1759
1760 // convert to a CS_DATEREC
1761 CS_DATEREC dr;
1762 cs_dt_crack(freetdsconn->context,
1763 CS_DATETIME_TYPE,
1764 (CS_VOID *)data[i],&dr);
1765
1766 datebind *db=&outbinddates[i];
1767 *(db->year)=dr.dateyear;
1768 *(db->month)=dr.datemonth+1;
1769 *(db->day)=dr.datedmonth;
1770 *(db->hour)=dr.datehour;
1771 *(db->minute)=dr.dateminute;
1772 *(db->second)=dr.datesecond;
1773 *(db->microsecond)=dr.datemsecond;
1774 *(db->tz)=NULL;
1775 *(db->isnegative)=false;
1776 }
1777 }
1778
1779
1780 discardResults();
1781 ncols=0;
1782 }
1783
1784 // return success only if no error was generated
1785 return (!freetdsconn->errorcode);
1786 }
1787
knowsAffectedRows()1788 bool freetdscursor::knowsAffectedRows() {
1789 return knowsaffectedrows;
1790 }
1791
affectedRows()1792 uint64_t freetdscursor::affectedRows() {
1793 return affectedrows;
1794 }
1795
colCount()1796 uint32_t freetdscursor::colCount() {
1797 return ncols;
1798 }
1799
getColumnName(uint32_t col)1800 const char *freetdscursor::getColumnName(uint32_t col) {
1801 return column[col].name;
1802 }
1803
getColumnType(uint32_t col)1804 uint16_t freetdscursor::getColumnType(uint32_t col) {
1805 switch (column[col].datatype) {
1806 case CS_CHAR_TYPE:
1807 return CHAR_DATATYPE;
1808 case CS_INT_TYPE:
1809 return INT_DATATYPE;
1810 case CS_SMALLINT_TYPE:
1811 return SMALLINT_DATATYPE;
1812 case CS_TINYINT_TYPE:
1813 return TINYINT_DATATYPE;
1814 case CS_MONEY_TYPE:
1815 return MONEY_DATATYPE;
1816 case CS_DATETIME_TYPE:
1817 return DATETIME_DATATYPE;
1818 case CS_NUMERIC_TYPE:
1819 return NUMERIC_DATATYPE;
1820 case CS_DECIMAL_TYPE:
1821 return DECIMAL_DATATYPE;
1822 case CS_DATETIME4_TYPE:
1823 return SMALLDATETIME_DATATYPE;
1824 case CS_MONEY4_TYPE:
1825 return SMALLMONEY_DATATYPE;
1826 case CS_IMAGE_TYPE:
1827 return IMAGE_DATATYPE;
1828 case CS_BINARY_TYPE:
1829 return BINARY_DATATYPE;
1830 case CS_BIT_TYPE:
1831 return BIT_DATATYPE;
1832 case CS_REAL_TYPE:
1833 return REAL_DATATYPE;
1834 case CS_FLOAT_TYPE:
1835 return FLOAT_DATATYPE;
1836 case CS_TEXT_TYPE:
1837 return TEXT_DATATYPE;
1838 case CS_VARCHAR_TYPE:
1839 return VARCHAR_DATATYPE;
1840 case CS_VARBINARY_TYPE:
1841 return VARBINARY_DATATYPE;
1842 case CS_LONGCHAR_TYPE:
1843 return LONGCHAR_DATATYPE;
1844 case CS_LONGBINARY_TYPE:
1845 return LONGBINARY_DATATYPE;
1846 case CS_LONG_TYPE:
1847 return LONG_DATATYPE;
1848 case CS_ILLEGAL_TYPE:
1849 return ILLEGAL_DATATYPE;
1850 case CS_SENSITIVITY_TYPE:
1851 return SENSITIVITY_DATATYPE;
1852 case CS_BOUNDARY_TYPE:
1853 return BOUNDARY_DATATYPE;
1854 case CS_VOID_TYPE:
1855 return VOID_DATATYPE;
1856 case CS_USHORT_TYPE:
1857 return USHORT_DATATYPE;
1858 #ifdef CS_BIGINT_TYPE
1859 case CS_BIGINT_TYPE:
1860 return BIGINT_DATATYPE;
1861 #endif
1862 #ifdef CS_UBIGINT_TYPE
1863 case CS_UBIGINT_TYPE:
1864 return UBIGINT_DATATYPE;
1865 #endif
1866 #ifdef CS_UNIQUE_TYPE
1867 case CS_UNIQUE_TYPE:
1868 return UNIQUEIDENTIFIER_DATATYPE;
1869 #endif
1870 default:
1871 return UNKNOWN_DATATYPE;
1872 }
1873 }
1874
getColumnLength(uint32_t col)1875 uint32_t freetdscursor::getColumnLength(uint32_t col) {
1876 // limit the column size
1877 uint32_t maxfieldlength=conn->cont->getMaxFieldLength();
1878 if ((uint32_t)column[col].maxlength>maxfieldlength) {
1879 column[col].maxlength=maxfieldlength;
1880 }
1881 return column[col].maxlength;
1882 }
1883
getColumnPrecision(uint32_t col)1884 uint32_t freetdscursor::getColumnPrecision(uint32_t col) {
1885 return column[col].precision;
1886 }
1887
getColumnScale(uint32_t col)1888 uint32_t freetdscursor::getColumnScale(uint32_t col) {
1889 return column[col].scale;
1890 }
1891
getColumnIsNullable(uint32_t col)1892 uint16_t freetdscursor::getColumnIsNullable(uint32_t col) {
1893 return (column[col].status&CS_CANBENULL);
1894 }
1895
getColumnIsPartOfKey(uint32_t col)1896 uint16_t freetdscursor::getColumnIsPartOfKey(uint32_t col) {
1897 return (column[col].status&(CS_KEY|CS_VERSION_KEY));
1898 }
1899
getColumnIsUnsigned(uint32_t col)1900 uint16_t freetdscursor::getColumnIsUnsigned(uint32_t col) {
1901 return (getColumnType(col)==USHORT_DATATYPE);
1902 }
1903
getColumnIsBinary(uint32_t col)1904 uint16_t freetdscursor::getColumnIsBinary(uint32_t col) {
1905 return (getColumnType(col)==IMAGE_DATATYPE);
1906 }
1907
getColumnIsAutoIncrement(uint32_t col)1908 uint16_t freetdscursor::getColumnIsAutoIncrement(uint32_t col) {
1909 return (column[col].status&CS_IDENTITY);
1910 }
1911
ignoreDateDdMmParameter(uint32_t col,const char * data,uint32_t size)1912 bool freetdscursor::ignoreDateDdMmParameter(uint32_t col,
1913 const char *data, uint32_t size) {
1914
1915 // This is for a very FreeTDS/MSSQL Server-specific issue...
1916 //
1917 // If we're translating dates in the result set then we have to be
1918 // careful about dates in the yyyy-xx-xx format.
1919 //
1920 // FreeTDS recognizes Sybase date/datetime columns and MSSQL datetime
1921 // columns as type CS_DATETIME_TYPE and formats them according to the
1922 // rules defined in locales.conf for the locale of the SQL Relay server.
1923 //
1924 // However, FreeTDS recognizes MSSQL date columns as type CS_CHAR_TYPE
1925 // and formats them in yyyy-mm-dd format universally.
1926 //
1927 // The date conversion routines take any fields that look like a date
1928 // and translate them to the specified format. Unfortunately if you're
1929 // in a region where dates are formatted dd/mm/yyyy (for example) then
1930 // dateddmm="yes" needs to be set so string literals like "03/04/2005"
1931 // will be recognized as April 3, 2005. This would cause problems when
1932 // fetching date columns from MSSQL so we have to ignore dateddmm in
1933 // that case.
1934 //
1935 // Ideally we'd only ignore dateddmm for MSSQL, on CS_DATETIME_TYPE
1936 // columns that appeared to be in the yyyy-xx-xx format. But, since
1937 // date columns are returned as CS_CHAR_TYPE then we can't. So, we
1938 // end up ignoring dateddmm for everything in yyyy-xx-xx format, whether
1939 // fetched from a date column or from a string column or from a
1940 // string literal in the original query.
1941 //
1942 // That means that if date translation is in effect, if dates are stored
1943 // in string fields in yyyy-xx-xx format, then they must be stored in
1944 // yyyy-mm-dd format.
1945 //
1946 // It also means that if date translation in in effect then unless the
1947 // translatedatetimes translation module is being used to normalize
1948 // string literals in the original query to some other format, then
1949 // they have to be in yyyy-mm-dd format as well.
1950
1951 // Override the dateddmm parameter if we're using MSSQL Server and the
1952 // fields is in yyyy-xx-xx format and appears to be a date.
1953 return (!freetdsconn->sybasedb &&
1954 size==10 &&
1955 data[4]=='-' && data[7]=='-' &&
1956 charstring::isNumber(data,4) &&
1957 charstring::isNumber(data+5,2) &&
1958 charstring::isNumber(data+8,2));
1959 }
1960
noRowsToReturn()1961 bool freetdscursor::noRowsToReturn() {
1962 // unless the query was a successful select, send no data
1963 return (resultstype!=CS_ROW_RESULT &&
1964 resultstype!=CS_CURSOR_RESULT &&
1965 resultstype!=CS_COMPUTE_RESULT);
1966 }
1967
skipRow(bool * error)1968 bool freetdscursor::skipRow(bool *error) {
1969 if (fetchRow(error)) {
1970 row++;
1971 return true;
1972 }
1973 return false;
1974 }
1975
fetchRow(bool * error)1976 bool freetdscursor::fetchRow(bool *error) {
1977
1978 *error=false;
1979 // FIXME: set error if an error occurs
1980
1981 if (row==(CS_INT)conn->cont->getFetchAtOnce()) {
1982 row=0;
1983 }
1984 if (row>0 && row==maxrow) {
1985 return false;
1986 }
1987 if (!row) {
1988 CS_RETCODE fetchresult=ct_fetch(cmd,CS_UNUSED,
1989 CS_UNUSED,
1990 CS_UNUSED,
1991 &rowsread);
1992
1993 // This is essential with freetds.
1994 // http://www.freetds.org/faq.html#pending
1995 //
1996 // Basically the TDS protocol doesn't handle multiple
1997 // simultaneous queries per connection. You can open multiple
1998 // cursors but you can't use them at the same time. Somehow
1999 // Sybase gets around this but FreeTDS doesn't. In particular,
2000 // unless all rows and all results sets have been fetched or
2001 // cancelled for all cursors, another cursor cannot run another
2002 // query. Since TDS supports multiple result sets per query,
2003 // it's not enough to just fetch all of the rows, all result
2004 // sets must be fetched or cancelled as well until ct_results
2005 // returns CS_END_RESULTS or CS_CANCELLED. Since SQL Relay only
2006 // supports one result set per query, we can go ahead and cancel
2007 // any remaining result sets here. closeResultSet would do this
2008 // for us but not before another query gets run on the same
2009 // cursor, so we must explicitly call it here too in case
2010 // someone wants to do something with another cursor.
2011 if (fetchresult==CS_END_DATA) {
2012 discardResults();
2013 }
2014
2015 if (fetchresult!=CS_SUCCEED || !rowsread) {
2016 if (fetchresult==CS_FAIL || fetchresult==CS_ROW_FAIL) {
2017 *error=true;
2018 }
2019 return false;
2020 }
2021 maxrow=rowsread;
2022 totalrows=totalrows+rowsread;
2023 }
2024 return true;
2025 }
2026
getField(uint32_t col,const char ** field,uint64_t * fieldlength,bool * blob,bool * null)2027 void freetdscursor::getField(uint32_t col,
2028 const char **field, uint64_t *fieldlength,
2029 bool *blob, bool *null) {
2030
2031 // handle NULLs
2032 if (nullindicator[col][row]==-1) {
2033 *null=true;
2034 return;
2035 }
2036
2037 uint32_t maxfieldlength=conn->cont->getMaxFieldLength();
2038
2039 // Empty TEXT fields don't get properly converted
2040 // to null-terminated strings. Handle them.
2041 if (column[col].datatype==CS_TEXT_TYPE && !datalength[col][row]) {
2042 data[col][row*maxfieldlength]='\0';
2043 datalength[col][row]=1;
2044 }
2045
2046 // handle normal datatypes
2047 *field=&data[col][row*maxfieldlength];
2048 *fieldlength=datalength[col][row]-1;
2049 }
2050
nextRow()2051 void freetdscursor::nextRow() {
2052 row++;
2053 }
2054
closeResultSet()2055 void freetdscursor::closeResultSet() {
2056
2057 if (clean) {
2058 return;
2059 }
2060
2061 discardResults();
2062 discardCursor();
2063
2064 clean=true;
2065 }
2066
discardResults()2067 void freetdscursor::discardResults() {
2068
2069 // if there are any unprocessed result sets, process them
2070 if (results==CS_SUCCEED) {
2071 do {
2072 if (ct_cancel(NULL,cmd,CS_CANCEL_CURRENT)==CS_FAIL) {
2073 freetdsconn->liveconnection=false;
2074 // FIXME: call ct_close(CS_FORCE_CLOSE)?
2075 }
2076 results=ct_results(cmd,&resultstype);
2077 } while (results==CS_SUCCEED);
2078 }
2079
2080 if (results==CS_FAIL) {
2081 if (ct_cancel(NULL,cmd,CS_CANCEL_ALL)==CS_FAIL) {
2082 freetdsconn->liveconnection=false;
2083 // FIXME: call ct_close(CS_FORCE_CLOSE)?
2084 }
2085 }
2086
2087 // Deallocating the result set buffers here causes a problem, but only
2088 // in the freetds connection.
2089 // When using freetds, we have to call discardResults() when we hit
2090 // the end of the result set (see note in fetchRow()) but if we
2091 // deallocate the result set buffers at that point, then subsequent
2092 // attempts to fetch column info, will result in a reference-after-free.
2093 // So, unlike the in the sybase/sap connection code we defer the
2094 // deallocate until right before the next allocate, in prepareQuery().
2095 /*if (!conn->cont->getMaxColumnCount()) {
2096 deallocateResultSetBuffers();
2097 }*/
2098 }
2099
2100
discardCursor()2101 void freetdscursor::discardCursor() {
2102
2103 #ifdef FREETDS_SUPPORTS_CURSORS
2104 if (cmd==cursorcmd) {
2105 if (ct_cursor(cursorcmd,CS_CURSOR_CLOSE,
2106 NULL,CS_UNUSED,
2107 NULL,CS_UNUSED,
2108 CS_DEALLOC)==CS_SUCCEED) {
2109 if (ct_send(cursorcmd)==CS_SUCCEED) {
2110 results=ct_results(cmd,&resultstype);
2111 discardResults();
2112 }
2113 }
2114 }
2115 #endif
2116 }
2117
csMessageCallback(CS_CONTEXT * ctxt,CS_CLIENTMSG * msgp)2118 CS_RETCODE freetdsconnection::csMessageCallback(CS_CONTEXT *ctxt,
2119 CS_CLIENTMSG *msgp) {
2120 if (errorcode) {
2121 return CS_SUCCEED;
2122 }
2123
2124 errorcode=msgp->msgnumber;
2125
2126 errorstring.clear();
2127 errorstring.append("Client Library error: ")->append(msgp->msgstring);
2128 errorstring.append(" severity(")->
2129 append((int32_t)CS_SEVERITY(msgp->msgnumber))->append(")");
2130 errorstring.append(" layer(")->
2131 append((int32_t)CS_LAYER(msgp->msgnumber))->append(")");
2132 errorstring.append(" origin(")->
2133 append((int32_t)CS_ORIGIN(msgp->msgnumber))->append(")");
2134 errorstring.append(" number(")->
2135 append((int32_t)CS_NUMBER(msgp->msgnumber))->append(")");
2136
2137 if (msgp->osstringlen>0) {
2138 errorstring.append(" Operating System Error: ");
2139 errorstring.append(msgp->osstring);
2140 }
2141
2142 // for a timeout message,
2143 // set liveconnection to false
2144 if (CS_SEVERITY(msgp->msgnumber)==CS_SV_RETRY_FAIL &&
2145 CS_LAYER(msgp->msgnumber)==63 &&
2146 CS_ORIGIN(msgp->msgnumber)==63 &&
2147 CS_NUMBER(msgp->msgnumber)==63) {
2148 liveconnection=false;
2149
2150 // for a read from sql server failed message,
2151 // set liveconnection to false
2152 } else if (CS_SEVERITY(msgp->msgnumber)==78 &&
2153 CS_LAYER(msgp->msgnumber)==0 &&
2154 CS_ORIGIN(msgp->msgnumber)==0 &&
2155 (CS_NUMBER(msgp->msgnumber)==36 ||
2156 CS_NUMBER(msgp->msgnumber)==38)) {
2157 liveconnection=false;
2158 }
2159 // FIXME: sybase connection has another case, do we need it?
2160
2161 return CS_SUCCEED;
2162 }
2163
clientMessageCallback(CS_CONTEXT * ctxt,CS_CONNECTION * cnn,CS_CLIENTMSG * msgp)2164 CS_RETCODE freetdsconnection::clientMessageCallback(CS_CONTEXT *ctxt,
2165 CS_CONNECTION *cnn,
2166 CS_CLIENTMSG *msgp) {
2167 if (errorcode) {
2168 return CS_SUCCEED;
2169 }
2170
2171 errorcode=msgp->msgnumber;
2172
2173 errorstring.clear();
2174 errorstring.append("Client Library error: ")->append(msgp->msgstring);
2175 errorstring.append(" severity(")->
2176 append((int32_t)CS_SEVERITY(msgp->msgnumber))->append(")");
2177 errorstring.append(" layer(")->
2178 append((int32_t)CS_LAYER(msgp->msgnumber))->append(")");
2179 errorstring.append(" origin(")->
2180 append((int32_t)CS_ORIGIN(msgp->msgnumber))->append(")");
2181 errorstring.append(" number(")->
2182 append((int32_t)CS_NUMBER(msgp->msgnumber))->append(")");
2183
2184 if (msgp->osstringlen>0) {
2185 errorstring.append(" Operating System Error: ");
2186 errorstring.append(msgp->osstring);
2187 }
2188
2189 // for a timeout message,
2190 // set liveconnection to false
2191 if (CS_SEVERITY(msgp->msgnumber)==CS_SV_RETRY_FAIL &&
2192 CS_LAYER(msgp->msgnumber)==63 &&
2193 CS_ORIGIN(msgp->msgnumber)==63 &&
2194 CS_NUMBER(msgp->msgnumber)==63) {
2195 liveconnection=false;
2196
2197 // for a read from sql server failed message,
2198 // set liveconnection to false
2199 } else if (CS_SEVERITY(msgp->msgnumber)==78 &&
2200 CS_LAYER(msgp->msgnumber)==0 &&
2201 CS_ORIGIN(msgp->msgnumber)==0 &&
2202 (CS_NUMBER(msgp->msgnumber)==36 ||
2203 CS_NUMBER(msgp->msgnumber)==38)) {
2204 liveconnection=false;
2205 }
2206 // FIXME: sybase connection has another case, do we need it?
2207
2208 return CS_SUCCEED;
2209 }
2210
serverMessageCallback(CS_CONTEXT * ctxt,CS_CONNECTION * cnn,CS_SERVERMSG * msgp)2211 CS_RETCODE freetdsconnection::serverMessageCallback(CS_CONTEXT *ctxt,
2212 CS_CONNECTION *cnn,
2213 CS_SERVERMSG *msgp) {
2214
2215 // This is a special case, for some reason, "use db" queries
2216 // throw a warning, ignore them.
2217 if ((CS_NUMBER(msgp->msgnumber)==5701 &&
2218 (CS_SEVERITY(msgp->msgnumber)==10 ||
2219 CS_SEVERITY(msgp->msgnumber)==0)) ||
2220 (CS_NUMBER(msgp->msgnumber)==69 &&
2221 CS_SEVERITY(msgp->msgnumber)==22)) {
2222 return CS_SUCCEED;
2223 }
2224
2225 if (errorcode) {
2226 return CS_SUCCEED;
2227 }
2228
2229 errorcode=msgp->msgnumber;
2230
2231 errorstring.clear();
2232 errorstring.append("Server message: ")->append(msgp->text);
2233 errorstring.append(" severity(")->
2234 append((int32_t)CS_SEVERITY(msgp->msgnumber))->append(")");
2235 errorstring.append(" number(")->
2236 append((int32_t)CS_NUMBER(msgp->msgnumber))->append(")");
2237 errorstring.append(" state(")->
2238 append((int32_t)msgp->state)->append(")");
2239 errorstring.append(" line(")->
2240 append((int32_t)msgp->line)->append(")");
2241 errorstring.append(" Server Name:")->append(msgp->svrname);
2242 errorstring.append(" Procedure Name:")->append(msgp->proc);
2243
2244 return CS_SUCCEED;
2245 }
2246
tempTableDropPrefix()2247 const char *freetdsconnection::tempTableDropPrefix() {
2248 return "#";
2249 }
2250
commit()2251 bool freetdsconnection::commit() {
2252 cont->closeAllResultSets();
2253 return sqlrserverconnection::commit();
2254 }
2255
rollback()2256 bool freetdsconnection::rollback() {
2257 cont->closeAllResultSets();
2258 return sqlrserverconnection::rollback();
2259 }
2260
errorMessage(char * errorbuffer,uint32_t errorbufferlength,uint32_t * errorlength,int64_t * errorcode,bool * liveconnection)2261 void freetdsconnection::errorMessage(char *errorbuffer,
2262 uint32_t errorbufferlength,
2263 uint32_t *errorlength,
2264 int64_t *errorcode,
2265 bool *liveconnection) {
2266 *errorlength=this->errorstring.getStringLength();
2267 charstring::safeCopy(errorbuffer,errorbufferlength,
2268 this->errorstring.getString(),*errorlength);
2269 *liveconnection=this->liveconnection;
2270 *errorcode=this->errorcode;
2271 }
2272
2273 extern "C" {
new_freetdsconnection(sqlrservercontroller * cont)2274 SQLRSERVER_DLLSPEC sqlrserverconnection *new_freetdsconnection(
2275 sqlrservercontroller *cont) {
2276 return new freetdsconnection(cont);
2277 }
2278 }
2279