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 10 : main(void)
20 : {
21 : CS_CONTEXT *ctx;
22 : CS_CONNECTION *conn;
23 : CS_COMMAND *cmd;
24 10 : 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 10 : 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 10 : 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 10 : printf("%s: submit a stored procedure using ct_param \n", __FILE__);
56 : if (verbose) {
57 : printf("Trying login\n");
58 : }
59 10 : check_call(try_ctlogin, (&ctx, &conn, &cmd, verbose));
60 10 : error_to_stdout = true;
61 :
62 : /* do not test error */
63 10 : ret = run_command(cmd, "IF OBJECT_ID('sample_rpc') IS NOT NULL DROP PROCEDURE sample_rpc");
64 :
65 10 : 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 10 : 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 10 : check_call(run_command, (cmd, cmdbuf));
84 :
85 : /*
86 : * Assign values to the variables used for parameter passing.
87 : */
88 :
89 10 : intvar = 2;
90 10 : smallintvar = 234;
91 10 : floatvar = 0.12;
92 10 : binaryvar = (CS_BINARY) 0xff;
93 10 : strcpy(rpc_name, "sample_rpc");
94 10 : strcpy(moneystring, "300.90");
95 :
96 : /*
97 : * Clear and setup the CS_DATAFMT structures used to convert datatypes.
98 : */
99 :
100 10 : memset(&srcfmt, 0, sizeof(CS_DATAFMT));
101 : srcfmt.datatype = CS_CHAR_TYPE;
102 10 : srcfmt.maxlength = strlen(moneystring);
103 10 : srcfmt.precision = 5;
104 10 : srcfmt.scale = 2;
105 : srcfmt.locale = NULL;
106 :
107 10 : memset(&destfmt, 0, sizeof(CS_DATAFMT));
108 10 : destfmt.datatype = CS_MONEY_TYPE;
109 10 : destfmt.maxlength = sizeof(CS_MONEY);
110 10 : destfmt.precision = 5;
111 10 : 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 10 : check_call(ct_cmd_props, (cmd, CS_GET, CS_PARENT_HANDLE, &conn, CS_UNUSED, NULL));
120 10 : check_call(ct_con_props, (conn, CS_GET, CS_PARENT_HANDLE, &ctx, CS_UNUSED, NULL));
121 10 : 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 10 : 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 10 : memset(&datafmt, 0, sizeof(datafmt));
133 10 : strcpy(datafmt.name, "@intparam");
134 10 : datafmt.namelen = CS_NULLTERM;
135 10 : datafmt.datatype = CS_INT_TYPE;
136 10 : datafmt.maxlength = CS_UNUSED;
137 10 : datafmt.status = CS_INPUTVALUE;
138 : datafmt.locale = NULL;
139 :
140 10 : check_call(ct_param, (cmd, &datafmt, (CS_VOID *) & intvar, CS_SIZEOF(CS_INT), 0));
141 :
142 10 : strcpy(datafmt.name, "@sintparam");
143 10 : datafmt.namelen = CS_NULLTERM;
144 10 : datafmt.datatype = CS_SMALLINT_TYPE;
145 10 : datafmt.maxlength = 255;
146 10 : datafmt.status = CS_RETURN;
147 10 : datafmt.locale = NULL;
148 :
149 10 : check_call(ct_param, (cmd, &datafmt, (CS_VOID *) & smallintvar, CS_SIZEOF(CS_SMALLINT), 0));
150 :
151 10 : strcpy(datafmt.name, "@floatparam");
152 10 : datafmt.namelen = CS_NULLTERM;
153 10 : datafmt.datatype = CS_FLOAT_TYPE;
154 10 : datafmt.maxlength = 255;
155 10 : datafmt.status = CS_RETURN;
156 10 : datafmt.locale = NULL;
157 :
158 10 : check_call(ct_param, (cmd, &datafmt, (CS_VOID *) & floatvar, CS_SIZEOF(CS_FLOAT), 0));
159 :
160 10 : strcpy(datafmt.name, "@moneyparam");
161 10 : datafmt.namelen = CS_NULLTERM;
162 10 : datafmt.datatype = CS_MONEY_TYPE;
163 10 : datafmt.maxlength = 255;
164 10 : datafmt.status = CS_RETURN;
165 10 : datafmt.locale = NULL;
166 :
167 10 : check_call(ct_param, (cmd, &datafmt, (CS_VOID *) & moneyvar, CS_SIZEOF(CS_MONEY), 0));
168 :
169 10 : strcpy(datafmt.name, "@dateparam");
170 10 : datafmt.namelen = CS_NULLTERM;
171 10 : datafmt.datatype = CS_DATETIME4_TYPE;
172 10 : datafmt.maxlength = 255;
173 10 : datafmt.status = CS_RETURN;
174 10 : 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 10 : check_call(ct_param, (cmd, &datafmt, NULL, 0, -1));
181 10 : strcpy(datafmt.name, "@charparam");
182 10 : datafmt.namelen = CS_NULLTERM;
183 10 : datafmt.datatype = CS_CHAR_TYPE;
184 10 : datafmt.maxlength = 60;
185 10 : datafmt.status = CS_RETURN;
186 10 : 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 10 : check_call(ct_param, (cmd, &datafmt, NULL, 0, -1));
193 :
194 10 : strcpy(datafmt.name, "@empty");
195 10 : datafmt.namelen = CS_NULLTERM;
196 10 : datafmt.datatype = CS_VARCHAR_TYPE;
197 10 : datafmt.maxlength = 60;
198 10 : datafmt.status = CS_RETURN;
199 10 : 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 10 : check_call(ct_param, (cmd, &datafmt, NULL, 0, -1));
206 :
207 10 : strcpy(datafmt.name, "@binaryparam");
208 10 : datafmt.namelen = CS_NULLTERM;
209 10 : datafmt.datatype = CS_BINARY_TYPE;
210 10 : datafmt.maxlength = 255;
211 10 : datafmt.status = CS_RETURN;
212 10 : datafmt.locale = NULL;
213 :
214 10 : check_call(ct_param, (cmd, &datafmt, (CS_VOID *) & binaryvar, CS_SIZEOF(CS_BINARY), 0));
215 :
216 10 : strcpy(datafmt.name, "@bitparam");
217 10 : datafmt.namelen = CS_NULLTERM;
218 10 : datafmt.datatype = CS_BIT_TYPE;
219 10 : datafmt.maxlength = 1;
220 10 : datafmt.status = 0;
221 10 : datafmt.locale = NULL;
222 :
223 10 : 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 10 : check_call(ct_send, (cmd));
229 :
230 : /*
231 : * Process the results of the RPC.
232 : */
233 80 : while ((ret = ct_results(cmd, &res_type)) == CS_SUCCEED) {
234 60 : switch ((int) res_type) {
235 30 : 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 30 : switch ((int) res_type) {
242 10 : case CS_ROW_RESULT:
243 10 : printf("\nROW RESULTS\n");
244 10 : break;
245 :
246 10 : case CS_PARAM_RESULT:
247 10 : printf("\nPARAMETER RESULTS\n");
248 10 : break;
249 :
250 10 : case CS_STATUS_RESULT:
251 10 : printf("\nSTATUS RESULTS\n");
252 10 : break;
253 : }
254 30 : 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 30 : 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 30 : 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 30 : coldata = (EX_COLUMN_DATA *) malloc(num_cols * sizeof(EX_COLUMN_DATA));
284 30 : if (coldata == NULL) {
285 0 : fprintf(stderr, "malloc coldata failed \n");
286 0 : return 1;
287 : }
288 :
289 30 : outdatafmt = (CS_DATAFMT *) malloc(num_cols * sizeof(CS_DATAFMT));
290 30 : if (outdatafmt == NULL) {
291 0 : fprintf(stderr, "malloc outdatafmt failed \n");
292 0 : return 1;
293 : }
294 :
295 150 : for (i = 0; i < num_cols; i++) {
296 150 : check_call(ct_describe, (cmd, (i + 1), &outdatafmt[i]));
297 :
298 150 : outdatafmt[i].maxlength = ex_display_dlen(&outdatafmt[i]) + 1;
299 150 : outdatafmt[i].datatype = CS_CHAR_TYPE;
300 150 : outdatafmt[i].format = CS_FMT_NULLTERM;
301 :
302 150 : coldata[i].value = (CS_CHAR *) malloc(outdatafmt[i].maxlength);
303 150 : if (coldata[i].value == NULL) {
304 0 : fprintf(stderr, "malloc coldata.value failed \n");
305 0 : return 1;
306 : }
307 150 : coldata[i].value[0] = 0;
308 :
309 150 : check_call(ct_bind, (cmd, (i + 1), &outdatafmt[i], coldata[i].value, &coldata[i].valuelen,
310 : & coldata[i].indicator));
311 : }
312 :
313 30 : ex_display_header(num_cols, outdatafmt);
314 :
315 90 : while (((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED,
316 30 : &rows_read)) == CS_SUCCEED) || (ret == CS_ROW_FAIL)) {
317 : /*
318 : * Increment our row count by the number of rows just fetched.
319 : */
320 30 : row_count = row_count + rows_read;
321 :
322 : /*
323 : * Check if we hit a recoverable error.
324 : */
325 30 : 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 150 : for (i = 0; i < num_cols; i++) {
335 : /*
336 : * Display the column value
337 : */
338 150 : printf("%s", coldata[i].value);
339 150 : fflush(stdout);
340 :
341 : /*
342 : * If not last column, Print out spaces between this
343 : * column and next one.
344 : */
345 150 : if (i != num_cols - 1) {
346 120 : disp_len = ex_display_dlen(&outdatafmt[i]);
347 120 : disp_len -= coldata[i].valuelen - 1;
348 2258 : for (j = 0; j < disp_len; j++) {
349 2138 : fputc(' ', stdout);
350 : }
351 : }
352 : }
353 30 : printf("\n");
354 30 : fflush(stdout);
355 : }
356 :
357 : /*
358 : * Free allocated space.
359 : */
360 150 : for (i = 0; i < num_cols; i++) {
361 150 : free(coldata[i].value);
362 : }
363 30 : free(coldata);
364 30 : free(outdatafmt);
365 :
366 : /*
367 : * We're done processing rows. Let's check the final return
368 : * value of ct_fetch().
369 : */
370 30 : switch ((int) ret) {
371 30 : case CS_END_DATA:
372 : /*
373 : * Everything went fine.
374 : */
375 30 : printf("All done processing rows.\n");
376 30 : 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 30 : 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 10 : 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 10 : run_command(cmd, "DROP PROCEDURE sample_rpc");
461 :
462 : if (verbose) {
463 : printf("Trying logout\n");
464 : }
465 10 : check_call(try_ctlogout, (ctx, conn, cmd, verbose));
466 :
467 10 : return 0;
468 : }
469 :
470 : static CS_INT
471 570 : ex_display_dlen(CS_DATAFMT * column)
472 : {
473 : CS_INT len;
474 :
475 570 : switch ((int) column->datatype) {
476 450 : case CS_CHAR_TYPE:
477 : case CS_VARCHAR_TYPE:
478 : case CS_TEXT_TYPE:
479 : case CS_IMAGE_TYPE:
480 450 : len = MIN(column->maxlength, 1024);
481 450 : break;
482 :
483 20 : case CS_BINARY_TYPE:
484 : case CS_VARBINARY_TYPE:
485 20 : len = MIN((2 * column->maxlength) + 2, 1024);
486 20 : break;
487 :
488 : case CS_BIT_TYPE:
489 : case CS_TINYINT_TYPE:
490 : len = 3;
491 : break;
492 :
493 20 : case CS_SMALLINT_TYPE:
494 20 : len = 6;
495 20 : break;
496 :
497 20 : case CS_INT_TYPE:
498 20 : len = 11;
499 20 : break;
500 :
501 20 : case CS_REAL_TYPE:
502 : case CS_FLOAT_TYPE:
503 20 : len = 20;
504 20 : break;
505 :
506 20 : case CS_MONEY_TYPE:
507 : case CS_MONEY4_TYPE:
508 20 : len = 24;
509 20 : break;
510 :
511 20 : case CS_DATETIME_TYPE:
512 : case CS_DATETIME4_TYPE:
513 20 : len = 30;
514 20 : 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 570 : return MAX((CS_INT) (strlen(column->name) + 1), len);
527 : }
528 :
529 : static CS_RETCODE
530 30 : 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 30 : fputc('\n', stdout);
538 180 : for (i = 0; i < numcols; i++) {
539 150 : disp_len = ex_display_dlen(&columns[i]);
540 150 : printf("%s", columns[i].name);
541 150 : fflush(stdout);
542 150 : l = disp_len - strlen(columns[i].name);
543 8410 : for (j = 0; j < l; j++) {
544 8260 : fputc(' ', stdout);
545 8260 : fflush(stdout);
546 : }
547 : }
548 30 : fputc('\n', stdout);
549 30 : fflush(stdout);
550 180 : for (i = 0; i < numcols; i++) {
551 150 : disp_len = ex_display_dlen(&columns[i]);
552 150 : l = disp_len - 1;
553 8960 : for (j = 0; j < l; j++) {
554 8810 : fputc('-', stdout);
555 : }
556 150 : fputc(' ', stdout);
557 : }
558 30 : fputc('\n', stdout);
559 :
560 30 : return CS_SUCCEED;
561 : }
|