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