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