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(&parameter[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,&parameter[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(&parameter[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,&parameter[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(&parameter[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,&parameter[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(&parameter[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,&parameter[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(&parameter[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,&parameter[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(&parameter[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,&parameter[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(&parameter[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,&parameter[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