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