1 | /* dynamic SQL support routines |
2 | * |
3 | * src/interfaces/ecpg/ecpglib/descriptor.c |
4 | */ |
5 | |
6 | #define POSTGRES_ECPG_INTERNAL |
7 | #include "postgres_fe.h" |
8 | |
9 | #include "catalog/pg_type_d.h" |
10 | |
11 | #include "ecpg-pthread-win32.h" |
12 | #include "ecpgtype.h" |
13 | #include "ecpglib.h" |
14 | #include "ecpgerrno.h" |
15 | #include "ecpglib_extern.h" |
16 | #include "sqlca.h" |
17 | #include "sqlda.h" |
18 | #include "sql3types.h" |
19 | |
20 | static void descriptor_free(struct descriptor *desc); |
21 | |
22 | /* We manage descriptors separately for each thread. */ |
23 | #ifdef ENABLE_THREAD_SAFETY |
24 | static pthread_key_t descriptor_key; |
25 | static pthread_once_t descriptor_once = PTHREAD_ONCE_INIT; |
26 | |
27 | static void descriptor_deallocate_all(struct descriptor *list); |
28 | |
29 | static void |
30 | descriptor_destructor(void *arg) |
31 | { |
32 | descriptor_deallocate_all(arg); |
33 | } |
34 | |
35 | static void |
36 | descriptor_key_init(void) |
37 | { |
38 | pthread_key_create(&descriptor_key, descriptor_destructor); |
39 | } |
40 | |
41 | static struct descriptor * |
42 | get_descriptors(void) |
43 | { |
44 | pthread_once(&descriptor_once, descriptor_key_init); |
45 | return (struct descriptor *) pthread_getspecific(descriptor_key); |
46 | } |
47 | |
48 | static void |
49 | set_descriptors(struct descriptor *value) |
50 | { |
51 | pthread_setspecific(descriptor_key, value); |
52 | } |
53 | #else |
54 | static struct descriptor *all_descriptors = NULL; |
55 | |
56 | #define get_descriptors() (all_descriptors) |
57 | #define set_descriptors(value) do { all_descriptors = (value); } while(0) |
58 | #endif |
59 | |
60 | /* old internal convenience function that might go away later */ |
61 | static PGresult * |
62 | ecpg_result_by_descriptor(int line, const char *name) |
63 | { |
64 | struct descriptor *desc = ecpg_find_desc(line, name); |
65 | |
66 | if (desc == NULL) |
67 | return NULL; |
68 | return desc->result; |
69 | } |
70 | |
71 | static unsigned int |
72 | ecpg_dynamic_type_DDT(Oid type) |
73 | { |
74 | switch (type) |
75 | { |
76 | case DATEOID: |
77 | return SQL3_DDT_DATE; |
78 | case TIMEOID: |
79 | return SQL3_DDT_TIME; |
80 | case TIMESTAMPOID: |
81 | return SQL3_DDT_TIMESTAMP; |
82 | case TIMESTAMPTZOID: |
83 | return SQL3_DDT_TIMESTAMP_WITH_TIME_ZONE; |
84 | case TIMETZOID: |
85 | return SQL3_DDT_TIME_WITH_TIME_ZONE; |
86 | default: |
87 | return SQL3_DDT_ILLEGAL; |
88 | } |
89 | } |
90 | |
91 | bool |
92 | (int lineno, const char *desc_name, int *count) |
93 | { |
94 | PGresult *ECPGresult; |
95 | struct sqlca_t *sqlca = ECPGget_sqlca(); |
96 | |
97 | if (sqlca == NULL) |
98 | { |
99 | ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, |
100 | ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); |
101 | return false; |
102 | } |
103 | |
104 | ecpg_init_sqlca(sqlca); |
105 | ECPGresult = ecpg_result_by_descriptor(lineno, desc_name); |
106 | if (!ECPGresult) |
107 | return false; |
108 | |
109 | *count = PQnfields(ECPGresult); |
110 | sqlca->sqlerrd[2] = 1; |
111 | ecpg_log("ECPGget_desc_header: found %d attributes\n" , *count); |
112 | return true; |
113 | } |
114 | |
115 | static bool |
116 | get_int_item(int lineno, void *var, enum ECPGttype vartype, int value) |
117 | { |
118 | switch (vartype) |
119 | { |
120 | case ECPGt_short: |
121 | *(short *) var = (short) value; |
122 | break; |
123 | case ECPGt_int: |
124 | *(int *) var = (int) value; |
125 | break; |
126 | case ECPGt_long: |
127 | *(long *) var = (long) value; |
128 | break; |
129 | case ECPGt_unsigned_short: |
130 | *(unsigned short *) var = (unsigned short) value; |
131 | break; |
132 | case ECPGt_unsigned_int: |
133 | *(unsigned int *) var = (unsigned int) value; |
134 | break; |
135 | case ECPGt_unsigned_long: |
136 | *(unsigned long *) var = (unsigned long) value; |
137 | break; |
138 | #ifdef HAVE_LONG_LONG_INT |
139 | case ECPGt_long_long: |
140 | *(long long int *) var = (long long int) value; |
141 | break; |
142 | case ECPGt_unsigned_long_long: |
143 | *(unsigned long long int *) var = (unsigned long long int) value; |
144 | break; |
145 | #endif /* HAVE_LONG_LONG_INT */ |
146 | case ECPGt_float: |
147 | *(float *) var = (float) value; |
148 | break; |
149 | case ECPGt_double: |
150 | *(double *) var = (double) value; |
151 | break; |
152 | default: |
153 | ecpg_raise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL); |
154 | return false; |
155 | } |
156 | |
157 | return true; |
158 | } |
159 | |
160 | static bool |
161 | set_int_item(int lineno, int *target, const void *var, enum ECPGttype vartype) |
162 | { |
163 | switch (vartype) |
164 | { |
165 | case ECPGt_short: |
166 | *target = *(const short *) var; |
167 | break; |
168 | case ECPGt_int: |
169 | *target = *(const int *) var; |
170 | break; |
171 | case ECPGt_long: |
172 | *target = *(const long *) var; |
173 | break; |
174 | case ECPGt_unsigned_short: |
175 | *target = *(const unsigned short *) var; |
176 | break; |
177 | case ECPGt_unsigned_int: |
178 | *target = *(const unsigned int *) var; |
179 | break; |
180 | case ECPGt_unsigned_long: |
181 | *target = *(const unsigned long *) var; |
182 | break; |
183 | #ifdef HAVE_LONG_LONG_INT |
184 | case ECPGt_long_long: |
185 | *target = *(const long long int *) var; |
186 | break; |
187 | case ECPGt_unsigned_long_long: |
188 | *target = *(const unsigned long long int *) var; |
189 | break; |
190 | #endif /* HAVE_LONG_LONG_INT */ |
191 | case ECPGt_float: |
192 | *target = *(const float *) var; |
193 | break; |
194 | case ECPGt_double: |
195 | *target = *(const double *) var; |
196 | break; |
197 | default: |
198 | ecpg_raise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL); |
199 | return false; |
200 | } |
201 | |
202 | return true; |
203 | } |
204 | |
205 | static bool |
206 | get_char_item(int lineno, void *var, enum ECPGttype vartype, char *value, int varcharsize) |
207 | { |
208 | switch (vartype) |
209 | { |
210 | case ECPGt_char: |
211 | case ECPGt_unsigned_char: |
212 | case ECPGt_string: |
213 | strncpy((char *) var, value, varcharsize); |
214 | break; |
215 | case ECPGt_varchar: |
216 | { |
217 | struct ECPGgeneric_varchar *variable = |
218 | (struct ECPGgeneric_varchar *) var; |
219 | |
220 | if (varcharsize == 0) |
221 | memcpy(variable->arr, value, strlen(value)); |
222 | else |
223 | strncpy(variable->arr, value, varcharsize); |
224 | |
225 | variable->len = strlen(value); |
226 | if (varcharsize > 0 && variable->len > varcharsize) |
227 | variable->len = varcharsize; |
228 | } |
229 | break; |
230 | default: |
231 | ecpg_raise(lineno, ECPG_VAR_NOT_CHAR, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL); |
232 | return false; |
233 | } |
234 | |
235 | return true; |
236 | } |
237 | |
238 | #define RETURN_IF_NO_DATA if (ntuples < 1) \ |
239 | { \ |
240 | va_end(args); \ |
241 | ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); \ |
242 | return false; \ |
243 | } |
244 | |
245 | bool |
246 | ECPGget_desc(int lineno, const char *desc_name, int index,...) |
247 | { |
248 | va_list args; |
249 | PGresult *ECPGresult; |
250 | enum ECPGdtype type; |
251 | int ntuples, |
252 | act_tuple; |
253 | struct variable data_var; |
254 | struct sqlca_t *sqlca = ECPGget_sqlca(); |
255 | |
256 | if (sqlca == NULL) |
257 | { |
258 | ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, |
259 | ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); |
260 | return false; |
261 | } |
262 | |
263 | va_start(args, index); |
264 | ecpg_init_sqlca(sqlca); |
265 | ECPGresult = ecpg_result_by_descriptor(lineno, desc_name); |
266 | if (!ECPGresult) |
267 | { |
268 | va_end(args); |
269 | return false; |
270 | } |
271 | |
272 | ntuples = PQntuples(ECPGresult); |
273 | |
274 | if (index < 1 || index > PQnfields(ECPGresult)) |
275 | { |
276 | ecpg_raise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX, NULL); |
277 | va_end(args); |
278 | return false; |
279 | } |
280 | |
281 | ecpg_log("ECPGget_desc: reading items for tuple %d\n" , index); |
282 | --index; |
283 | |
284 | type = va_arg(args, enum ECPGdtype); |
285 | |
286 | memset(&data_var, 0, sizeof data_var); |
287 | data_var.type = ECPGt_EORT; |
288 | data_var.ind_type = ECPGt_NO_INDICATOR; |
289 | |
290 | while (type != ECPGd_EODT) |
291 | { |
292 | char type_str[20]; |
293 | long varcharsize; |
294 | long offset; |
295 | long arrsize; |
296 | enum ECPGttype vartype; |
297 | void *var; |
298 | |
299 | vartype = va_arg(args, enum ECPGttype); |
300 | var = va_arg(args, void *); |
301 | varcharsize = va_arg(args, long); |
302 | arrsize = va_arg(args, long); |
303 | offset = va_arg(args, long); |
304 | |
305 | switch (type) |
306 | { |
307 | case (ECPGd_indicator): |
308 | RETURN_IF_NO_DATA; |
309 | data_var.ind_type = vartype; |
310 | data_var.ind_pointer = var; |
311 | data_var.ind_varcharsize = varcharsize; |
312 | data_var.ind_arrsize = arrsize; |
313 | data_var.ind_offset = offset; |
314 | if (data_var.ind_arrsize == 0 || data_var.ind_varcharsize == 0) |
315 | data_var.ind_value = *((void **) (data_var.ind_pointer)); |
316 | else |
317 | data_var.ind_value = data_var.ind_pointer; |
318 | break; |
319 | |
320 | case ECPGd_data: |
321 | RETURN_IF_NO_DATA; |
322 | data_var.type = vartype; |
323 | data_var.pointer = var; |
324 | data_var.varcharsize = varcharsize; |
325 | data_var.arrsize = arrsize; |
326 | data_var.offset = offset; |
327 | if (data_var.arrsize == 0 || data_var.varcharsize == 0) |
328 | data_var.value = *((void **) (data_var.pointer)); |
329 | else |
330 | data_var.value = data_var.pointer; |
331 | break; |
332 | |
333 | case ECPGd_name: |
334 | if (!get_char_item(lineno, var, vartype, PQfname(ECPGresult, index), varcharsize)) |
335 | { |
336 | va_end(args); |
337 | return false; |
338 | } |
339 | |
340 | ecpg_log("ECPGget_desc: NAME = %s\n" , PQfname(ECPGresult, index)); |
341 | break; |
342 | |
343 | case ECPGd_nullable: |
344 | if (!get_int_item(lineno, var, vartype, 1)) |
345 | { |
346 | va_end(args); |
347 | return false; |
348 | } |
349 | |
350 | break; |
351 | |
352 | case ECPGd_key_member: |
353 | if (!get_int_item(lineno, var, vartype, 0)) |
354 | { |
355 | va_end(args); |
356 | return false; |
357 | } |
358 | |
359 | break; |
360 | |
361 | case ECPGd_scale: |
362 | if (!get_int_item(lineno, var, vartype, (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff)) |
363 | { |
364 | va_end(args); |
365 | return false; |
366 | } |
367 | |
368 | ecpg_log("ECPGget_desc: SCALE = %d\n" , (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff); |
369 | break; |
370 | |
371 | case ECPGd_precision: |
372 | if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) >> 16)) |
373 | { |
374 | va_end(args); |
375 | return false; |
376 | } |
377 | |
378 | ecpg_log("ECPGget_desc: PRECISION = %d\n" , PQfmod(ECPGresult, index) >> 16); |
379 | break; |
380 | |
381 | case ECPGd_octet: |
382 | if (!get_int_item(lineno, var, vartype, PQfsize(ECPGresult, index))) |
383 | { |
384 | va_end(args); |
385 | return false; |
386 | } |
387 | |
388 | ecpg_log("ECPGget_desc: OCTET_LENGTH = %d\n" , PQfsize(ECPGresult, index)); |
389 | break; |
390 | |
391 | case ECPGd_length: |
392 | if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) - VARHDRSZ)) |
393 | { |
394 | va_end(args); |
395 | return false; |
396 | } |
397 | |
398 | ecpg_log("ECPGget_desc: LENGTH = %d\n" , PQfmod(ECPGresult, index) - VARHDRSZ); |
399 | break; |
400 | |
401 | case ECPGd_type: |
402 | if (!get_int_item(lineno, var, vartype, ecpg_dynamic_type(PQftype(ECPGresult, index)))) |
403 | { |
404 | va_end(args); |
405 | return false; |
406 | } |
407 | |
408 | ecpg_log("ECPGget_desc: TYPE = %d\n" , ecpg_dynamic_type(PQftype(ECPGresult, index))); |
409 | break; |
410 | |
411 | case ECPGd_di_code: |
412 | if (!get_int_item(lineno, var, vartype, ecpg_dynamic_type_DDT(PQftype(ECPGresult, index)))) |
413 | { |
414 | va_end(args); |
415 | return false; |
416 | } |
417 | |
418 | ecpg_log("ECPGget_desc: TYPE = %d\n" , ecpg_dynamic_type_DDT(PQftype(ECPGresult, index))); |
419 | break; |
420 | |
421 | case ECPGd_cardinality: |
422 | if (!get_int_item(lineno, var, vartype, PQntuples(ECPGresult))) |
423 | { |
424 | va_end(args); |
425 | return false; |
426 | } |
427 | |
428 | ecpg_log("ECPGget_desc: CARDINALITY = %d\n" , PQntuples(ECPGresult)); |
429 | break; |
430 | |
431 | case ECPGd_ret_length: |
432 | case ECPGd_ret_octet: |
433 | |
434 | RETURN_IF_NO_DATA; |
435 | |
436 | /* |
437 | * this is like ECPGstore_result |
438 | */ |
439 | if (arrsize > 0 && ntuples > arrsize) |
440 | { |
441 | ecpg_log("ECPGget_desc on line %d: incorrect number of matches; %d don't fit into array of %ld\n" , |
442 | lineno, ntuples, arrsize); |
443 | ecpg_raise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL); |
444 | va_end(args); |
445 | return false; |
446 | } |
447 | /* allocate storage if needed */ |
448 | if (arrsize == 0 && *(void **) var == NULL) |
449 | { |
450 | void *mem = (void *) ecpg_auto_alloc(offset * ntuples, lineno); |
451 | |
452 | if (!mem) |
453 | { |
454 | va_end(args); |
455 | return false; |
456 | } |
457 | *(void **) var = mem; |
458 | var = mem; |
459 | } |
460 | |
461 | for (act_tuple = 0; act_tuple < ntuples; act_tuple++) |
462 | { |
463 | if (!get_int_item(lineno, var, vartype, PQgetlength(ECPGresult, act_tuple, index))) |
464 | { |
465 | va_end(args); |
466 | return false; |
467 | } |
468 | var = (char *) var + offset; |
469 | ecpg_log("ECPGget_desc: RETURNED[%d] = %d\n" , act_tuple, PQgetlength(ECPGresult, act_tuple, index)); |
470 | } |
471 | break; |
472 | |
473 | default: |
474 | snprintf(type_str, sizeof(type_str), "%d" , type); |
475 | ecpg_raise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str); |
476 | va_end(args); |
477 | return false; |
478 | } |
479 | |
480 | type = va_arg(args, enum ECPGdtype); |
481 | } |
482 | |
483 | if (data_var.type != ECPGt_EORT) |
484 | { |
485 | struct statement stmt; |
486 | |
487 | memset(&stmt, 0, sizeof stmt); |
488 | stmt.lineno = lineno; |
489 | |
490 | /* Make sure we do NOT honor the locale for numeric input */ |
491 | /* since the database gives the standard decimal point */ |
492 | /* (see comments in execute.c) */ |
493 | #ifdef HAVE_USELOCALE |
494 | stmt.clocale = newlocale(LC_NUMERIC_MASK, "C" , (locale_t) 0); |
495 | if (stmt.clocale != (locale_t) 0) |
496 | stmt.oldlocale = uselocale(stmt.clocale); |
497 | #else |
498 | #ifdef HAVE__CONFIGTHREADLOCALE |
499 | stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); |
500 | #endif |
501 | stmt.oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno); |
502 | setlocale(LC_NUMERIC, "C" ); |
503 | #endif |
504 | |
505 | /* desperate try to guess something sensible */ |
506 | stmt.connection = ecpg_get_connection(NULL); |
507 | ecpg_store_result(ECPGresult, index, &stmt, &data_var); |
508 | |
509 | #ifdef HAVE_USELOCALE |
510 | if (stmt.oldlocale != (locale_t) 0) |
511 | uselocale(stmt.oldlocale); |
512 | if (stmt.clocale) |
513 | freelocale(stmt.clocale); |
514 | #else |
515 | if (stmt.oldlocale) |
516 | { |
517 | setlocale(LC_NUMERIC, stmt.oldlocale); |
518 | ecpg_free(stmt.oldlocale); |
519 | } |
520 | #ifdef HAVE__CONFIGTHREADLOCALE |
521 | if (stmt.oldthreadlocale != -1) |
522 | (void) _configthreadlocale(stmt.oldthreadlocale); |
523 | #endif |
524 | #endif |
525 | } |
526 | else if (data_var.ind_type != ECPGt_NO_INDICATOR && data_var.ind_pointer != NULL) |
527 | |
528 | /* |
529 | * ind_type != NO_INDICATOR should always have ind_pointer != NULL but |
530 | * since this might be changed manually in the .c file let's play it |
531 | * safe |
532 | */ |
533 | { |
534 | /* |
535 | * this is like ECPGstore_result but since we don't have a data |
536 | * variable at hand, we can't call it |
537 | */ |
538 | if (data_var.ind_arrsize > 0 && ntuples > data_var.ind_arrsize) |
539 | { |
540 | ecpg_log("ECPGget_desc on line %d: incorrect number of matches (indicator); %d don't fit into array of %ld\n" , |
541 | lineno, ntuples, data_var.ind_arrsize); |
542 | ecpg_raise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL); |
543 | va_end(args); |
544 | return false; |
545 | } |
546 | |
547 | /* allocate storage if needed */ |
548 | if (data_var.ind_arrsize == 0 && data_var.ind_value == NULL) |
549 | { |
550 | void *mem = (void *) ecpg_auto_alloc(data_var.ind_offset * ntuples, lineno); |
551 | |
552 | if (!mem) |
553 | { |
554 | va_end(args); |
555 | return false; |
556 | } |
557 | *(void **) data_var.ind_pointer = mem; |
558 | data_var.ind_value = mem; |
559 | } |
560 | |
561 | for (act_tuple = 0; act_tuple < ntuples; act_tuple++) |
562 | { |
563 | if (!get_int_item(lineno, data_var.ind_value, data_var.ind_type, -PQgetisnull(ECPGresult, act_tuple, index))) |
564 | { |
565 | va_end(args); |
566 | return false; |
567 | } |
568 | data_var.ind_value = (char *) data_var.ind_value + data_var.ind_offset; |
569 | ecpg_log("ECPGget_desc: INDICATOR[%d] = %d\n" , act_tuple, -PQgetisnull(ECPGresult, act_tuple, index)); |
570 | } |
571 | } |
572 | sqlca->sqlerrd[2] = ntuples; |
573 | va_end(args); |
574 | return true; |
575 | } |
576 | |
577 | #undef RETURN_IF_NO_DATA |
578 | |
579 | bool |
580 | (int lineno, const char *desc_name, int count) |
581 | { |
582 | struct descriptor *desc = ecpg_find_desc(lineno, desc_name); |
583 | |
584 | if (desc == NULL) |
585 | return false; |
586 | desc->count = count; |
587 | return true; |
588 | } |
589 | |
590 | static void |
591 | set_desc_attr(struct descriptor_item *desc_item, struct variable *var, |
592 | char *tobeinserted) |
593 | { |
594 | if (var->type != ECPGt_bytea) |
595 | desc_item->is_binary = false; |
596 | |
597 | else |
598 | { |
599 | struct ECPGgeneric_varchar *variable = |
600 | (struct ECPGgeneric_varchar *) (var->value); |
601 | |
602 | desc_item->is_binary = true; |
603 | desc_item->data_len = variable->len; |
604 | } |
605 | |
606 | ecpg_free(desc_item->data); /* free() takes care of a potential NULL value */ |
607 | desc_item->data = (char *) tobeinserted; |
608 | } |
609 | |
610 | |
611 | bool |
612 | ECPGset_desc(int lineno, const char *desc_name, int index,...) |
613 | { |
614 | va_list args; |
615 | struct descriptor *desc; |
616 | struct descriptor_item *desc_item; |
617 | struct variable *var; |
618 | |
619 | desc = ecpg_find_desc(lineno, desc_name); |
620 | if (desc == NULL) |
621 | return false; |
622 | |
623 | for (desc_item = desc->items; desc_item; desc_item = desc_item->next) |
624 | { |
625 | if (desc_item->num == index) |
626 | break; |
627 | } |
628 | |
629 | if (desc_item == NULL) |
630 | { |
631 | desc_item = (struct descriptor_item *) ecpg_alloc(sizeof(*desc_item), lineno); |
632 | if (!desc_item) |
633 | return false; |
634 | desc_item->num = index; |
635 | if (desc->count < index) |
636 | desc->count = index; |
637 | desc_item->next = desc->items; |
638 | desc->items = desc_item; |
639 | } |
640 | |
641 | if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno))) |
642 | return false; |
643 | |
644 | va_start(args, index); |
645 | |
646 | for (;;) |
647 | { |
648 | enum ECPGdtype itemtype; |
649 | char *tobeinserted = NULL; |
650 | |
651 | itemtype = va_arg(args, enum ECPGdtype); |
652 | |
653 | if (itemtype == ECPGd_EODT) |
654 | break; |
655 | |
656 | var->type = va_arg(args, enum ECPGttype); |
657 | var->pointer = va_arg(args, char *); |
658 | |
659 | var->varcharsize = va_arg(args, long); |
660 | var->arrsize = va_arg(args, long); |
661 | var->offset = va_arg(args, long); |
662 | |
663 | if (var->arrsize == 0 || var->varcharsize == 0) |
664 | var->value = *((char **) (var->pointer)); |
665 | else |
666 | var->value = var->pointer; |
667 | |
668 | /* |
669 | * negative values are used to indicate an array without given bounds |
670 | */ |
671 | /* reset to zero for us */ |
672 | if (var->arrsize < 0) |
673 | var->arrsize = 0; |
674 | if (var->varcharsize < 0) |
675 | var->varcharsize = 0; |
676 | |
677 | var->next = NULL; |
678 | |
679 | switch (itemtype) |
680 | { |
681 | case ECPGd_data: |
682 | { |
683 | if (!ecpg_store_input(lineno, true, var, &tobeinserted, false)) |
684 | { |
685 | ecpg_free(var); |
686 | va_end(args); |
687 | return false; |
688 | } |
689 | |
690 | set_desc_attr(desc_item, var, tobeinserted); |
691 | tobeinserted = NULL; |
692 | break; |
693 | } |
694 | |
695 | case ECPGd_indicator: |
696 | set_int_item(lineno, &desc_item->indicator, var->pointer, var->type); |
697 | break; |
698 | |
699 | case ECPGd_length: |
700 | set_int_item(lineno, &desc_item->length, var->pointer, var->type); |
701 | break; |
702 | |
703 | case ECPGd_precision: |
704 | set_int_item(lineno, &desc_item->precision, var->pointer, var->type); |
705 | break; |
706 | |
707 | case ECPGd_scale: |
708 | set_int_item(lineno, &desc_item->scale, var->pointer, var->type); |
709 | break; |
710 | |
711 | case ECPGd_type: |
712 | set_int_item(lineno, &desc_item->type, var->pointer, var->type); |
713 | break; |
714 | |
715 | default: |
716 | { |
717 | char type_str[20]; |
718 | |
719 | snprintf(type_str, sizeof(type_str), "%d" , itemtype); |
720 | ecpg_raise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str); |
721 | ecpg_free(var); |
722 | va_end(args); |
723 | return false; |
724 | } |
725 | } |
726 | } |
727 | ecpg_free(var); |
728 | va_end(args); |
729 | |
730 | return true; |
731 | } |
732 | |
733 | /* Free the descriptor and items in it. */ |
734 | static void |
735 | descriptor_free(struct descriptor *desc) |
736 | { |
737 | struct descriptor_item *desc_item; |
738 | |
739 | for (desc_item = desc->items; desc_item;) |
740 | { |
741 | struct descriptor_item *di; |
742 | |
743 | ecpg_free(desc_item->data); |
744 | di = desc_item; |
745 | desc_item = desc_item->next; |
746 | ecpg_free(di); |
747 | } |
748 | |
749 | ecpg_free(desc->name); |
750 | PQclear(desc->result); |
751 | ecpg_free(desc); |
752 | } |
753 | |
754 | bool |
755 | ECPGdeallocate_desc(int line, const char *name) |
756 | { |
757 | struct descriptor *desc; |
758 | struct descriptor *prev; |
759 | struct sqlca_t *sqlca = ECPGget_sqlca(); |
760 | |
761 | if (sqlca == NULL) |
762 | { |
763 | ecpg_raise(line, ECPG_OUT_OF_MEMORY, |
764 | ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); |
765 | return false; |
766 | } |
767 | |
768 | ecpg_init_sqlca(sqlca); |
769 | for (desc = get_descriptors(), prev = NULL; desc; prev = desc, desc = desc->next) |
770 | { |
771 | if (strcmp(name, desc->name) == 0) |
772 | { |
773 | if (prev) |
774 | prev->next = desc->next; |
775 | else |
776 | set_descriptors(desc->next); |
777 | descriptor_free(desc); |
778 | return true; |
779 | } |
780 | } |
781 | ecpg_raise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, name); |
782 | return false; |
783 | } |
784 | |
785 | #ifdef ENABLE_THREAD_SAFETY |
786 | |
787 | /* Deallocate all descriptors in the list */ |
788 | static void |
789 | descriptor_deallocate_all(struct descriptor *list) |
790 | { |
791 | while (list) |
792 | { |
793 | struct descriptor *next = list->next; |
794 | |
795 | descriptor_free(list); |
796 | list = next; |
797 | } |
798 | } |
799 | #endif /* ENABLE_THREAD_SAFETY */ |
800 | |
801 | bool |
802 | ECPGallocate_desc(int line, const char *name) |
803 | { |
804 | struct descriptor *new; |
805 | struct sqlca_t *sqlca = ECPGget_sqlca(); |
806 | |
807 | if (sqlca == NULL) |
808 | { |
809 | ecpg_raise(line, ECPG_OUT_OF_MEMORY, |
810 | ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); |
811 | return false; |
812 | } |
813 | |
814 | ecpg_init_sqlca(sqlca); |
815 | new = (struct descriptor *) ecpg_alloc(sizeof(struct descriptor), line); |
816 | if (!new) |
817 | return false; |
818 | new->next = get_descriptors(); |
819 | new->name = ecpg_alloc(strlen(name) + 1, line); |
820 | if (!new->name) |
821 | { |
822 | ecpg_free(new); |
823 | return false; |
824 | } |
825 | new->count = -1; |
826 | new->items = NULL; |
827 | new->result = PQmakeEmptyPGresult(NULL, 0); |
828 | if (!new->result) |
829 | { |
830 | ecpg_free(new->name); |
831 | ecpg_free(new); |
832 | ecpg_raise(line, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); |
833 | return false; |
834 | } |
835 | strcpy(new->name, name); |
836 | set_descriptors(new); |
837 | return true; |
838 | } |
839 | |
840 | /* Find descriptor with name in the connection. */ |
841 | struct descriptor * |
842 | ecpg_find_desc(int line, const char *name) |
843 | { |
844 | struct descriptor *desc; |
845 | |
846 | for (desc = get_descriptors(); desc; desc = desc->next) |
847 | { |
848 | if (strcmp(name, desc->name) == 0) |
849 | return desc; |
850 | } |
851 | |
852 | ecpg_raise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, name); |
853 | return NULL; /* not found */ |
854 | } |
855 | |
856 | bool |
857 | ECPGdescribe(int line, int compat, bool input, const char *connection_name, const char *stmt_name,...) |
858 | { |
859 | bool ret = false; |
860 | struct connection *con; |
861 | struct prepared_statement *prep; |
862 | PGresult *res; |
863 | va_list args; |
864 | |
865 | /* DESCRIBE INPUT is not yet supported */ |
866 | if (input) |
867 | { |
868 | ecpg_raise(line, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, "DESCRIBE INPUT" ); |
869 | return ret; |
870 | } |
871 | |
872 | con = ecpg_get_connection(connection_name); |
873 | if (!con) |
874 | { |
875 | ecpg_raise(line, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST, |
876 | connection_name ? connection_name : ecpg_gettext("NULL" )); |
877 | return ret; |
878 | } |
879 | prep = ecpg_find_prepared_statement(stmt_name, con, NULL); |
880 | if (!prep) |
881 | { |
882 | ecpg_raise(line, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, stmt_name); |
883 | return ret; |
884 | } |
885 | |
886 | va_start(args, stmt_name); |
887 | |
888 | for (;;) |
889 | { |
890 | enum ECPGttype type; |
891 | void *ptr; |
892 | |
893 | /* variable type */ |
894 | type = va_arg(args, enum ECPGttype); |
895 | |
896 | if (type == ECPGt_EORT) |
897 | break; |
898 | |
899 | /* rest of variable parameters */ |
900 | ptr = va_arg(args, void *); |
901 | (void) va_arg(args, long); /* skip args */ |
902 | (void) va_arg(args, long); |
903 | (void) va_arg(args, long); |
904 | |
905 | /* variable indicator */ |
906 | (void) va_arg(args, enum ECPGttype); |
907 | (void) va_arg(args, void *); /* skip args */ |
908 | (void) va_arg(args, long); |
909 | (void) va_arg(args, long); |
910 | (void) va_arg(args, long); |
911 | |
912 | switch (type) |
913 | { |
914 | case ECPGt_descriptor: |
915 | { |
916 | char *name = ptr; |
917 | struct descriptor *desc = ecpg_find_desc(line, name); |
918 | |
919 | if (desc == NULL) |
920 | break; |
921 | |
922 | res = PQdescribePrepared(con->connection, stmt_name); |
923 | if (!ecpg_check_PQresult(res, line, con->connection, compat)) |
924 | break; |
925 | |
926 | if (desc->result != NULL) |
927 | PQclear(desc->result); |
928 | |
929 | desc->result = res; |
930 | ret = true; |
931 | break; |
932 | } |
933 | case ECPGt_sqlda: |
934 | { |
935 | if (INFORMIX_MODE(compat)) |
936 | { |
937 | struct sqlda_compat **_sqlda = ptr; |
938 | struct sqlda_compat *sqlda; |
939 | |
940 | res = PQdescribePrepared(con->connection, stmt_name); |
941 | if (!ecpg_check_PQresult(res, line, con->connection, compat)) |
942 | break; |
943 | |
944 | sqlda = ecpg_build_compat_sqlda(line, res, -1, compat); |
945 | if (sqlda) |
946 | { |
947 | struct sqlda_compat *sqlda_old = *_sqlda; |
948 | struct sqlda_compat *sqlda_old1; |
949 | |
950 | while (sqlda_old) |
951 | { |
952 | sqlda_old1 = sqlda_old->desc_next; |
953 | free(sqlda_old); |
954 | sqlda_old = sqlda_old1; |
955 | } |
956 | |
957 | *_sqlda = sqlda; |
958 | ret = true; |
959 | } |
960 | |
961 | PQclear(res); |
962 | } |
963 | else |
964 | { |
965 | struct sqlda_struct **_sqlda = ptr; |
966 | struct sqlda_struct *sqlda; |
967 | |
968 | res = PQdescribePrepared(con->connection, stmt_name); |
969 | if (!ecpg_check_PQresult(res, line, con->connection, compat)) |
970 | break; |
971 | |
972 | sqlda = ecpg_build_native_sqlda(line, res, -1, compat); |
973 | if (sqlda) |
974 | { |
975 | struct sqlda_struct *sqlda_old = *_sqlda; |
976 | struct sqlda_struct *sqlda_old1; |
977 | |
978 | while (sqlda_old) |
979 | { |
980 | sqlda_old1 = sqlda_old->desc_next; |
981 | free(sqlda_old); |
982 | sqlda_old = sqlda_old1; |
983 | } |
984 | |
985 | *_sqlda = sqlda; |
986 | ret = true; |
987 | } |
988 | |
989 | PQclear(res); |
990 | } |
991 | break; |
992 | } |
993 | default: |
994 | /* nothing else may come */ |
995 | ; |
996 | } |
997 | } |
998 | |
999 | va_end(args); |
1000 | |
1001 | return ret; |
1002 | } |
1003 | |