1 /*
2  * Purpose: Test remote procedure calls
3  * Functions:  dbretdata dbretlen dbretname dbretstatus dbrettype dbrpcinit dbrpcparam dbrpcsend
4  */
5 
6 #include "common.h"
7 
8 static RETCODE init_proc(DBPROCESS * dbproc, const char *name);
9 int ignore_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr);
10 int ignore_msg_handler(DBPROCESS * dbproc, DBINT msgno, int state, int severity, char *text, char *server, char *proc, int line);
11 
12 typedef struct {
13 	char *name, *value;
14 	int type, len;
15 } RETPARAM;
16 
17 static RETPARAM* save_retparam(RETPARAM *param, char *name, char *value, int type, int len);
18 
19 static RETCODE
init_proc(DBPROCESS * dbproc,const char * name)20 init_proc(DBPROCESS * dbproc, const char *name)
21 {
22 	RETCODE ret = FAIL;
23 
24 	if (name[0] != '#') {
25 		printf("Dropping procedure %s\n", name);
26 		sql_cmd(dbproc);
27 		dbsqlexec(dbproc);
28 		while (dbresults(dbproc) != NO_MORE_RESULTS) {
29 			/* nop */
30 		}
31 	}
32 
33 	printf("Creating procedure %s\n", name);
34 	sql_cmd(dbproc);
35 	if ((ret = dbsqlexec(dbproc)) == FAIL) {
36 		if (name[0] == '#')
37 			printf("Failed to create procedure %s. Wrong permission or not MSSQL.\n", name);
38 		else
39 			printf("Failed to create procedure %s. Wrong permission.\n", name);
40 	}
41 	while (dbresults(dbproc) != NO_MORE_RESULTS) {
42 		/* nop */
43 	}
44 	return ret;
45 }
46 
47 static RETPARAM*
save_retparam(RETPARAM * param,char * name,char * value,int type,int len)48 save_retparam(RETPARAM *param, char *name, char *value, int type, int len)
49 {
50 	free(param->name);
51 	free(param->value);
52 
53 	param->name = strdup(name);
54 	param->value = strdup(value);
55 
56 	param->type = type;
57 	param->len = len;
58 
59 	return param;
60 }
61 
62 static void
free_retparam(RETPARAM * param)63 free_retparam(RETPARAM *param)
64 {
65 	free(param->name);
66 	free(param->value);
67 	param->name = param->value = NULL;
68 }
69 
70 static int failed = 0;
71 
72 int
ignore_msg_handler(DBPROCESS * dbproc,DBINT msgno,int state,int severity,char * text,char * server,char * proc,int line)73 ignore_msg_handler(DBPROCESS * dbproc, DBINT msgno, int state, int severity, char *text, char *server, char *proc, int line)
74 {
75 	int ret;
76 
77 	dbsetuserdata(dbproc, (BYTE*) &msgno);
78 	/* printf("(ignoring message %d)\n", msgno); */
79 	ret = syb_msg_handler(dbproc, msgno, state, severity, text, server, proc, line);
80 	dbsetuserdata(dbproc, NULL);
81 	return ret;
82 }
83 /*
84  * The bad procedure name message has severity 15, causing db-lib to call the error handler after calling the message handler.
85  * This wrapper anticipates that behavior, and again sets the userdata, telling the handler this error is expected.
86  */
87 int
ignore_err_handler(DBPROCESS * dbproc,int severity,int dberr,int oserr,char * dberrstr,char * oserrstr)88 ignore_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
89 {
90 	int erc;
91 	static int recursion_depth = 0;
92 
93 	if (dbproc == NULL) {
94 		printf("expected error %d: \"%s\"\n", dberr, dberrstr? dberrstr : "");
95 		return INT_CANCEL;
96 	}
97 
98 	if (recursion_depth++) {
99 		printf("error %d: \"%s\"\n", dberr, dberrstr? dberrstr : "");
100 		printf("logic error: recursive call to ignore_err_handler\n");
101 		exit(1);
102 	}
103 	dbsetuserdata(dbproc, (BYTE*) &dberr);
104 	/* printf("(ignoring error %d)\n", dberr); */
105 	erc = syb_err_handler(dbproc, severity, dberr, oserr, dberrstr, oserrstr);
106 	dbsetuserdata(dbproc, NULL);
107 	recursion_depth--;
108 	return erc;
109 }
110 
111 static int
colwidth(DBPROCESS * dbproc,int icol)112 colwidth( DBPROCESS * dbproc, int icol )
113 {
114 	int width = dbwillconvert(dbcoltype(dbproc, icol), SYBCHAR);
115 	return 255 == width? dbcollen(dbproc, icol) : width;
116 }
117 
118 char param_data1[64], param_data3[8000+1], param_data4[2 * 4000 + 1];
119 int param_data2, param_data5;
120 
121 struct parameters_t {
122 	const char   *name;
123 	BYTE         status;
124 	int          type;
125 	DBINT        maxlen;
126 	DBINT        datalen;
127 	BYTE         *value;
128 };
129 
130 static struct parameters_t bindings[] = {
131 	  { "@null_input", DBRPCRETURN, SYBCHAR,  -1,   0, NULL }
132 	, { "@first_type", DBRPCRETURN, SYBCHAR,  sizeof(param_data1), 0, (BYTE *) &param_data1 }
133 	, { "@nullout",    DBRPCRETURN, SYBINT4,  -1,   0, (BYTE *) &param_data2 }
134 	, { "@varchar_tds7_out", DBRPCRETURN, SYBVARCHAR,  sizeof(param_data3),   0, (BYTE *) &param_data3 }
135 	, { "@nvarchar_tds7_out", DBRPCRETURN, 231,  sizeof(param_data4),   0, (BYTE *) &param_data4 }
136 	, { "@nrows",      DBRPCRETURN, SYBINT4,  -1,  -1, (BYTE *) &param_data5 }
137 	, { "@c_this_name_is_way_more_than_thirty_characters_charlie",
138 		           0,        SYBVARCHAR,   0,   0, NULL }
139 	, { "@nv",         0,        SYBVARCHAR,  -1,   2, (BYTE *) "OK:" }
140 	, { NULL, 0, 0, 0, 0, NULL }
141 };
142 
143 #define PARAM_STR(s) sizeof(s)-1, (BYTE*) s
144 static struct parameters_t bindings_mssql1[] = {
145 	  { "", 0, SYBVARCHAR,  -1,  PARAM_STR("set @a='test123'") }
146 	, { "", 0, SYBVARCHAR,  -1,  PARAM_STR("@a varchar(max) out") }
147 	, { "", DBRPCRETURN, SYBTEXT,  sizeof(param_data3), 0, (BYTE *) &param_data3 }
148 	, { NULL, 0, 0, 0, 0, NULL }
149 };
150 
151 static struct parameters_t bindings_mssql2[] = {
152 	  { "", 0, SYBVARCHAR,  -1,  PARAM_STR("set @a=null") }
153 	, { "", 0, SYBVARCHAR,  -1,  PARAM_STR("@a bit out") }
154 	, { "", DBRPCRETURN, SYBBIT,  sizeof(param_data3), 0, (BYTE *) &param_data3 }
155 	, { NULL, 0, 0, 0, 0, NULL }
156 };
157 
158 static void
bind_param(DBPROCESS * dbproc,struct parameters_t * pb)159 bind_param(DBPROCESS *dbproc, struct parameters_t *pb)
160 {
161 	RETCODE erc;
162 	const char *name = pb->name[0] ? pb->name : NULL;
163 
164 	if ((erc = dbrpcparam(dbproc, name, pb->status, pb->type, pb->maxlen, pb->datalen, pb->value)) == FAIL) {
165 		fprintf(stderr, "Failed line %d: dbrpcparam\n", __LINE__);
166 		failed++;
167 	}
168 }
169 
170 int
main(int argc,char ** argv)171 main(int argc, char **argv)
172 {
173 	LOGINREC *login;
174 	DBPROCESS *dbproc;
175 	RETPARAM save_param, save_varchar_tds7_param, save_nvarchar_tds7_param;
176 
177 	char teststr[8000+1], abbrev_data[10+3+1], *output;
178 	char *retname = NULL;
179 	int i;
180 	int rettype = 0, retlen = 0, return_status = 0;
181 	char proc[] = "#t0022";
182 	char *proc_name = proc;
183 
184 	int num_resultset = 0, num_empty_resultset = 0;
185 	int num_params = 6;
186 
187 	struct parameters_t *pb;
188 
189 	static const char dashes30[] = "------------------------------";
190 	static const char  *dashes5 = dashes30 + (sizeof(dashes30) - 5),
191 			  *dashes20 = dashes30 + (sizeof(dashes30) - 20);
192 
193 	RETCODE erc, row_code;
194 
195 	set_malloc_options();
196 
197 	memset(&save_param, 0, sizeof(save_param));
198 	memset(&save_varchar_tds7_param, 0, sizeof(save_varchar_tds7_param));
199 	memset(&save_nvarchar_tds7_param, 0, sizeof(save_nvarchar_tds7_param));
200 
201 	read_login_info(argc, argv);
202 
203 	printf("Starting %s\n", argv[0]);
204 
205 	dbinit();
206 
207 	dberrhandle(syb_err_handler);
208 	dbmsghandle(syb_msg_handler);
209 
210 	printf("About to logon\n");
211 
212 	login = dblogin();
213 	DBSETLPWD(login, PASSWORD);
214 	DBSETLUSER(login, USER);
215 	DBSETLAPP(login, "rpc");
216 	dberrhandle(ignore_err_handler);
217 	DBSETLPACKET(login, -1);
218 	dberrhandle(syb_err_handler);
219 
220 
221 	printf("About to open %s.%s\n", SERVER, DATABASE);
222 
223 	dbproc = dbopen(login, SERVER);
224 	if (strlen(DATABASE))
225 		dbuse(dbproc, DATABASE);
226 	dbloginfree(login);
227 
228 	printf("Check if server support long identifiers\n");
229 	sql_cmd(dbproc);
230 	i = 103;
231 	dbsetuserdata(dbproc, (BYTE*) &i);
232 	dbsqlexec(dbproc);
233 	while (dbresults(dbproc) != NO_MORE_RESULTS)
234 		while (dbnextrow(dbproc) != NO_MORE_ROWS)
235 			continue;
236 	dbsetuserdata(dbproc, NULL);
237 	if (i == 0) {
238 		fprintf(stderr, "This server does not support long identifiers\n");
239 		dbexit();
240 		return 0;
241 	}
242 
243 	dberrhandle(ignore_err_handler);
244 	dbmsghandle(ignore_msg_handler);
245 
246 	printf("trying to create a temporary stored procedure\n");
247 	if (FAIL == init_proc(dbproc, proc_name)) {
248 		num_params = 4;
249 		printf("trying to create a permanent stored procedure\n");
250 		if (FAIL == init_proc(dbproc, ++proc_name))
251 			exit(EXIT_FAILURE);
252 	}
253 
254 	dberrhandle(syb_err_handler);
255 	dbmsghandle(syb_msg_handler);
256 
257 	printf("Created procedure %s\n", proc_name);
258 
259 	/* set up and send the rpc */
260 	printf("executing dbrpcinit\n");
261 	erc = dbrpcinit(dbproc, proc_name, 0);	/* no options */
262 	if (erc == FAIL) {
263 		fprintf(stderr, "Failed line %d: dbrpcinit\n", __LINE__);
264 		failed = 1;
265 	}
266 
267 	for (pb = bindings, i = 0; pb->name != NULL; pb++, i++) {
268 		printf("executing dbrpcparam for %s\n", pb->name);
269 		if (num_params == 4 && (i == 3 || i == 4))
270 			continue;
271 		bind_param(dbproc, pb);
272 	}
273 	printf("executing dbrpcsend\n");
274 	param_data5 = 0x11223344;
275 	erc = dbrpcsend(dbproc);
276 	if (erc == FAIL) {
277 		fprintf(stderr, "Failed line %d: dbrpcsend\n", __LINE__);
278 		exit(1);
279 	}
280 
281 	/* wait for it to execute */
282 	printf("executing dbsqlok\n");
283 	erc = dbsqlok(dbproc);
284 	if (erc == FAIL) {
285 		fprintf(stderr, "Failed line %d: dbsqlok\n", __LINE__);
286 		exit(1);
287 	}
288 
289 	/* retrieve outputs per usual */
290 	printf("fetching results\n");
291 	while ((erc = dbresults(dbproc)) != NO_MORE_RESULTS) {
292 		printf("fetched resultset %d %s:\n", 1+num_resultset, erc==SUCCEED? "successfully":"unsuccessfully");
293 		if (erc == SUCCEED) {
294 			const int ncol = dbnumcols(dbproc);
295 			int empty_resultset = 1, c;
296 			enum {buflen=1024, nbuf=5};
297 			char bound_buffers[nbuf][buflen] = { "one", "two", "three", "four", "five" };
298 
299 			++num_resultset;
300 
301 			for( c=0; c < ncol && c < nbuf; c++ ) {
302 				printf("column %d (%s) is %d wide, ", c+1, dbcolname(dbproc, c+1), colwidth(dbproc, c+1));
303 				printf("buffer initialized to '%s'\n", bound_buffers[c]);
304 			}
305 			for( c=0; c < ncol && c < nbuf; c++ ) {
306 				erc = dbbind(dbproc, c+1, STRINGBIND, 0, (BYTE *) bound_buffers[c]);
307 				if (erc == FAIL) {
308 					fprintf(stderr, "Failed line %d: dbbind\n", __LINE__);
309 					exit(1);
310 				}
311 
312 				printf("%-*s ", colwidth(dbproc, c+1), dbcolname(dbproc, c+1));
313 			}
314 			printf("\n");
315 
316 			while ((row_code = dbnextrow(dbproc)) != NO_MORE_ROWS) {
317 				empty_resultset = 0;
318 				if (row_code == REG_ROW) {
319 					int c;
320 					for( c=0; c < ncol && c < nbuf; c++ ) {
321 						printf("%-*s ", colwidth(dbproc, c+1), bound_buffers[c]);
322 					}
323 					printf("\n");
324 				} else {
325 					/* not supporting computed rows in this unit test */
326 					failed = 1;
327 					fprintf(stderr, "Failed.  Expected a row\n");
328 					exit(1);
329 				}
330 			}
331 			printf("row count %d\n", (int) dbcount(dbproc));
332 			printf("hasretstatus %d\n", dbhasretstat(dbproc));
333 			if (num_resultset == 4 && !dbhasretstat(dbproc)) {
334 				fprintf(stderr, "dbnextrow should have set hasretstatus after last recordset\n");
335 				exit(1);
336 			}
337 			if (empty_resultset)
338 				++num_empty_resultset;
339 		} else {
340 			fprintf(stderr, "Expected a result set.\n");
341 			exit(1);
342 		}
343 	} /* while dbresults */
344 
345 	/* check return status */
346 	printf("retrieving return status...\n");
347 	if (dbhasretstat(dbproc) == TRUE) {
348 		printf("%d\n", return_status = dbretstatus(dbproc));
349 	} else {
350 		printf("none\n");
351 	}
352 
353 	/*
354 	 * Check output parameter values
355 	 */
356 	if (dbnumrets(dbproc) != num_params) {	/* dbnumrets missed something */
357 		fprintf(stderr, "Expected %d output parameters.\n", num_params);
358 		exit(1);
359 	}
360 	printf("retrieving output parameters...\n");
361 	printf("%-5s %-20s %5s %6s  %-30s\n", "param", "name", "type", "length", "data");
362 	printf("%-5s %-20s %5s %5s- %-30s\n", dashes5, dashes20, dashes5, dashes5, dashes30);
363 	for (i = 1; i <= dbnumrets(dbproc); i++) {
364 		retname = dbretname(dbproc, i);
365 		rettype = dbrettype(dbproc, i);
366 		retlen = dbretlen(dbproc, i);
367 		dbconvert(dbproc, rettype, dbretdata(dbproc, i), retlen, SYBVARCHAR, (BYTE*) teststr, -1);
368 		if(retlen <= 10) {
369 			output = teststr;
370 		} else {
371 			memcpy(abbrev_data, teststr, 10);
372 			sprintf(&abbrev_data[10], "...");
373 			output = abbrev_data;
374 		}
375 		printf("%-5d %-20s %5d %6d  %-30s\n", i, retname, rettype, retlen, output);
376 
377 		save_retparam(&save_param, retname, teststr, rettype, retlen);
378 		if (i == 4) {
379 			save_retparam(&save_varchar_tds7_param, retname, teststr, rettype, retlen);
380 		}
381 		if (i == 5) {
382 			save_retparam(&save_nvarchar_tds7_param, retname, teststr, rettype, retlen);
383 		}
384 	}
385 
386 	/*
387 	 * Test the last parameter for expected outcome
388 	 */
389 	if ((save_param.name == NULL) || strcmp(save_param.name, bindings[5].name)) {
390 		fprintf(stderr, "Expected retname to be '%s', got ", bindings[5].name);
391 		if (save_param.name == NULL)
392 			fprintf(stderr, "<NULL> instead.\n");
393 		else
394 			fprintf(stderr, "'%s' instead.\n", save_param.name);
395 		exit(1);
396 	}
397 	if (strcmp(save_param.value, "3")) {
398 		fprintf(stderr, "Expected retdata to be 3.\n");
399 		exit(1);
400 	}
401 	if (save_param.type != SYBINT4) {
402 		fprintf(stderr, "Expected rettype to be SYBINT4 was %d.\n", save_param.type);
403 		exit(1);
404 	}
405 	if (save_param.len != 4) {
406 		fprintf(stderr, "Expected retlen to be 4.\n");
407 		exit(1);
408 	}
409 
410 	if (num_params == 6) {
411 		/*
412 		 * Test name, size, contents of the VARCHAR(8000) output parameter
413 		 */
414 		if ((save_varchar_tds7_param.name == NULL) || strcmp(save_varchar_tds7_param.name, bindings[3].name)) {
415 			fprintf(stderr, "Expected retname to be '%s', got ", bindings[3].name);
416 			if (save_varchar_tds7_param.name == NULL)
417 				fprintf(stderr, "<NULL> instead.\n");
418 			else
419 				fprintf(stderr, "'%s' instead.\n", save_varchar_tds7_param.name);
420 			exit(1);
421 		}
422 		if (save_varchar_tds7_param.type != SYBVARCHAR) {
423 			fprintf(stderr, "Expected rettype to be SYBVARCHAR was %d.\n", save_varchar_tds7_param.type);
424 			exit(1);
425 		}
426 		if (save_varchar_tds7_param.len != 8000) {
427 			fprintf(stderr, "Expected retlen to be 8000 was %d.\n", save_varchar_tds7_param.len);
428 			exit(1);
429 		}
430 
431 		/*
432 		 * Test name, size, contents of the NVARCHAR(4000) output parameter
433 		 */
434 		if ((save_nvarchar_tds7_param.name == NULL) || strcmp(save_nvarchar_tds7_param.name, bindings[4].name)) {
435 			fprintf(stderr, "Expected retname to be '%s', got ", bindings[4].name);
436 			if (save_varchar_tds7_param.name == NULL)
437 				fprintf(stderr, "<NULL> instead.\n");
438 			else
439 				fprintf(stderr, "'%s' instead.\n", save_nvarchar_tds7_param.name);
440 			exit(1);
441 		}
442 		if (save_nvarchar_tds7_param.len != 4000) {
443 			fprintf(stderr, "Expected retlen to be 4000 was %d.\n", save_nvarchar_tds7_param.len);
444 			exit(1);
445 		}
446 	}
447 
448 	if(42 != return_status) {
449 		fprintf(stderr, "Expected status to be 42.\n");
450 		exit(1);
451 	}
452 
453 	printf("Good: Got 6 output parameters and 1 return status of %d.\n", return_status);
454 
455 
456 	/* Test number of result sets */
457 	if (num_resultset != 4) {
458 		fprintf(stderr, "Expected 4 resultset got %d.\n", num_resultset);
459 		exit(1);
460 	}
461 	if (num_empty_resultset != 1) {
462 		fprintf(stderr, "Expected an empty resultset got %d.\n", num_empty_resultset);
463 		exit(1);
464 	}
465 	printf("Good: Got %d resultsets and %d empty resultset.\n", num_resultset, num_empty_resultset);
466 
467 
468 
469 	printf("Dropping procedure\n");
470 	sql_cmd(dbproc);
471 	dbsqlexec(dbproc);
472 	while (dbresults(dbproc) != NO_MORE_RESULTS) {
473 		/* nop */
474 	}
475 
476 	/* additional tests for mssql */
477 #if defined(DBTDS_7_2)
478 	if (num_params == 6 && dbtds(dbproc) >= DBTDS_7_2) {
479 		erc = dbrpcinit(dbproc, "sp_executesql", 0);	/* no options */
480 		if (erc == FAIL) {
481 			fprintf(stderr, "Failed line %d: dbrpcinit\n", __LINE__);
482 			failed = 1;
483 		}
484 		for (pb = bindings_mssql1; pb->name != NULL; pb++)
485 			bind_param(dbproc, pb);
486 		erc = dbrpcsend(dbproc);
487 		if (erc == FAIL) {
488 			fprintf(stderr, "Failed line %d: dbrpcsend\n", __LINE__);
489 			exit(1);
490 		}
491 		while (dbresults(dbproc) != NO_MORE_RESULTS)
492 			continue;
493 		if (dbnumrets(dbproc) != 1) {	/* dbnumrets missed something */
494 			fprintf(stderr, "Expected 1 output parameters.\n");
495 			exit(1);
496 		}
497 		i = 1;
498 		retname = dbretname(dbproc, i);
499 		rettype = dbrettype(dbproc, i);
500 		retlen = dbretlen(dbproc, i);
501 		dbconvert(dbproc, rettype, dbretdata(dbproc, i), retlen, SYBVARCHAR, (BYTE*) teststr, -1);
502 		if (strcmp(teststr, "test123") != 0) {
503 			fprintf(stderr, "Unexpected '%s' results.\n", teststr);
504 			exit(1);
505 		}
506 
507 		erc = dbrpcinit(dbproc, "sp_executesql", 0);	/* no options */
508 		if (erc == FAIL) {
509 			fprintf(stderr, "Failed line %d: dbrpcinit\n", __LINE__);
510 			failed = 1;
511 		}
512 		for (pb = bindings_mssql2; pb->name != NULL; pb++)
513 			bind_param(dbproc, pb);
514 		erc = dbrpcsend(dbproc);
515 		if (erc == FAIL) {
516 			fprintf(stderr, "Failed line %d: dbrpcsend\n", __LINE__);
517 			exit(1);
518 		}
519 		while (dbresults(dbproc) != NO_MORE_RESULTS)
520 			continue;
521 		if (dbnumrets(dbproc) != 1) {	/* dbnumrets missed something */
522 			fprintf(stderr, "Expected 1 output parameters.\n");
523 			exit(1);
524 		}
525 		i = 1;
526 		retname = dbretname(dbproc, i);
527 		rettype = dbrettype(dbproc, i);
528 		retlen = dbretlen(dbproc, i);
529 		dbconvert(dbproc, rettype, dbretdata(dbproc, i), retlen, SYBVARCHAR, (BYTE*) teststr, -1);
530 		if (dbretdata(dbproc, i) != NULL || rettype != SYBBIT || retlen != 0) {
531 			fprintf(stderr, "Unexpected '%s' results.\n", teststr);
532 			exit(1);
533 		}
534 	}
535 #endif
536 
537 	dbexit();
538 
539 	printf("%s %s\n", __FILE__, (failed ? "failed!" : "OK"));
540 
541 	free_retparam(&save_param);
542 	free_retparam(&save_varchar_tds7_param);
543 	free_retparam(&save_nvarchar_tds7_param);
544 
545 	return failed ? 1 : 0;
546 }
547