1 // Copyright (c) 1999-2018 David Muse
2 // See the file COPYING for more information
3 
4 #include <sqlrelay/sqlrserver.h>
5 #include <rudiments/bytestring.h>
6 #include <rudiments/character.h>
7 #include <rudiments/snooze.h>
8 #include <rudiments/regularexpression.h>
9 
10 #include <datatypes.h>
11 #include <defines.h>
12 #include <config.h>
13 
14 #include <sqlrelay/sqlrclient.h>
15 
16 struct outputbindvar {
17 	const char	*variable;
18 	union {
19 		char		*stringvalue;
20 		int64_t		*intvalue;
21 		double		*doublevalue;
22 		struct {
23 			int16_t		*year;
24 			int16_t		*month;
25 			int16_t		*day;
26 			int16_t		*hour;
27 			int16_t		*minute;
28 			int16_t		*second;
29 			int32_t		*microsecond;
30 			const char	**tz;
31 			bool		*isnegative;
32 		} datevalue;
33 	} value;
34 	uint32_t		valuesize;
35 	sqlrserverbindvartype_t	type;
36 	int16_t			*isnull;
37 };
38 
39 struct cursorbindvar {
40 	const char	*variable;
41 	sqlrservercursor	*cursor;
42 };
43 
44 class routercursor;
45 
46 class SQLRSERVER_DLLSPEC routerconnection : public sqlrserverconnection {
47 	friend class routercursor;
48 	public:
49 			routerconnection(sqlrservercontroller *cont);
50 			~routerconnection();
51 	private:
52 		bool		supportsAuthOnDatabase();
53 		void		handleConnectString();
54 		bool		logIn(const char **error, const char **warning);
55 		sqlrservercursor	*newCursor(uint16_t id);
56 		void		deleteCursor(sqlrservercursor *curs);
57 		void		logOut();
58 		bool		autoCommitOn();
59 		bool		autoCommitOff();
60 		bool		supportsAutoCommit();
61 		bool		begin();
62 		bool		commit();
63 		bool		rollback();
64 		void		errorMessage(char *errorbuffer,
65 						uint32_t errorbufferlength,
66 						uint32_t *errorlength,
67 						int64_t	*errorcode,
68 						bool *liveconnection);
69 		const char	*identify();
70 		const char	*dbVersion();
71 		const char	*dbHostName();
72 		const char	*dbIpAddress();
73 		bool		cacheDbHostInfo();
74 		bool		getListsByApiCalls();
75 		bool		getDatabaseList(sqlrservercursor *cursor,
76 						const char *wild);
77 		bool		getTableList(sqlrservercursor *cursor,
78 						const char *wild,
79 						uint16_t objecttypes);
80 		bool		getColumnList(sqlrservercursor *cursor,
81 						const char *table,
82 						const char *wild);
83 		bool		ping();
84 		const char	*selectDatabaseQuery();
85 		char		*getCurrentDatabase();
86 		bool		getLastInsertId(uint64_t *id);
87 		void		endSession();
88 
89 		void	route(bool *routed, bool *err);
90 
91 		void	autoCommitOnFailed(uint16_t index);
92 		void	autoCommitOffFailed(uint16_t index);
93 		void	beginFailed(uint16_t index);
94 		void	commitFailed(uint16_t index);
95 		void	rollbackFailed(uint16_t index);
96 		void	beginQueryFailed(uint16_t index);
97 		void	raiseIntegrityViolationEvent(const char *command,
98 								uint16_t index);
99 		const char	*identity;
100 
101 		const char	**conids;
102 		sqlrconnection	**cons;
103 		uint16_t	concount;
104 		const char	**beginquery;
105 		bool		anymustbegin;
106 
107 		sqlrconnection	*currentcon;
108 		uint16_t	currentconindex;
109 
110 		bool		justloggedin;
111 
112 		int16_t		nullbindvalue;
113 		int16_t		nonnullbindvalue;
114 
115 		sqlrrouters	*sqlrr;
116 
117 		bool		routeentiresession;
118 
119 		bool		debug;
120 
121 		linkedlist< routercursor * >	routercursors;
122 };
123 
124 class SQLRSERVER_DLLSPEC routercursor : public sqlrservercursor {
125 	friend class routerconnection;
126 	private:
127 				routercursor(sqlrserverconnection *conn,
128 								uint16_t id);
129 				~routercursor();
130 		bool		prepareQuery(const char *query,
131 						uint32_t length);
132 		void		route(bool *routed, bool *err);
133 		bool		supportsNativeBinds(const char *query,
134 							uint32_t length);
135 		bool		inputBind(const char *variable,
136 						uint16_t variablesize,
137 						const char *value,
138 						uint32_t valuesize,
139 						int16_t *isnull);
140 		bool		inputBind(const char *variable,
141 						uint16_t variablesize,
142 						int64_t *value);
143 		bool		inputBind(const char *variable,
144 						uint16_t variablesize,
145 						double *value,
146 						uint32_t precision,
147 						uint32_t scale);
148 		bool		inputBind(const char *variable,
149 						uint16_t variablesize,
150 						int64_t year,
151 						int16_t month,
152 						int16_t day,
153 						int16_t hour,
154 						int16_t minute,
155 						int16_t second,
156 						int32_t microsecond,
157 						const char *tz,
158 						bool isnegative,
159 						char *buffer,
160 						uint16_t buffersize,
161 						int16_t *isnull);
162 		bool		inputBindBlob(const char *variable,
163 						uint16_t variablesize,
164 						const char *value,
165 						uint32_t valuesize,
166 						int16_t *isnull);
167 		bool		inputBindClob(const char *variable,
168 						uint16_t variablesize,
169 						const char *value,
170 						uint32_t valuesize,
171 						int16_t *isnull);
172 		bool		outputBind(const char *variable,
173 						uint16_t variablesize,
174 						char *value,
175 						uint32_t valuesize,
176 						int16_t *isnull);
177 		bool		outputBind(const char *variable,
178 						uint16_t variablesize,
179 						int64_t *value,
180 						int16_t *isnull);
181 		bool		outputBind(const char *variable,
182 						uint16_t variablesize,
183 						double *value,
184 						uint32_t *precision,
185 						uint32_t *scale,
186 						int16_t *isnull);
187 		bool		outputBind(const char *variable,
188 						uint16_t variablesize,
189 						int16_t *year,
190 						int16_t *month,
191 						int16_t *day,
192 						int16_t *hour,
193 						int16_t *minute,
194 						int16_t *second,
195 						int32_t *microsecond,
196 						const char **tz,
197 						bool *isnegative,
198 						char *buffer,
199 						uint16_t buffersize,
200 						int16_t *isnull);
201 		bool		outputBindBlob(const char *variable,
202 						uint16_t variablesize,
203 						uint16_t index,
204 						int16_t *isnull);
205 		bool		outputBindClob(const char *variable,
206 						uint16_t variablesize,
207 						uint16_t index,
208 						int16_t *isnull);
209 		bool		outputBindCursor(const char *variable,
210 						uint16_t variablesize,
211 						sqlrservercursor *cursor);
212 		bool		getLobOutputBindLength(uint16_t index,
213 						uint64_t *length);
214 		bool		getLobOutputBindSegment(uint16_t index,
215 						char *buffer,
216 						uint64_t buffersize,
217 						uint64_t offset,
218 						uint64_t charstoread,
219 						uint64_t *charsread);
220 		bool		executeQuery(const char *query,
221 						uint32_t length);
222 		void		errorMessage(char *errorbuffer,
223 						uint32_t errorbufferlength,
224 						uint32_t *errorlength,
225 						int64_t	*errorcode,
226 						bool *liveconnection);
227 		bool		knowsRowCount();
228 		uint64_t	rowCount();
229 		uint64_t	affectedRows();
230 		uint32_t	colCount();
231 		uint16_t	columnTypeFormat();
232 		const char	*getColumnName(uint32_t col);
233 		const char	*getColumnTypeName(uint32_t col);
234 		uint32_t	getColumnLength(uint32_t col);
235 		uint32_t	getColumnPrecision(uint32_t col);
236 		uint32_t	getColumnScale(uint32_t col);
237 		uint16_t	getColumnIsNullable(uint32_t col);
238 		uint16_t	getColumnIsPrimaryKey(uint32_t col);
239 		uint16_t	getColumnIsUnique(uint32_t col);
240 		uint16_t	getColumnIsPartOfKey(uint32_t col);
241 		uint16_t	getColumnIsUnsigned(uint32_t col);
242 		uint16_t	getColumnIsZeroFilled(uint32_t col);
243 		uint16_t	getColumnIsBinary(uint32_t col);
244 		uint16_t	getColumnIsAutoIncrement(uint32_t col);
245 		const char	*getColumnTable(uint32_t col);
246 		bool		noRowsToReturn();
247 		bool		fetchRow(bool *error);
248 		void		getField(uint32_t col,
249 					const char **field,
250 					uint64_t *fieldlength,
251 					bool *blob,
252 					bool *null);
253 		void		closeResultSet();
254 
255 		routerconnection	*routerconn;
256 
257 		sqlrconnection	*currentcon;
258 		sqlrcursor	*currentcur;
259 
260 		bool		isbindcur;
261 
262 		sqlrcursor	**curs;
263 
264 		uint64_t	nextrow;
265 
266 		outputbindvar	*obv;
267 		uint16_t	obcount;
268 
269 		cursorbindvar	*cbv;
270 		uint16_t	cbcount;
271 
272 		bool		emptyquery;
273 };
274 
routerconnection(sqlrservercontroller * cont)275 routerconnection::routerconnection(sqlrservercontroller *cont) :
276 					sqlrserverconnection(cont) {
277 	identity=NULL;
278 
279 	conids=NULL;
280 	cons=NULL;
281 	concount=0;
282 	currentcon=NULL;
283 	currentconindex=0;
284 	beginquery=NULL;
285 	anymustbegin=false;
286 	justloggedin=false;
287 	nullbindvalue=nullBindValue();
288 	nonnullbindvalue=nonNullBindValue();
289 
290 	sqlrr=NULL;
291 	routeentiresession=false;
292 
293 	debug=cont->getConfig()->getDebugRouters();
294 }
295 
~routerconnection()296 routerconnection::~routerconnection() {
297 	for (uint16_t index=0; index<concount; index++) {
298 		delete cons[index];
299 	}
300 	delete[] conids;
301 	delete[] cons;
302 	delete[] beginquery;
303 	routercursors.clear();
304 	delete sqlrr;
305 }
306 
supportsAuthOnDatabase()307 bool routerconnection::supportsAuthOnDatabase() {
308 	return false;
309 }
310 
handleConnectString()311 void routerconnection::handleConnectString() {
312 
313 	identity=cont->getConnectStringValue("identity");
314 
315 	// re-get fetchatonce, defaulting to 10, and allowing it to be set to 0
316 	uint32_t	fetchatonce=10;
317 	const char	*fao=cont->getConnectStringValue("fetchatonce");
318 	if (fao) {
319 		fetchatonce=charstring::toUnsignedInteger(fao);
320 	}
321 	cont->setFetchAtOnce(fetchatonce);
322 
323 	cont->setMaxColumnCount(0);
324 	cont->setMaxFieldLength(0);
325 
326 
327 	// build the connections that we'll route to
328 	// (this is just a convenient place to do it)
329 	linkedlist< connectstringcontainer * >	*cslist=
330 				cont->getConfig()->getConnectStringList();
331 	concount=cslist->getLength();
332 
333 	conids=new const char *[concount];
334 	cons=new sqlrconnection *[concount];
335 	beginquery=new const char *[concount];
336 	anymustbegin=false;
337 
338 	uint16_t index=0;
339 	connectstringnode	*csln=cslist->getFirst();
340 	while (index<concount) {
341 
342 		connectstringcontainer	*csc=csln->getValue();
343 
344 		conids[index]=csc->getConnectionId();
345 
346 		cons[index]=new sqlrconnection(
347 				csc->getConnectStringValue("server"),
348 				charstring::toUnsignedInteger(
349 					csc->getConnectStringValue("port")),
350 				csc->getConnectStringValue("socket"),
351 				csc->getConnectStringValue("user"),
352 				csc->getConnectStringValue("password"),
353 				0,1);
354 
355 		const char	*id=cons[index]->identify();
356 		if (!charstring::compare(id,"sap") ||
357 				!charstring::compare(id,"sybase") ||
358 				!charstring::compare(id,"freetds")) {
359 			beginquery[index]="begin tran";
360 		} else if (!charstring::compare(id,"sqlite")) {
361 			beginquery[index]="begin transaction";
362 		} else if (!charstring::compare(id,"postgresql") ||
363 				!charstring::compare(id,"router")) {
364 			beginquery[index]="begin";
365 		} else {
366 			beginquery[index]=NULL;
367 		}
368 
369 		if (beginquery[index]) {
370 			anymustbegin=true;
371 		}
372 
373 		index++;
374 		csln=csln->getNext();
375 	}
376 
377 	// load the router modules
378 	// (this is just a convenient place to do it)
379 	domnode	*routers=cont->getConfig()->getRouters();
380 	if (!routers->isNullNode()) {
381 		sqlrr=new sqlrrouters(cont,conids,cons,concount);
382 		sqlrr->load(routers);
383 		routeentiresession=sqlrr->routeEntireSession();
384 	}
385 }
386 
logIn(const char ** error,const char ** warning)387 bool routerconnection::logIn(const char **error, const char **warning) {
388 
389 	justloggedin=true;
390 
391 	while (!ping()) {
392 		snooze::macrosnooze(1);
393 	}
394 	endSession();
395 	return true;
396 }
397 
newCursor(uint16_t id)398 sqlrservercursor *routerconnection::newCursor(uint16_t id) {
399 	return (sqlrservercursor *)new routercursor(
400 					(sqlrserverconnection *)this,id);
401 }
402 
deleteCursor(sqlrservercursor * curs)403 void routerconnection::deleteCursor(sqlrservercursor *curs) {
404 	delete (routercursor *)curs;
405 }
406 
logOut()407 void routerconnection::logOut() {
408 }
409 
autoCommitOn()410 bool routerconnection::autoCommitOn() {
411 
412 	if (debug) {
413 		stdoutput.printf("autoCommitOn {\n");
414 	}
415 
416 	if (justloggedin) {
417 		justloggedin=false;
418 	}
419 
420 	// route
421 	bool	routed=false;
422 	bool	err=false;
423 	route(&routed,&err);
424 	if (err) {
425 		if (debug) {
426 			stdoutput.printf("	routing error\n}\n");
427 		}
428 		return false;
429 	}
430 
431 	// if routing entire sessions, then just enable for
432 	// the appropriate connection
433 	if (routed && routeentiresession) {
434 		if (debug) {
435 			stdoutput.printf("	only executing on: %s\n}\n",
436 				(currentcon)?conids[currentconindex]:NULL);
437 		}
438 		return (currentcon)?currentcon->autoCommitOn():true;
439 	}
440 
441 	// otherwise, turn autocommit on for all connections,
442 	// if any fail, return failure
443 	bool	result=true;
444 	for (uint16_t index=0; index<concount; index++) {
445 
446 		if (debug) {
447 			stdoutput.printf("	executing on: %s\n",
448 							conids[index]);
449 		}
450 
451 		bool	res=cons[index]->autoCommitOn();
452 		if (!res) {
453 			if (debug) {
454 				stdoutput.printf("	failed\n");
455 			}
456 			autoCommitOnFailed(index);
457 		}
458 		// The connection class calls autoCommitOn or autoCommitOff
459 		// immediately after logging in, which will cause the
460 		// cons to connect to the relay's and tie them up unless we
461 		// call endSession.  We'd rather not tie them up until a
462 		// client connects, so if we just logged in, we'll call
463 		// endSession.
464 		if (justloggedin) {
465 			// if any of the connections must begin transactions,
466 			// then those connections will start off in auto-commit
467 			// mode no matter what, so put all connections in
468 			// autocommit mode
469 			// (this is a convenient place to do this...)
470 			if (anymustbegin) {
471 				cons[index]->autoCommitOn();
472 			}
473 			cons[index]->endSession();
474 		}
475 		if (result) {
476 			result=res;
477 		}
478 	}
479 
480 	if (debug) {
481 		stdoutput.printf("}\n");
482 	}
483 	return result;
484 }
485 
autoCommitOff()486 bool routerconnection::autoCommitOff() {
487 
488 	if (debug) {
489 		stdoutput.printf("autoCommitOff {\n");
490 	}
491 
492 	if (justloggedin) {
493 		justloggedin=false;
494 	}
495 
496 	// route
497 	bool	routed=false;
498 	bool	err=false;
499 	route(&routed,&err);
500 	if (err) {
501 		if (debug) {
502 			stdoutput.printf("	routing error\n}\n");
503 		}
504 		return false;
505 	}
506 
507 	// if routing entire sessions, then just disable for
508 	// the appropriate connection
509 	if (routed && routeentiresession) {
510 		if (debug) {
511 			stdoutput.printf("	only executing on: %s\n}\n",
512 				(currentcon)?conids[currentconindex]:NULL);
513 		}
514 		return (currentcon)?currentcon->autoCommitOff():true;
515 	}
516 
517 	// otherwise, turn autocommit on for all connections,
518 	// if any fail, return failure
519 	bool	result=true;
520 	for (uint16_t index=0; index<concount; index++) {
521 
522 		if (debug) {
523 			stdoutput.printf("	executing on: %s\n",
524 							conids[index]);
525 		}
526 
527 		bool	res=cons[index]->autoCommitOff();
528 		if (!res) {
529 			if (debug) {
530 				stdoutput.printf("	failed\n");
531 			}
532 			autoCommitOffFailed(index);
533 		}
534 		// The connection class calls autoCommitOn or autoCommitOff
535 		// immediately after logging in, which will cause the
536 		// cons to connect to the relay's and tie them up unless we
537 		// call endSession.  We'd rather not tie them up until a
538 		// client connects, so if we just logged in, we'll call
539 		// endSession.
540 		if (justloggedin) {
541 			// if any of the connections must begin transactions,
542 			// then those connections will start off in auto-commit
543 			// mode no matter what, so put all connections in
544 			// autocommit mode, even if autocommit-off is called
545 			// here
546 			// (this is a convenient place to do this...)
547 			if (anymustbegin) {
548 				cons[index]->autoCommitOn();
549 			}
550 			cons[index]->endSession();
551 		}
552 		if (result) {
553 			result=res;
554 		}
555 	}
556 
557 	if (debug) {
558 		stdoutput.printf("}\n");
559 	}
560 	return result;
561 }
562 
supportsAutoCommit()563 bool routerconnection::supportsAutoCommit() {
564 	return true;
565 }
566 
begin()567 bool routerconnection::begin() {
568 
569 	if (debug) {
570 		stdoutput.printf("begin {\n");
571 	}
572 
573 	// route
574 	bool	routed=false;
575 	bool	err=false;
576 	route(&routed,&err);
577 	if (err) {
578 		if (debug) {
579 			stdoutput.printf("	routing error\n}\n");
580 		}
581 		return false;
582 	}
583 
584 	// if routing entire sessions, then just begin for
585 	// the appropriate connection
586 	if (routed && routeentiresession) {
587 		if (debug) {
588 			stdoutput.printf("	only executing on: %s\n}\n",
589 				(currentcon)?conids[currentconindex]:NULL);
590 		}
591 		return (currentcon)?currentcon->begin():true;
592 	}
593 
594 	// otherwise, begin all connections, if any fail, return failure
595 	bool	result=true;
596 	for (uint16_t index=0; index<concount; index++) {
597 
598 		if (debug) {
599 			stdoutput.printf("	executing on: %s\n",
600 							conids[index]);
601 		}
602 
603 		bool	res=cons[index]->begin();
604 		if (!res) {
605 			if (debug) {
606 				stdoutput.printf("	failed\n");
607 			}
608 			beginFailed(index);
609 		}
610 		if (result) {
611 			result=res;
612 		}
613 	}
614 
615 	if (debug) {
616 		stdoutput.printf("}\n");
617 	}
618 	return result;
619 }
620 
commit()621 bool routerconnection::commit() {
622 
623 	if (debug) {
624 		stdoutput.printf("commit {\n");
625 	}
626 
627 	// route
628 	bool	routed=false;
629 	bool	err=false;
630 	route(&routed,&err);
631 	if (err) {
632 		if (debug) {
633 			stdoutput.printf("	routing error\n}\n");
634 		}
635 		return false;
636 	}
637 
638 	// if routing entire sessions, then just commit for
639 	// the appropriate connection
640 	if (routed && routeentiresession) {
641 		if (debug) {
642 			stdoutput.printf("	only executing on: %s\n}\n",
643 				(currentcon)?conids[currentconindex]:NULL);
644 		}
645 		return (currentcon)?currentcon->commit():true;
646 	}
647 
648 	// otherwise, commit all connections, if any fail, return failure
649 	bool	result=true;
650 	for (uint16_t index=0; index<concount; index++) {
651 
652 		if (debug) {
653 			stdoutput.printf("	executing on: %s\n",
654 							conids[index]);
655 		}
656 
657 		bool	res=cons[index]->commit();
658 		if (!res) {
659 			if (debug) {
660 				stdoutput.printf("	failed\n");
661 			}
662 			commitFailed(index);
663 		}
664 		if (result) {
665 			result=res;
666 		}
667 	}
668 
669 	if (debug) {
670 		stdoutput.printf("}\n");
671 	}
672 	return result;
673 }
674 
rollback()675 bool routerconnection::rollback() {
676 
677 	if (debug) {
678 		stdoutput.printf("rollback {\n");
679 	}
680 
681 	// route
682 	bool	routed=false;
683 	bool	err=false;
684 	route(&routed,&err);
685 	if (err) {
686 		if (debug) {
687 			stdoutput.printf("	routing error\n}\n");
688 		}
689 		return false;
690 	}
691 
692 	// if routing entire sessions, then just rollback for
693 	// the appropriate connection
694 	if (routed && routeentiresession) {
695 		if (debug) {
696 			stdoutput.printf("	only executing on: %s\n}\n",
697 				(currentcon)?conids[currentconindex]:NULL);
698 		}
699 		return (currentcon)?currentcon->rollback():true;
700 	}
701 
702 	// otherwise, rollback all connections, if any fail, return failure
703 	bool	result=true;
704 	for (uint16_t index=0; index<concount; index++) {
705 
706 		if (debug) {
707 			stdoutput.printf("	executing on: %s\n",
708 							conids[index]);
709 		}
710 
711 		bool	res=cons[index]->rollback();
712 		if (!res) {
713 			if (debug) {
714 				stdoutput.printf("	failed\n");
715 			}
716 			rollbackFailed(index);
717 		}
718 		if (result) {
719 			result=res;
720 		}
721 	}
722 
723 	if (debug) {
724 		stdoutput.printf("}\n");
725 	}
726 	return result;
727 }
728 
errorMessage(char * errorbuffer,uint32_t errorbufferlength,uint32_t * errorlength,int64_t * errorcode,bool * liveconnection)729 void routerconnection::errorMessage(char *errorbuffer,
730 					uint32_t errorbufferlength,
731 					uint32_t *errorlength,
732 					int64_t *errorcode,
733 					bool *liveconnection) {
734 
735 	for (uint16_t index=0; index<concount; index++) {
736 		const char	*errormessage=cons[index]->errorMessage();
737 		if (!charstring::length(errormessage)) {
738 			*errorlength=charstring::length(errormessage);
739 			charstring::safeCopy(errorbuffer,errorbufferlength,
740 						errormessage,*errorlength);
741 			*errorcode=cons[index]->errorNumber();
742 			break;
743 		}
744 	}
745 	*liveconnection=true;
746 }
747 
identify()748 const char *routerconnection::identify() {
749 	// FIXME: maybe this should only return router if there's no currentcon
750 	return (identity)?identity:"router";
751 }
752 
dbVersion()753 const char *routerconnection::dbVersion() {
754 
755 	if (debug) {
756 		stdoutput.printf("dbVersion {\n");
757 	}
758 
759 	// route
760 	bool	routed=false;
761 	bool	err=false;
762 	route(&routed,&err);
763 	if (err) {
764 		if (debug) {
765 			stdoutput.printf("	routing error\n}\n");
766 		}
767 		return NULL;
768 	}
769 
770 	// if routing entire sessions, then get this for
771 	// the appropriate connection
772 	if (routeentiresession) {
773 		if (debug) {
774 			stdoutput.printf("	only executing on: %s\n}\n",
775 				(currentcon)?conids[currentconindex]:NULL);
776 		}
777 		return (currentcon)?currentcon->dbVersion():NULL;
778 	}
779 
780 	// otherwise, try to find a usable connection
781 	if (!currentcon) {
782 		for (uint16_t index=0; !currentcon && index<concount; index++) {
783 			currentcon=cons[index];
784 			currentconindex=index;
785 		}
786 	}
787 
788 	if (debug) {
789 		stdoutput.printf("	executing on: %s\n",
790 			(currentcon)?conids[currentconindex]:NULL);
791 	}
792 
793 	const char	*retval=(currentcon)?currentcon->dbVersion():NULL;
794 	if (debug) {
795 		stdoutput.printf("	db version: %s\n}\n",retval);
796 	}
797 	return retval;
798 }
799 
dbHostName()800 const char *routerconnection::dbHostName() {
801 
802 	if (debug) {
803 		stdoutput.printf("dbHostName {\n");
804 	}
805 
806 	// route
807 	bool	routed=false;
808 	bool	err=false;
809 	route(&routed,&err);
810 	if (err) {
811 		if (debug) {
812 			stdoutput.printf("	routing error\n}\n");
813 		}
814 		return NULL;
815 	}
816 
817 	// if routing entire sessions, then get this for
818 	// the appropriate connection
819 	if (routeentiresession) {
820 		if (debug) {
821 			stdoutput.printf("	only executing on: %s\n}\n",
822 				(currentcon)?conids[currentconindex]:NULL);
823 		}
824 		return (currentcon)?currentcon->dbHostName():NULL;
825 	}
826 
827 	// otherwise, try to find a usable connection
828 	if (!currentcon) {
829 		for (uint16_t index=0; !currentcon && index<concount; index++) {
830 			currentcon=cons[index];
831 			currentconindex=index;
832 		}
833 	}
834 
835 	if (debug) {
836 		stdoutput.printf("	executing on: %s\n",
837 			(currentcon)?conids[currentconindex]:NULL);
838 	}
839 
840 	const char	*retval=(currentcon)?currentcon->dbHostName():NULL;
841 	if (debug) {
842 		stdoutput.printf("	db hostname: %s\n}\n",retval);
843 	}
844 	return retval;
845 }
846 
dbIpAddress()847 const char *routerconnection::dbIpAddress() {
848 
849 	if (debug) {
850 		stdoutput.printf("dbIpAddress {\n");
851 	}
852 
853 	// route
854 	bool	routed=false;
855 	bool	err=false;
856 	route(&routed,&err);
857 	if (err) {
858 		if (debug) {
859 			stdoutput.printf("	routing error\n}\n");
860 		}
861 		return NULL;
862 	}
863 
864 	// if routing entire sessions, then get this for
865 	// the appropriate connection
866 	if (routeentiresession) {
867 		if (debug) {
868 			stdoutput.printf("	only executing on: %s\n}\n",
869 				(currentcon)?conids[currentconindex]:NULL);
870 		}
871 		return (currentcon)?currentcon->dbIpAddress():NULL;
872 	}
873 
874 	// otherwise, try to find a usable connection
875 	if (!currentcon) {
876 		for (uint16_t index=0; !currentcon && index<concount; index++) {
877 			currentcon=cons[index];
878 			currentconindex=index;
879 		}
880 	}
881 
882 	if (debug) {
883 		stdoutput.printf("	executing on: %s\n",
884 			(currentcon)?conids[currentconindex]:NULL);
885 	}
886 
887 	const char	*retval=(currentcon)?currentcon->dbIpAddress():NULL;
888 	if (debug) {
889 		stdoutput.printf("	db ip address: %s\n}\n",retval);
890 	}
891 	return retval;
892 }
893 
cacheDbHostInfo()894 bool  routerconnection::cacheDbHostInfo() {
895 	return false;
896 }
897 
getListsByApiCalls()898 bool routerconnection::getListsByApiCalls() {
899 	return true;
900 }
901 
getDatabaseList(sqlrservercursor * cursor,const char * wild)902 bool routerconnection::getDatabaseList(sqlrservercursor *cursor,
903 						const char *wild) {
904 	// FIXME: implement this
905 	cont->setError(cursor,SQLR_ERROR_NOTIMPLEMENTED_STRING,
906 				SQLR_ERROR_NOTIMPLEMENTED,true);
907 	return false;
908 }
909 
getTableList(sqlrservercursor * cursor,const char * wild,uint16_t objecttypes)910 bool routerconnection::getTableList(sqlrservercursor *cursor,
911 						const char *wild,
912 						uint16_t objecttypes) {
913 	// FIXME: implement this
914 	cont->setError(cursor,SQLR_ERROR_NOTIMPLEMENTED_STRING,
915 				SQLR_ERROR_NOTIMPLEMENTED,true);
916 	return false;
917 }
918 
getColumnList(sqlrservercursor * cursor,const char * table,const char * wild)919 bool routerconnection::getColumnList(sqlrservercursor *cursor,
920 						const char *table,
921 						const char *wild) {
922 	// FIXME: implement this
923 	cont->setError(cursor,SQLR_ERROR_NOTIMPLEMENTED_STRING,
924 				SQLR_ERROR_NOTIMPLEMENTED,true);
925 	return false;
926 }
927 
ping()928 bool routerconnection::ping() {
929 
930 	if (debug) {
931 		stdoutput.printf("ping {\n");
932 	}
933 
934 	// route
935 	bool	routed=false;
936 	bool	err=false;
937 	route(&routed,&err);
938 	if (err) {
939 		if (debug) {
940 			stdoutput.printf("	routing error\n}\n");
941 		}
942 		return false;
943 	}
944 
945 	// if routing entire sessions, then ping the appropriate connection
946 	if (routed && routeentiresession) {
947 		if (debug) {
948 			stdoutput.printf("	only executing on: %s\n}\n",
949 				(currentcon)?conids[currentconindex]:NULL);
950 		}
951 		return (currentcon)?currentcon->ping():true;
952 	}
953 
954 	// ping all connections, if any fail, return failure
955 	bool	result=true;
956 	for (uint16_t index=0; index<concount; index++) {
957 
958 		if (debug) {
959 			stdoutput.printf("	executing on: %s\n",
960 							conids[index]);
961 		}
962 
963 		bool	res=cons[index]->ping();
964 		if (!res) {
965 			if (debug) {
966 				stdoutput.printf("	failed\n");
967 			}
968 			result=res;
969 		}
970 	}
971 
972 	if (debug) {
973 		stdoutput.printf("}\n");
974 	}
975 	return result;
976 }
977 
selectDatabaseQuery()978 const char *routerconnection::selectDatabaseQuery() {
979 	return "use %s";
980 }
981 
getCurrentDatabase()982 char *routerconnection::getCurrentDatabase() {
983 	return (currentcon)?
984 		charstring::duplicate(currentcon->getCurrentDatabase()):NULL;
985 }
986 
getLastInsertId(uint64_t * id)987 bool routerconnection::getLastInsertId(uint64_t *id) {
988 	// get this from the most recently used connection
989 	if (!currentcon) {
990 		*id=0;
991 		return true;
992 	}
993 	*id=currentcon->getLastInsertId();
994 	return (*id!=0);
995 }
996 
endSession()997 void routerconnection::endSession() {
998 
999 	if (debug) {
1000 		stdoutput.printf("endSession {\n");
1001 	}
1002 
1003 	// route
1004 	bool	routed=false;
1005 	bool	err=false;
1006 	route(&routed,&err);
1007 	if (err) {
1008 		if (debug) {
1009 			stdoutput.printf("	routing error\n}\n");
1010 		}
1011 		return;
1012 	}
1013 
1014 	if (routed && routeentiresession) {
1015 
1016 		// if routing entire sessions, then end-session
1017 		// on the appropriate connection
1018 		if (debug) {
1019 			stdoutput.printf("	only executing on: %s\n}\n",
1020 				(currentcon)?conids[currentconindex]:NULL);
1021 		}
1022 		currentcon->endSession();
1023 
1024 	} else {
1025 
1026 		// otherwise end-session on all connections
1027 		for (uint16_t index=0; index<concount; index++) {
1028 			if (debug) {
1029 				stdoutput.printf("	executing on: %s\n",
1030 								conids[index]);
1031 			}
1032 			cons[index]->endSession();
1033 		}
1034 	}
1035 
1036 	// reset pointers and index
1037 	currentcon=NULL;
1038 	currentconindex=0;
1039 	for (linkedlistnode< routercursor * > *node=routercursors.getFirst();
1040 						node; node=node->getNext()) {
1041 		routercursor	*rcur=node->getValue();
1042 		rcur->currentcon=NULL;
1043 		rcur->currentcur=NULL;
1044 	}
1045 	sqlrr->setCurrentConnectionId(NULL);
1046 
1047 	if (debug) {
1048 		stdoutput.printf("}\n");
1049 	}
1050 }
1051 
route(bool * routed,bool * err)1052 void routerconnection::route(bool *routed, bool *err) {
1053 
1054 	if (debug) {
1055 		stdoutput.printf("	route (connection) {\n");
1056 	}
1057 
1058 	// initialize return values
1059 	*err=false;
1060 	*routed=false;
1061 
1062 	// bail if we're routing the entire session
1063 	// and we already have a currentcon
1064 	if (routeentiresession && currentcon) {
1065 		if (debug) {
1066 			stdoutput.printf("		"
1067 					"routing entire session and "
1068 					"have currentcon\n	}\n");
1069 		}
1070 		return;
1071 	}
1072 
1073 	// otherwise, sort ourselves out...
1074 
1075 	// reset pointers and index
1076 	currentcon=NULL;
1077 	currentconindex=0;
1078 
1079 	// route...
1080 	const char	*errm=NULL;
1081 	int64_t		errn=0;
1082 	const char	*connectionid=sqlrr->route(this,NULL,&errm,&errn);
1083 	if (!connectionid) {
1084 		if (debug) {
1085 			stdoutput.printf("		"
1086 					"no connection id returned\n");
1087 		}
1088 		if (errm) {
1089 			if (debug) {
1090 				stdoutput.printf("		"
1091 						"an error occurred: "
1092 						"%d - %s\n",errn,errm);
1093 			}
1094 			cont->setError(errm,errn,true);
1095 			*err=true;
1096 		}
1097 		if (debug) {
1098 			stdoutput.printf("	}\n");
1099 		}
1100 		return;
1101 	}
1102 	if (debug) {
1103 		stdoutput.printf("		routing to: %s\n",connectionid);
1104 	}
1105 
1106 	// get the corresponding connection
1107 	for (uint16_t i=0; i<concount; i++) {
1108 		if (!charstring::compare(connectionid,conids[i])) {
1109 			currentcon=cons[i];
1110 			currentconindex=i;
1111 			sqlrr->setCurrentConnectionId(connectionid);
1112 			*routed=true;
1113 			if (debug) {
1114 				stdoutput.printf("	}\n");
1115 			}
1116 			return;
1117 		}
1118 	}
1119 
1120 	if (debug) {
1121 		stdoutput.printf("		"
1122 				"%s not found\n	}\n",connectionid);
1123 	}
1124 }
1125 
autoCommitOnFailed(uint16_t index)1126 void routerconnection::autoCommitOnFailed(uint16_t index) {
1127 	raiseIntegrityViolationEvent("autocommit-on",index);
1128 }
1129 
autoCommitOffFailed(uint16_t index)1130 void routerconnection::autoCommitOffFailed(uint16_t index) {
1131 	raiseIntegrityViolationEvent("autocommit-off",index);
1132 }
1133 
beginFailed(uint16_t index)1134 void routerconnection::beginFailed(uint16_t index) {
1135 	raiseIntegrityViolationEvent("begin",index);
1136 }
1137 
commitFailed(uint16_t index)1138 void routerconnection::commitFailed(uint16_t index) {
1139 	raiseIntegrityViolationEvent("commit",index);
1140 }
1141 
rollbackFailed(uint16_t index)1142 void routerconnection::rollbackFailed(uint16_t index) {
1143 	raiseIntegrityViolationEvent("rollback",index);
1144 }
1145 
beginQueryFailed(uint16_t index)1146 void routerconnection::beginQueryFailed(uint16_t index) {
1147 	raiseIntegrityViolationEvent("begin",index);
1148 }
1149 
raiseIntegrityViolationEvent(const char * command,uint16_t index)1150 void routerconnection::raiseIntegrityViolationEvent(const char *command,
1151 							uint16_t index) {
1152 	stringbuffer	info;
1153 	info.append(command);
1154 	info.append(" failed on connectionid: ");
1155 	info.append(conids[index]);
1156 	cont->raiseIntegrityViolationEvent(info.getString());
1157 
1158 	cont->setInstanceDisabled(true);
1159 }
1160 
1161 
routercursor(sqlrserverconnection * conn,uint16_t id)1162 routercursor::routercursor(sqlrserverconnection *conn, uint16_t id) :
1163 						sqlrservercursor(conn,id) {
1164 	routerconn=(routerconnection *)conn;
1165 	nextrow=0;
1166 	currentcon=NULL;
1167 	currentcur=NULL;
1168 	isbindcur=false;
1169 	curs=new sqlrcursor *[routerconn->concount];
1170 	for (uint16_t index=0; index<routerconn->concount; index++) {
1171 		curs[index]=NULL;
1172 		if (!routerconn->cons[index]) {
1173 			continue;
1174 		}
1175 		curs[index]=new sqlrcursor(routerconn->cons[index]);
1176 		curs[index]->setResultSetBufferSize(
1177 					conn->cont->getFetchAtOnce());
1178 	}
1179 
1180 	obv=new outputbindvar[conn->cont->getConfig()->getMaxBindCount()];
1181 	obcount=0;
1182 
1183 	cbv=new cursorbindvar[conn->cont->getConfig()->getMaxBindCount()];
1184 	cbcount=0;
1185 
1186 	emptyquery=false;
1187 
1188 	routerconn->routercursors.append(this);
1189 }
1190 
~routercursor()1191 routercursor::~routercursor() {
1192 	for (uint16_t index=0; index<routerconn->concount; index++) {
1193 		delete curs[index];
1194 	}
1195 	delete[] curs;
1196 	delete[] obv;
1197 	delete[] cbv;
1198 	routerconn->routercursors.remove(this);
1199 }
1200 
prepareQuery(const char * query,uint32_t length)1201 bool routercursor::prepareQuery(const char *query, uint32_t length) {
1202 
1203 	if (routerconn->debug) {
1204 		stdoutput.printf("prepareQuery {\n");
1205 	}
1206 
1207 	// FIXME: remove this and use a translation
1208 
1209 	// convert to lowercase and normalize whitespace, for regex matching
1210 	char	*nquery=new char[length+1];
1211 	if (query) {
1212 		for (uint32_t i=0; i<length; ++i) {
1213 			char	c=query[i];
1214 			if (character::isWhitespace(c)) {
1215 				nquery[i]=' ';
1216 			} else {
1217 				nquery[i]=character::toLowerCase(c);
1218 			}
1219 		}
1220 	}
1221 	nquery[length]='\0';
1222 
1223 	// reset bind cursor
1224 	if (isbindcur) {
1225 		delete currentcur;
1226 		currentcur=NULL;
1227 		isbindcur=false;
1228 	}
1229 
1230 	// initialize the output bind count
1231 	obcount=0;
1232 
1233 	// initialize the cursor bind count
1234 	cbcount=0;
1235 
1236 	// initialize the empty query flag
1237 	emptyquery=false;
1238 
1239 	// route
1240 	bool	routed=false;
1241 	bool	err=false;
1242 	route(&routed,&err);
1243 	if (err) {
1244 		if (routerconn->debug) {
1245 			stdoutput.printf("	routing error\n}\n");
1246 		}
1247 		return false;
1248 	}
1249 
1250 	// free normalized query
1251 	delete[] nquery;
1252 
1253 	// currentcur could be NULL here if no
1254 	// connection could be found to run the query.
1255 	if (!currentcur) {
1256 		if (routerconn->debug) {
1257 			stdoutput.printf("	no connection "
1258 					"found, bailing\n}\n");
1259 		}
1260 		return false;
1261 	}
1262 
1263 	// Did a module make the query empty?  If so, then we won't actually
1264 	// prepare/execute it, just return true.  The usedatabase module does
1265 	// this.
1266 	emptyquery=!getQueryLength();
1267 	if (routerconn->debug) {
1268 		stdoutput.printf("%s",(emptyquery)?"	query set empty\n":"");
1269 	}
1270 
1271 	// prepare the query using the cursor from whichever
1272 	// connection turned out to be the right one
1273 	if (!emptyquery) {
1274 		if (routerconn->debug) {
1275 			stdoutput.printf("	query: %.*s\n",length,query);
1276 		}
1277 		currentcur->prepareQuery(query,length);
1278 	}
1279 
1280 	if (routerconn->debug) {
1281 		stdoutput.printf("}\n");
1282 	}
1283 	return true;
1284 }
1285 
route(bool * routed,bool * err)1286 void routercursor::route(bool *routed, bool *err) {
1287 
1288 	if (routerconn->debug) {
1289 		stdoutput.printf("	route (cursor) {\n");
1290 	}
1291 
1292 	// initialize return values
1293 	*err=false;
1294 	*routed=false;
1295 
1296 	// if we're routing the entire session and this particular routercursor
1297 	// hasn't sorted itself out, but the routerconnection has, then get
1298 	// which connection and cursor to use from the routerconnection
1299 	if (routerconn->routeentiresession) {
1300 
1301 		if (routerconn->debug) {
1302 			stdoutput.printf("		"
1303 					"routing entire session ");
1304 		}
1305 		if (currentcon) {
1306 			if (routerconn->debug) {
1307 				stdoutput.printf("and have currentcon\n	}\n");
1308 			}
1309 			return;
1310 		} else if (routerconn->currentcon) {
1311 			if (routerconn->debug) {
1312 				stdoutput.printf("and conn has "
1313 						"currentcon\n	}\n");
1314 			}
1315 			currentcon=routerconn->currentcon;
1316 			currentcur=curs[routerconn->currentconindex];
1317 			return;
1318 		}
1319 		if (routerconn->debug) {
1320 			stdoutput.printf("but need to get currentcon\n");
1321 		}
1322 	}
1323 
1324 	// otherwise, sort this routercursor out...
1325 
1326 	// reset pointers and index
1327 	currentcon=NULL;
1328 	currentcur=NULL;
1329 	routerconn->currentcon=NULL;
1330 	routerconn->currentconindex=0;
1331 
1332 	// route...
1333 	const char	*errm=NULL;
1334 	int64_t		errn=0;
1335 	const char	*connectionid=routerconn->sqlrr->route(
1336 						routerconn,this,&errm,&errn);
1337 	if (!connectionid) {
1338 		if (routerconn->debug) {
1339 			stdoutput.printf("		"
1340 					"no connection id returned\n");
1341 		}
1342 		if (errm) {
1343 			if (routerconn->debug) {
1344 				stdoutput.printf("		"
1345 						"an error occurred: "
1346 						"%d - %s\n",errn,errm);
1347 			}
1348 			conn->cont->setError(this,errm,errn,true);
1349 			*err=true;
1350 		}
1351 		if (routerconn->debug) {
1352 			stdoutput.printf("	}\n");
1353 		}
1354 		return;
1355 	}
1356 	if (routerconn->debug) {
1357 		stdoutput.printf("		routing to: %s\n",connectionid);
1358 	}
1359 
1360 	// get the corresponding connection and cursor
1361 	for (uint16_t i=0; i<routerconn->concount; i++) {
1362 		if (!charstring::compare(connectionid,routerconn->conids[i])) {
1363 			currentcon=routerconn->cons[i];
1364 			currentcur=curs[i];
1365 			routerconn->currentcon=currentcon;
1366 			routerconn->currentconindex=i;
1367 			routerconn->sqlrr->setCurrentConnectionId(connectionid);
1368 			*routed=true;
1369 			if (routerconn->debug) {
1370 				stdoutput.printf("	}\n");
1371 			}
1372 			return;
1373 		}
1374 	}
1375 
1376 	if (routerconn->debug) {
1377 		stdoutput.printf("		"
1378 				"%s not found\n	}\n",connectionid);
1379 	}
1380 }
1381 
supportsNativeBinds(const char * query,uint32_t length)1382 bool routercursor::supportsNativeBinds(const char *query, uint32_t length) {
1383 	return true;
1384 }
1385 
inputBind(const char * variable,uint16_t variablesize,const char * value,uint32_t valuesize,int16_t * isnull)1386 bool routercursor::inputBind(const char *variable,
1387 				uint16_t variablesize,
1388 				const char *value,
1389 				uint32_t valuesize,
1390 				int16_t *isnull) {
1391 	currentcur->inputBind(variable+1,value);
1392 	return true;
1393 }
1394 
inputBind(const char * variable,uint16_t variablesize,int64_t * value)1395 bool routercursor::inputBind(const char *variable,
1396 				uint16_t variablesize,
1397 				int64_t *value) {
1398 	currentcur->inputBind(variable+1,*value);
1399 	return true;
1400 }
1401 
inputBind(const char * variable,uint16_t variablesize,double * value,uint32_t precision,uint32_t scale)1402 bool routercursor::inputBind(const char *variable,
1403 				uint16_t variablesize,
1404 				double *value,
1405 				uint32_t precision,
1406 				uint32_t scale) {
1407 	currentcur->inputBind(variable+1,*value,precision,scale);
1408 	return true;
1409 }
1410 
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)1411 bool routercursor::inputBind(const char *variable,
1412 				uint16_t variablesize,
1413 				int64_t year,
1414 				int16_t month,
1415 				int16_t day,
1416 				int16_t hour,
1417 				int16_t minute,
1418 				int16_t second,
1419 				int32_t microsecond,
1420 				const char *tz,
1421 				bool isnegative,
1422 				char *buffer,
1423 				uint16_t buffersize,
1424 				int16_t *isnull) {
1425 	currentcur->inputBind(variable+1,year,month,day,
1426 			hour,minute,second,microsecond,tz,isnegative);
1427 	return true;
1428 }
1429 
inputBindBlob(const char * variable,uint16_t variablesize,const char * value,uint32_t valuesize,int16_t * isnull)1430 bool routercursor::inputBindBlob(const char *variable,
1431 					uint16_t variablesize,
1432 					const char *value,
1433 					uint32_t valuesize,
1434 					int16_t *isnull) {
1435 	currentcur->inputBindBlob(variable+1,value,valuesize);
1436 	return true;
1437 }
1438 
inputBindClob(const char * variable,uint16_t variablesize,const char * value,uint32_t valuesize,int16_t * isnull)1439 bool routercursor::inputBindClob(const char *variable,
1440 					uint16_t variablesize,
1441 					const char *value,
1442 					uint32_t valuesize,
1443 					int16_t *isnull) {
1444 	currentcur->inputBindClob(variable+1,value,valuesize);
1445 	return true;
1446 }
1447 
outputBind(const char * variable,uint16_t variablesize,char * value,uint32_t valuesize,int16_t * isnull)1448 bool routercursor::outputBind(const char *variable,
1449 				uint16_t variablesize,
1450 				char *value,
1451 				uint32_t valuesize,
1452 				int16_t *isnull) {
1453 	currentcur->defineOutputBindString(variable+1,valuesize);
1454 	obv[obcount].variable=variable+1;
1455 	obv[obcount].type=SQLRSERVERBINDVARTYPE_STRING;
1456 	obv[obcount].value.stringvalue=value;
1457 	obv[obcount].valuesize=valuesize;
1458 	obv[obcount].isnull=isnull;
1459 	obcount++;
1460 	return true;
1461 }
1462 
outputBind(const char * variable,uint16_t variablesize,int64_t * value,int16_t * isnull)1463 bool routercursor::outputBind(const char *variable,
1464 				uint16_t variablesize,
1465 				int64_t *value,
1466 				int16_t *isnull) {
1467 	currentcur->defineOutputBindInteger(variable+1);
1468 	obv[obcount].variable=variable+1;
1469 	obv[obcount].type=SQLRSERVERBINDVARTYPE_INTEGER;
1470 	obv[obcount].value.intvalue=value;
1471 	obv[obcount].isnull=isnull;
1472 	obcount++;
1473 	return true;
1474 }
1475 
outputBind(const char * variable,uint16_t variablesize,double * value,uint32_t * precision,uint32_t * scale,int16_t * isnull)1476 bool routercursor::outputBind(const char *variable,
1477 				uint16_t variablesize,
1478 				double *value,
1479 				uint32_t *precision,
1480 				uint32_t *scale,
1481 				int16_t *isnull) {
1482 	currentcur->defineOutputBindDouble(variable+1);
1483 	obv[obcount].variable=variable+1;
1484 	obv[obcount].type=SQLRSERVERBINDVARTYPE_DOUBLE;
1485 	obv[obcount].value.doublevalue=value;
1486 	obv[obcount].isnull=isnull;
1487 	obcount++;
1488 	return true;
1489 }
1490 
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)1491 bool routercursor::outputBind(const char *variable,
1492 				uint16_t variablesize,
1493 				int16_t *year,
1494 				int16_t *month,
1495 				int16_t *day,
1496 				int16_t *hour,
1497 				int16_t *minute,
1498 				int16_t *second,
1499 				int32_t *microsecond,
1500 				const char **tz,
1501 				bool *isnegative,
1502 				char *buffer,
1503 				uint16_t buffersize,
1504 				int16_t *isnull) {
1505 	currentcur->defineOutputBindDouble(variable+1);
1506 	obv[obcount].variable=variable+1;
1507 	obv[obcount].type=SQLRSERVERBINDVARTYPE_DATE;
1508 	obv[obcount].value.datevalue.year=year;
1509 	obv[obcount].value.datevalue.month=month;
1510 	obv[obcount].value.datevalue.day=day;
1511 	obv[obcount].value.datevalue.hour=hour;
1512 	obv[obcount].value.datevalue.minute=minute;
1513 	obv[obcount].value.datevalue.second=second;
1514 	obv[obcount].value.datevalue.microsecond=microsecond;
1515 	obv[obcount].value.datevalue.tz=tz;
1516 	obv[obcount].isnull=isnull;
1517 	obv[obcount].value.datevalue.isnegative=isnegative;
1518 	obcount++;
1519 	return true;
1520 }
1521 
1522 
outputBindBlob(const char * variable,uint16_t variablesize,uint16_t index,int16_t * isnull)1523 bool routercursor::outputBindBlob(const char *variable,
1524 					uint16_t variablesize,
1525 					uint16_t index,
1526 					int16_t *isnull) {
1527 	currentcur->defineOutputBindBlob(variable+1);
1528 	obv[obcount].variable=variable+1;
1529 	obv[obcount].type=SQLRSERVERBINDVARTYPE_BLOB;
1530 	obv[obcount].isnull=isnull;
1531 	obcount++;
1532 	return true;
1533 }
1534 
outputBindClob(const char * variable,uint16_t variablesize,uint16_t index,int16_t * isnull)1535 bool routercursor::outputBindClob(const char *variable,
1536 					uint16_t variablesize,
1537 					uint16_t index,
1538 					int16_t *isnull) {
1539 	currentcur->defineOutputBindClob(variable+1);
1540 	obv[obcount].variable=variable+1;
1541 	obv[obcount].type=SQLRSERVERBINDVARTYPE_CLOB;
1542 	obv[obcount].isnull=isnull;
1543 	obcount++;
1544 	return true;
1545 }
1546 
outputBindCursor(const char * variable,uint16_t variablesize,sqlrservercursor * cursor)1547 bool routercursor::outputBindCursor(const char *variable,
1548 					uint16_t variablesize,
1549 					sqlrservercursor *cursor) {
1550 	currentcur->defineOutputBindCursor(variable+1);
1551 	cbv[cbcount].variable=variable+1;
1552 	cbv[cbcount].cursor=cursor;
1553 	cbcount++;
1554 	return true;
1555 }
1556 
getLobOutputBindLength(uint16_t index,uint64_t * length)1557 bool routercursor::getLobOutputBindLength(uint16_t index, uint64_t *length) {
1558 	*length=currentcur->getOutputBindLength(obv[index].variable);
1559 	return true;
1560 }
1561 
getLobOutputBindSegment(uint16_t index,char * buffer,uint64_t buffersize,uint64_t offset,uint64_t charstoread,uint64_t * charsread)1562 bool routercursor::getLobOutputBindSegment(uint16_t index,
1563 					char *buffer, uint64_t buffersize,
1564 					uint64_t offset, uint64_t charstoread,
1565 					uint64_t *charsread) {
1566 	const char	*varname=obv[index].variable;
1567 	const char	*var=currentcur->getOutputBindClob(varname);
1568 	if (!var) {
1569 		var=currentcur->getOutputBindBlob(varname);
1570 	}
1571 	uint32_t	length=currentcur->getOutputBindLength(varname);
1572 	if (offset+charstoread>length) {
1573 		charstoread=length-offset;
1574 	}
1575 	bytestring::copy(buffer,var,charstoread);
1576 	*charsread=charstoread;
1577 	return true;
1578 }
1579 
executeQuery(const char * query,uint32_t length)1580 bool routercursor::executeQuery(const char *query, uint32_t length) {
1581 
1582 	// FIXME: if routing entire sessions, then compare and just do this for
1583 	// the appropriate connection
1584 
1585 	if (!currentcur) {
1586 		if (!prepareQuery(query,length)) {
1587 			return false;
1588 		}
1589 	}
1590 
1591 	if (!currentcur) {
1592 		return false;
1593 	}
1594 
1595 	if (!emptyquery) {
1596 		if (!currentcur->executeQuery()) {
1597 			return false;
1598 		}
1599 	}
1600 
1601 	nextrow=0;
1602 
1603 	// populate output bind values
1604 	for (uint16_t outi=0; outi<obcount; outi++) {
1605 		const char	*variable=obv[outi].variable;
1606 		*(obv[outi].isnull)=routerconn->nonnullbindvalue;
1607 		if (obv[outi].type==SQLRSERVERBINDVARTYPE_STRING) {
1608 			const char	*str=
1609 				currentcur->getOutputBindString(variable);
1610 			uint32_t	len=
1611 				currentcur->getOutputBindLength(variable);
1612 			if (str) {
1613 				charstring::copy(obv[outi].value.stringvalue,
1614 								str,len);
1615 			} else {
1616 				obv[outi].value.stringvalue[0]='\0';
1617 				*(obv[outi].isnull)=routerconn->nullbindvalue;
1618 			}
1619 		} else if (obv[outi].type==SQLRSERVERBINDVARTYPE_INTEGER) {
1620 			*(obv[outi].value.intvalue)=
1621 				currentcur->getOutputBindInteger(variable);
1622 		} else if (obv[outi].type==SQLRSERVERBINDVARTYPE_DOUBLE) {
1623 			*(obv[outi].value.doublevalue)=
1624 				currentcur->getOutputBindDouble(variable);
1625 		} else if (obv[outi].type==SQLRSERVERBINDVARTYPE_DATE) {
1626 			currentcur->getOutputBindDate(variable,
1627 					obv[outi].value.datevalue.year,
1628 					obv[outi].value.datevalue.month,
1629 					obv[outi].value.datevalue.day,
1630 					obv[outi].value.datevalue.hour,
1631 					obv[outi].value.datevalue.minute,
1632 					obv[outi].value.datevalue.second,
1633 					obv[outi].value.datevalue.microsecond,
1634 					obv[outi].value.datevalue.tz,
1635 					obv[outi].value.datevalue.isnegative);
1636 		}
1637 	}
1638 
1639 	// handle cursor bind values
1640 	for (uint16_t curi=0; curi<cbcount; curi++) {
1641 		routercursor	*rcur=(routercursor *)cbv[curi].cursor;
1642 		rcur->currentcon=currentcon;
1643 		rcur->currentcur=
1644 			currentcur->getOutputBindCursor(cbv[curi].variable);
1645 		if (!rcur->currentcur) {
1646 			return false;
1647 		}
1648 		rcur->currentcur->setResultSetBufferSize(
1649 					conn->cont->getFetchAtOnce());
1650 		rcur->isbindcur=true;
1651 		rcur->nextrow=0;
1652 		if (!rcur->currentcur->fetchFromBindCursor()) {
1653 			return false;
1654 		}
1655 	}
1656 	return true;
1657 }
1658 
errorMessage(char * errorbuffer,uint32_t errorbufferlength,uint32_t * errorlength,int64_t * errorcode,bool * liveconnection)1659 void routercursor::errorMessage(char *errorbuffer,
1660 					uint32_t errorbufferlength,
1661 					uint32_t *errorlength,
1662 					int64_t *errorcode,
1663 					bool *liveconnection) {
1664 	const char	*errormessage=
1665 			(currentcur)?currentcur->errorMessage():"";
1666 	*errorlength=charstring::length(errormessage);
1667 	charstring::safeCopy(errorbuffer,errorbufferlength,
1668 					errormessage,*errorlength);
1669 	*errorcode=(currentcur)?currentcur->errorNumber():0;
1670 	*liveconnection=true;
1671 }
1672 
knowsRowCount()1673 bool routercursor::knowsRowCount() {
1674 	return true;
1675 }
1676 
rowCount()1677 uint64_t routercursor::rowCount() {
1678 	return (currentcur)?currentcur->rowCount():0;
1679 }
1680 
affectedRows()1681 uint64_t routercursor::affectedRows() {
1682 	return (currentcur)?currentcur->affectedRows():0;
1683 }
1684 
colCount()1685 uint32_t routercursor::colCount() {
1686 	return (currentcur)?currentcur->colCount():0;
1687 }
1688 
columnTypeFormat()1689 uint16_t routercursor::columnTypeFormat() {
1690 	return (uint16_t)COLUMN_TYPE_NAMES;
1691 }
1692 
getColumnName(uint32_t col)1693 const char *routercursor::getColumnName(uint32_t col) {
1694 	return (currentcur)?currentcur->getColumnName(col):NULL;
1695 }
1696 
getColumnTypeName(uint32_t col)1697 const char *routercursor::getColumnTypeName(uint32_t col) {
1698 	return (currentcur)?currentcur->getColumnType(col):NULL;
1699 }
1700 
getColumnLength(uint32_t col)1701 uint32_t routercursor::getColumnLength(uint32_t col) {
1702 	return (currentcur)?currentcur->getColumnLength(col):0;
1703 }
1704 
getColumnPrecision(uint32_t col)1705 uint32_t routercursor::getColumnPrecision(uint32_t col) {
1706 	return (currentcur)?currentcur->getColumnPrecision(col):0;
1707 }
1708 
getColumnScale(uint32_t col)1709 uint32_t routercursor::getColumnScale(uint32_t col) {
1710 	return (currentcur)?currentcur->getColumnScale(col):0;
1711 }
1712 
getColumnIsNullable(uint32_t col)1713 uint16_t routercursor::getColumnIsNullable(uint32_t col) {
1714 	return (currentcur)?currentcur->getColumnIsNullable(col):0;
1715 }
1716 
getColumnIsPrimaryKey(uint32_t col)1717 uint16_t routercursor::getColumnIsPrimaryKey(uint32_t col) {
1718 	return (currentcur)?currentcur->getColumnIsPrimaryKey(col):0;
1719 }
1720 
getColumnIsUnique(uint32_t col)1721 uint16_t routercursor::getColumnIsUnique(uint32_t col) {
1722 	return (currentcur)?currentcur->getColumnIsUnique(col):0;
1723 }
1724 
getColumnIsPartOfKey(uint32_t col)1725 uint16_t routercursor::getColumnIsPartOfKey(uint32_t col) {
1726 	return (currentcur)?currentcur->getColumnIsPartOfKey(col):0;
1727 }
1728 
getColumnIsUnsigned(uint32_t col)1729 uint16_t routercursor::getColumnIsUnsigned(uint32_t col) {
1730 	return (currentcur)?currentcur->getColumnIsUnsigned(col):0;
1731 }
1732 
getColumnIsZeroFilled(uint32_t col)1733 uint16_t routercursor::getColumnIsZeroFilled(uint32_t col) {
1734 	return (currentcur)?currentcur->getColumnIsZeroFilled(col):0;
1735 }
1736 
getColumnIsBinary(uint32_t col)1737 uint16_t routercursor::getColumnIsBinary(uint32_t col) {
1738 	return (currentcur)?currentcur->getColumnIsBinary(col):0;
1739 }
1740 
getColumnIsAutoIncrement(uint32_t col)1741 uint16_t routercursor::getColumnIsAutoIncrement(uint32_t col) {
1742 	return (currentcur)?currentcur->getColumnIsAutoIncrement(col):0;
1743 }
1744 
getColumnTable(uint32_t col)1745 const char *routercursor::getColumnTable(uint32_t col) {
1746 	return (currentcur)?currentcur->getColumnTable(col):NULL;
1747 }
1748 
noRowsToReturn()1749 bool routercursor::noRowsToReturn() {
1750 	return (((currentcur)?currentcur->rowCount():0)==0);
1751 }
1752 
fetchRow(bool * error)1753 bool routercursor::fetchRow(bool *error) {
1754 
1755 	*error=false;
1756 
1757 	if (!currentcur) {
1758 		return false;
1759 	}
1760 	if (currentcur->getField(nextrow,(uint32_t)0)) {
1761 		nextrow++;
1762 		return true;
1763 	}
1764 	if (currentcur->errorMessage()) {
1765 		*error=true;
1766 	}
1767 	return false;
1768 }
1769 
getField(uint32_t col,const char ** field,uint64_t * fieldlength,bool * blob,bool * null)1770 void routercursor::getField(uint32_t col,
1771 				const char **field, uint64_t *fieldlength,
1772 				bool *blob, bool *null) {
1773 	const char	*fld=currentcur->getField(nextrow-1,col);
1774 	uint32_t	len=currentcur->getFieldLength(nextrow-1,col);
1775 	if (len) {
1776 		*field=fld;
1777 		*fieldlength=len;
1778 	} else {
1779 		*null=true;
1780 	}
1781 }
1782 
closeResultSet()1783 void routercursor::closeResultSet() {
1784 	if (currentcur) {
1785 		currentcur->clearBinds();
1786 	}
1787 	obcount=0;
1788 	cbcount=0;
1789 }
1790 
1791 
1792 extern "C" {
new_routerconnection(sqlrservercontroller * cont)1793 	SQLRSERVER_DLLSPEC sqlrserverconnection *new_routerconnection(
1794 						sqlrservercontroller *cont) {
1795 		return new routerconnection(cont);
1796 	}
1797 }
1798