Line data Source code
1 : #include "common.h"
2 :
3 : #include <freetds/macros.h>
4 :
5 : static CS_RETCODE ex_display_header(CS_INT numcols, CS_DATAFMT columns[]);
6 : static CS_INT ex_display_dlen(CS_DATAFMT *column);
7 : static CS_INT ex_display_results(CS_COMMAND * cmd);
8 :
9 : typedef struct _ex_column_data
10 : {
11 : CS_SMALLINT indicator;
12 : CS_CHAR *value;
13 : CS_INT valuelen;
14 : }
15 : EX_COLUMN_DATA;
16 :
17 : /* Testing: array binding of result set */
18 10 : TEST_MAIN()
19 : {
20 : CS_CONTEXT *ctx;
21 : CS_CONNECTION *conn;
22 : CS_COMMAND *cmd;
23 10 : int verbose = 0;
24 :
25 : CS_INT datalength;
26 : CS_SMALLINT nullind;
27 : CS_SMALLINT notnullind;
28 :
29 : CS_CHAR cmdbuf[4096];
30 :
31 : CS_DATAFMT datafmt;
32 : CS_DATAFMT srcfmt;
33 : CS_DATAFMT destfmt;
34 : CS_INT intvar;
35 : CS_SMALLINT smallintvar;
36 : CS_FLOAT floatvar;
37 : CS_MONEY moneyvar;
38 : CS_BINARY binaryvar;
39 : char moneystring[10];
40 : char rpc_name[15];
41 : CS_INT destlen;
42 :
43 :
44 :
45 10 : printf("%s: submit a stored procedure using ct_setparam \n", __FILE__);
46 : if (verbose) {
47 : printf("Trying login\n");
48 : }
49 10 : check_call(try_ctlogin, (&ctx, &conn, &cmd, verbose));
50 10 : error_to_stdout = true;
51 :
52 : /* do not test error */
53 10 : run_command(cmd, "IF OBJECT_ID('sample_rpc') IS NOT NULL DROP PROCEDURE sample_rpc");
54 :
55 10 : strcpy(cmdbuf, "create proc sample_rpc (@intparam int, \
56 : @sintparam smallint output, @floatparam float output, \
57 : @moneyparam money output, \
58 : @dateparam datetime output, @charparam char(20) output, \
59 : @binaryparam varbinary(2000) output) \
60 : as ");
61 :
62 10 : strcat(cmdbuf, "select @intparam, @sintparam, @floatparam, @moneyparam, \
63 : @dateparam, @charparam, @binaryparam \
64 : select @sintparam = @sintparam + @intparam \
65 : select @floatparam = @floatparam + @intparam \
66 : select @moneyparam = @moneyparam + convert(money, @intparam) \
67 : select @dateparam = getdate() \
68 : select @charparam = \'The char parameters\' \
69 : select @binaryparam = @binaryparam \
70 : print \'This is the message printed out by sample_rpc.\'");
71 :
72 10 : check_call(run_command, (cmd, cmdbuf));
73 :
74 : /*
75 : * Assign values to the variables used for parameter passing.
76 : */
77 :
78 10 : intvar = 2;
79 10 : smallintvar = 234;
80 10 : floatvar = 0.12;
81 10 : binaryvar = (CS_BINARY) 0xff;
82 10 : strcpy(rpc_name, "sample_rpc");
83 10 : strcpy(moneystring, "300.90");
84 :
85 : /*
86 : * Clear and setup the CS_DATAFMT structures used to convert datatypes.
87 : */
88 :
89 10 : memset(&srcfmt, 0, sizeof(CS_DATAFMT));
90 : srcfmt.datatype = CS_CHAR_TYPE;
91 10 : srcfmt.maxlength = (CS_INT) strlen(moneystring);
92 10 : srcfmt.precision = 5;
93 10 : srcfmt.scale = 2;
94 : srcfmt.locale = NULL;
95 :
96 10 : memset(&destfmt, 0, sizeof(CS_DATAFMT));
97 10 : destfmt.datatype = CS_MONEY_TYPE;
98 10 : destfmt.maxlength = sizeof(CS_MONEY);
99 10 : destfmt.precision = 5;
100 10 : destfmt.scale = 2;
101 : destfmt.locale = NULL;
102 :
103 : /*
104 : * Convert the string representing the money value
105 : * to a CS_MONEY variable. Since this routine does not have the
106 : * context handle, we use the property functions to get it.
107 : */
108 10 : check_call(ct_cmd_props, (cmd, CS_GET, CS_PARENT_HANDLE, &conn, CS_UNUSED, NULL));
109 10 : check_call(ct_con_props, (conn, CS_GET, CS_PARENT_HANDLE, &ctx, CS_UNUSED, NULL));
110 10 : check_call(cs_convert, (ctx, &srcfmt, (CS_VOID *) moneystring, &destfmt, &moneyvar, &destlen));
111 :
112 : /*
113 : * Send the RPC command for our stored procedure.
114 : */
115 10 : check_call(ct_command, (cmd, CS_RPC_CMD, rpc_name, CS_NULLTERM, CS_NO_RECOMPILE));
116 :
117 10 : nullind = -1;
118 10 : notnullind = 0;
119 : /*
120 : * Clear and setup the CS_DATAFMT structure, then pass
121 : * each of the parameters for the RPC.
122 : */
123 10 : memset(&datafmt, 0, sizeof(datafmt));
124 10 : strcpy(datafmt.name, "@intparam");
125 10 : datafmt.namelen = CS_NULLTERM;
126 10 : datafmt.datatype = CS_INT_TYPE;
127 10 : datafmt.maxlength = CS_UNUSED;
128 10 : datafmt.status = CS_INPUTVALUE;
129 : datafmt.locale = NULL;
130 :
131 10 : datalength = CS_SIZEOF(CS_INT);
132 :
133 10 : check_call(ct_setparam, (cmd, &datafmt, (CS_VOID *) & intvar, &datalength, ¬nullind));
134 :
135 10 : strcpy(datafmt.name, "@sintparam");
136 10 : datafmt.namelen = CS_NULLTERM;
137 10 : datafmt.datatype = CS_SMALLINT_TYPE;
138 10 : datafmt.maxlength = 255;
139 10 : datafmt.status = CS_RETURN;
140 10 : datafmt.locale = NULL;
141 :
142 10 : datalength = CS_SIZEOF(CS_SMALLINT);
143 :
144 10 : check_call(ct_setparam, (cmd, &datafmt, (CS_VOID *) & smallintvar, &datalength, ¬nullind));
145 :
146 10 : strcpy(datafmt.name, "@floatparam");
147 10 : datafmt.namelen = CS_NULLTERM;
148 10 : datafmt.datatype = CS_FLOAT_TYPE;
149 10 : datafmt.maxlength = 255;
150 10 : datafmt.status = CS_RETURN;
151 10 : datafmt.locale = NULL;
152 :
153 10 : datalength = CS_SIZEOF(CS_FLOAT);
154 :
155 10 : check_call(ct_setparam, (cmd, &datafmt, (CS_VOID *) & floatvar, &datalength, ¬nullind));
156 :
157 10 : strcpy(datafmt.name, "@moneyparam");
158 10 : datafmt.namelen = CS_NULLTERM;
159 10 : datafmt.datatype = CS_MONEY_TYPE;
160 10 : datafmt.maxlength = 255;
161 10 : datafmt.status = CS_RETURN;
162 10 : datafmt.locale = NULL;
163 :
164 10 : datalength = CS_SIZEOF(CS_MONEY);
165 :
166 10 : check_call(ct_setparam, (cmd, &datafmt, (CS_VOID *) & moneyvar, &datalength, ¬nullind));
167 :
168 10 : strcpy(datafmt.name, "@dateparam");
169 10 : datafmt.namelen = CS_NULLTERM;
170 10 : datafmt.datatype = CS_DATETIME4_TYPE;
171 10 : datafmt.maxlength = 255;
172 10 : datafmt.status = CS_RETURN;
173 10 : datafmt.locale = NULL;
174 :
175 : /*
176 : * The datetime variable is filled in by the RPC so pass NULL for
177 : * the data, 0 for data length, and -1 for the indicator arguments.
178 : */
179 :
180 10 : datalength = 0;
181 :
182 10 : check_call(ct_setparam, (cmd, &datafmt, NULL, &datalength, &nullind));
183 10 : strcpy(datafmt.name, "@charparam");
184 10 : datafmt.namelen = CS_NULLTERM;
185 10 : datafmt.datatype = CS_CHAR_TYPE;
186 10 : datafmt.maxlength = 60;
187 10 : datafmt.status = CS_RETURN;
188 10 : datafmt.locale = NULL;
189 :
190 :
191 10 : datalength = 0;
192 :
193 : /*
194 : * The character string variable is filled in by the RPC so pass NULL
195 : * for the data 0 for data length, and -1 for the indicator arguments.
196 : */
197 10 : check_call(ct_setparam, (cmd, &datafmt, NULL, &datalength, &nullind));
198 :
199 10 : strcpy(datafmt.name, "@binaryparam");
200 10 : datafmt.namelen = CS_NULLTERM;
201 10 : datafmt.datatype = CS_LONGBINARY_TYPE;
202 10 : datafmt.maxlength = 2000;
203 10 : datafmt.status = CS_RETURN;
204 10 : datafmt.locale = NULL;
205 :
206 10 : datalength = CS_SIZEOF(CS_BINARY);
207 10 : nullind = -1;
208 :
209 10 : check_call(ct_setparam, (cmd, &datafmt, (CS_VOID *) & binaryvar, &datalength, ¬nullind));
210 :
211 : /*
212 : * Send the command to the server
213 : */
214 10 : check_call(ct_send, (cmd));
215 :
216 10 : check_call(ex_display_results, (cmd));
217 :
218 10 : intvar = 3;
219 :
220 10 : check_call(ct_send, (cmd));
221 :
222 10 : check_call(ex_display_results, (cmd));
223 :
224 10 : run_command(cmd, "DROP PROCEDURE sample_rpc");
225 :
226 : if (verbose) {
227 : printf("Trying logout\n");
228 : }
229 10 : check_call(try_ctlogout, (ctx, conn, cmd, verbose));
230 :
231 10 : return 0;
232 : }
233 :
234 : static CS_INT
235 20 : ex_display_results(CS_COMMAND * cmd)
236 : {
237 :
238 : CS_RETCODE ret;
239 : CS_INT res_type;
240 : CS_INT num_cols;
241 : EX_COLUMN_DATA *coldata;
242 : CS_DATAFMT *outdatafmt;
243 20 : CS_INT row_count = 0;
244 : CS_INT rows_read;
245 : CS_INT disp_len;
246 : CS_SMALLINT msg_id;
247 : int i, j;
248 :
249 : /*
250 : * Process the results of the RPC.
251 : */
252 92 : while ((ret = ct_results(cmd, &res_type)) == CS_SUCCEED) {
253 52 : switch ((int) res_type) {
254 12 : case CS_ROW_RESULT:
255 : case CS_PARAM_RESULT:
256 : case CS_STATUS_RESULT:
257 : /*
258 : * Print the result header based on the result type.
259 : */
260 12 : switch ((int) res_type) {
261 4 : case CS_ROW_RESULT:
262 4 : printf("\nROW RESULTS\n");
263 4 : break;
264 :
265 4 : case CS_PARAM_RESULT:
266 4 : printf("\nPARAMETER RESULTS\n");
267 4 : break;
268 :
269 4 : case CS_STATUS_RESULT:
270 4 : printf("\nSTATUS RESULTS\n");
271 4 : break;
272 : }
273 12 : fflush(stdout);
274 :
275 : /*
276 : * All three of these result types are fetchable.
277 : * Since the result model for rpcs and rows have
278 : * been unified in the New Client-Library, we
279 : * will use the same routine to display them
280 : */
281 :
282 : /*
283 : * Find out how many columns there are in this result set.
284 : */
285 12 : check_call(ct_res_info, (cmd, CS_NUMDATA, &num_cols, CS_UNUSED, NULL));
286 :
287 : /*
288 : * Make sure we have at least one column
289 : */
290 12 : if (num_cols <= 0) {
291 0 : fprintf(stderr, "ct_res_info(CS_NUMDATA) returned zero columns");
292 0 : return 1;
293 : }
294 :
295 : /*
296 : * Our program variable, called 'coldata', is an array of
297 : * EX_COLUMN_DATA structures. Each array element represents
298 : * one column. Each array element will re-used for each row.
299 : *
300 : * First, allocate memory for the data element to process.
301 : */
302 12 : coldata = (EX_COLUMN_DATA *) malloc(num_cols * sizeof(EX_COLUMN_DATA));
303 12 : if (coldata == NULL) {
304 0 : fprintf(stderr, "malloc coldata failed \n");
305 0 : return 1;
306 : }
307 :
308 12 : outdatafmt = (CS_DATAFMT *) malloc(num_cols * sizeof(CS_DATAFMT));
309 12 : if (outdatafmt == NULL) {
310 0 : free(coldata);
311 0 : fprintf(stderr, "malloc outdatafmt failed \n");
312 0 : return 1;
313 : }
314 :
315 68 : for (i = 0; i < num_cols; i++) {
316 56 : ret = ct_describe(cmd, (i + 1), &outdatafmt[i]);
317 56 : if (ret != CS_SUCCEED) {
318 0 : fprintf(stderr, "ct_describe failed \n");
319 0 : break;
320 : }
321 :
322 56 : outdatafmt[i].maxlength = ex_display_dlen(&outdatafmt[i]) + 1;
323 56 : outdatafmt[i].datatype = CS_CHAR_TYPE;
324 56 : outdatafmt[i].format = CS_FMT_NULLTERM;
325 :
326 56 : coldata[i].value = (CS_CHAR *) malloc(outdatafmt[i].maxlength);
327 56 : coldata[i].value[0] = 0;
328 : if (coldata[i].value == NULL) {
329 : fprintf(stderr, "malloc coldata.value failed \n");
330 : ret = CS_FAIL;
331 : break;
332 : }
333 :
334 56 : ret = ct_bind(cmd, (i + 1), &outdatafmt[i], coldata[i].value, &coldata[i].valuelen,
335 : & coldata[i].indicator);
336 56 : if (ret != CS_SUCCEED) {
337 0 : free(coldata[i].value);
338 0 : fprintf(stderr, "ct_bind failed \n");
339 0 : break;
340 : }
341 : }
342 12 : if (ret != CS_SUCCEED) {
343 0 : for (j = 0; j < i; j++) {
344 0 : free(coldata[j].value);
345 : }
346 0 : free(coldata);
347 0 : free(outdatafmt);
348 0 : return 1;
349 : }
350 :
351 12 : ex_display_header(num_cols, outdatafmt);
352 :
353 36 : while (((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED,
354 12 : &rows_read)) == CS_SUCCEED) || (ret == CS_ROW_FAIL)) {
355 : /*
356 : * Increment our row count by the number of rows just fetched.
357 : */
358 12 : row_count = row_count + rows_read;
359 :
360 : /*
361 : * Check if we hit a recoverable error.
362 : */
363 12 : if (ret == CS_ROW_FAIL) {
364 0 : printf("Error on row %d.\n", row_count);
365 0 : fflush(stdout);
366 : }
367 :
368 : /*
369 : * We have a row. Loop through the columns displaying the
370 : * column values.
371 : */
372 56 : for (i = 0; i < num_cols; i++) {
373 : /*
374 : * Display the column value
375 : */
376 56 : printf("%s", coldata[i].value);
377 56 : fflush(stdout);
378 :
379 : /*
380 : * If not last column, Print out spaces between this
381 : * column and next one.
382 : */
383 56 : if (i != num_cols - 1) {
384 44 : disp_len = ex_display_dlen(&outdatafmt[i]);
385 44 : disp_len -= coldata[i].valuelen - 1;
386 784 : for (j = 0; j < disp_len; j++) {
387 740 : fputc(' ', stdout);
388 : }
389 : }
390 : }
391 12 : printf("\n");
392 12 : fflush(stdout);
393 : }
394 :
395 : /*
396 : * Free allocated space.
397 : */
398 56 : for (i = 0; i < num_cols; i++) {
399 56 : free(coldata[i].value);
400 : }
401 12 : free(coldata);
402 12 : free(outdatafmt);
403 :
404 : /*
405 : * We're done processing rows. Let's check the final return
406 : * value of ct_fetch().
407 : */
408 12 : switch ((int) ret) {
409 12 : case CS_END_DATA:
410 : /*
411 : * Everything went fine.
412 : */
413 12 : printf("All done processing rows.\n");
414 12 : fflush(stdout);
415 : break;
416 :
417 0 : case CS_FAIL:
418 : /*
419 : * Something terrible happened.
420 : */
421 0 : fprintf(stderr, "ct_fetch returned CS_FAIL\n");
422 0 : return 1;
423 : break;
424 :
425 0 : default:
426 : /*
427 : * We got an unexpected return value.
428 : */
429 0 : fprintf(stderr, "ct_fetch returned %d\n", ret);
430 0 : return 1;
431 : break;
432 :
433 : }
434 12 : break;
435 :
436 0 : case CS_MSG_RESULT:
437 0 : check_call(ct_res_info, (cmd, CS_MSGTYPE, (CS_VOID *) & msg_id, CS_UNUSED, NULL));
438 0 : printf("ct_result returned CS_MSG_RESULT where msg id = %d.\n", msg_id);
439 0 : fflush(stdout);
440 0 : break;
441 :
442 : case CS_CMD_SUCCEED:
443 : /*
444 : * This means no rows were returned.
445 : */
446 : break;
447 :
448 : case CS_CMD_DONE:
449 : /*
450 : * Done with result set.
451 : */
452 : break;
453 :
454 14 : case CS_CMD_FAIL:
455 : /*
456 : * The server encountered an error while
457 : * processing our command.
458 : */
459 14 : fprintf(stderr, "ct_results returned CS_CMD_FAIL.");
460 14 : break;
461 :
462 0 : default:
463 : /*
464 : * We got something unexpected.
465 : */
466 0 : fprintf(stderr, "ct_results returned unexpected result type.");
467 0 : return CS_FAIL;
468 : }
469 : }
470 :
471 : /*
472 : * We're done processing results. Let's check the
473 : * return value of ct_results() to see if everything
474 : * went ok.
475 : */
476 20 : switch ((int) ret) {
477 : case CS_END_RESULTS:
478 : /*
479 : * Everything went fine.
480 : */
481 : break;
482 :
483 2 : case CS_FAIL:
484 : /*
485 : * Something failed happened.
486 : */
487 2 : fprintf(stderr, "ct_results failed.");
488 2 : break;
489 :
490 0 : default:
491 : /*
492 : * We got an unexpected return value.
493 : */
494 0 : fprintf(stderr, "ct_results returned unexpected result type.");
495 0 : break;
496 : }
497 :
498 : return CS_SUCCEED;
499 :
500 :
501 :
502 : }
503 :
504 : static CS_INT
505 212 : ex_display_dlen(CS_DATAFMT *column)
506 : {
507 : CS_INT len;
508 :
509 212 : switch ((int) column->datatype) {
510 164 : case CS_CHAR_TYPE:
511 : case CS_VARCHAR_TYPE:
512 : case CS_TEXT_TYPE:
513 : case CS_IMAGE_TYPE:
514 164 : len = TDS_MIN(column->maxlength, 1024);
515 164 : break;
516 :
517 0 : case CS_BINARY_TYPE:
518 : case CS_VARBINARY_TYPE:
519 0 : len = TDS_MIN((2 * column->maxlength) + 2, 1024);
520 0 : break;
521 :
522 : case CS_BIT_TYPE:
523 : case CS_TINYINT_TYPE:
524 : len = 3;
525 : break;
526 :
527 8 : case CS_SMALLINT_TYPE:
528 8 : len = 6;
529 8 : break;
530 :
531 8 : case CS_INT_TYPE:
532 8 : len = 11;
533 8 : break;
534 :
535 8 : case CS_REAL_TYPE:
536 : case CS_FLOAT_TYPE:
537 8 : len = 20;
538 8 : break;
539 :
540 8 : case CS_MONEY_TYPE:
541 : case CS_MONEY4_TYPE:
542 8 : len = 24;
543 8 : break;
544 :
545 8 : case CS_DATETIME_TYPE:
546 : case CS_DATETIME4_TYPE:
547 8 : len = 30;
548 8 : break;
549 :
550 0 : case CS_NUMERIC_TYPE:
551 : case CS_DECIMAL_TYPE:
552 0 : len = (CS_MAX_PREC + 2);
553 0 : break;
554 :
555 8 : default:
556 8 : len = 12;
557 8 : break;
558 : }
559 :
560 212 : return TDS_MAX((CS_INT) (strlen(column->name) + 1), len);
561 : }
562 :
563 : static CS_RETCODE
564 12 : ex_display_header(CS_INT numcols, CS_DATAFMT columns[])
565 : {
566 : CS_INT i;
567 : CS_INT l;
568 : CS_INT j;
569 : CS_INT disp_len;
570 :
571 12 : fputc('\n', stdout);
572 68 : for (i = 0; i < numcols; i++) {
573 56 : disp_len = ex_display_dlen(&columns[i]);
574 56 : printf("%s", columns[i].name);
575 56 : fflush(stdout);
576 56 : l = disp_len - (CS_INT) strlen(columns[i].name);
577 1024 : for (j = 0; j < l; j++) {
578 968 : fputc(' ', stdout);
579 968 : fflush(stdout);
580 : }
581 : }
582 12 : fputc('\n', stdout);
583 12 : fflush(stdout);
584 68 : for (i = 0; i < numcols; i++) {
585 56 : disp_len = ex_display_dlen(&columns[i]);
586 56 : l = disp_len - 1;
587 1224 : for (j = 0; j < l; j++) {
588 1168 : fputc('-', stdout);
589 : }
590 56 : fputc(' ', stdout);
591 : }
592 12 : fputc('\n', stdout);
593 :
594 12 : return CS_SUCCEED;
595 : }
|