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