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