1/*
2 * SQLDA support routines
3 *
4 * The allocated memory area pointed by an sqlda pointer
5 * contains both the metadata and the data, so freeing up
6 * is a simple free(sqlda) as expected by the ESQL/C examples.
7 */
8
9#define POSTGRES_ECPG_INTERNAL
10#include "postgres_fe.h"
11
12#include "catalog/pg_type_d.h"
13
14#include "ecpg-pthread-win32.h"
15#include "decimal.h"
16#include "ecpgtype.h"
17#include "ecpglib.h"
18#include "ecpgerrno.h"
19#include "ecpglib_extern.h"
20#include "sqlca.h"
21#include "sqlda-native.h"
22#include "sqlda-compat.h"
23
24/*
25 * Compute the next variable's offset with
26 * the current variable's size and alignment.
27 *
28 *
29 * Returns:
30 * - the current variable's offset in *current
31 * - the next variable's offset in *next
32 */
33static void
34ecpg_sqlda_align_add_size(long offset, int alignment, int size, long *current, long *next)
35{
36 if (offset % alignment)
37 offset += alignment - (offset % alignment);
38 if (current)
39 *current = offset;
40 offset += size;
41 if (next)
42 *next = offset;
43}
44
45static long
46sqlda_compat_empty_size(const PGresult *res)
47{
48 long offset;
49 int i;
50 int sqld = PQnfields(res);
51
52 /* Initial size to store main structure and field structures */
53 offset = sizeof(struct sqlda_compat) + sqld * sizeof(struct sqlvar_compat);
54
55 /* Add space for field names */
56 for (i = 0; i < sqld; i++)
57 offset += strlen(PQfname(res, i)) + 1;
58
59 /* Add padding to the first field value */
60 ecpg_sqlda_align_add_size(offset, sizeof(int), 0, &offset, NULL);
61
62 return offset;
63}
64
65static long
66sqlda_common_total_size(const PGresult *res, int row, enum COMPAT_MODE compat, long offset)
67{
68 int sqld = PQnfields(res);
69 int i;
70 long next_offset;
71
72 /* Add space for the field values */
73 for (i = 0; i < sqld; i++)
74 {
75 enum ECPGttype type = sqlda_dynamic_type(PQftype(res, i), compat);
76
77 switch (type)
78 {
79 case ECPGt_short:
80 case ECPGt_unsigned_short:
81 ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset);
82 break;
83 case ECPGt_int:
84 case ECPGt_unsigned_int:
85 ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset);
86 break;
87 case ECPGt_long:
88 case ECPGt_unsigned_long:
89 ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset);
90 break;
91 case ECPGt_long_long:
92 case ECPGt_unsigned_long_long:
93 ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset);
94 break;
95 case ECPGt_bool:
96 ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset);
97 break;
98 case ECPGt_float:
99 ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset);
100 break;
101 case ECPGt_double:
102 ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset);
103 break;
104 case ECPGt_decimal:
105 ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset);
106 break;
107 case ECPGt_numeric:
108
109 /*
110 * We align the numeric struct to allow it to store a pointer,
111 * while the digits array is aligned to int (which seems like
112 * overkill, but let's keep compatibility here).
113 *
114 * Unfortunately we need to deconstruct the value twice to
115 * find out the digits array's size and then later fill it.
116 */
117 ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset);
118 if (!PQgetisnull(res, row, i))
119 {
120 char *val = PQgetvalue(res, row, i);
121 numeric *num;
122
123 num = PGTYPESnumeric_from_asc(val, NULL);
124 if (!num)
125 break;
126 if (num->buf)
127 ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset);
128 PGTYPESnumeric_free(num);
129 }
130 break;
131 case ECPGt_date:
132 ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset);
133 break;
134 case ECPGt_timestamp:
135 ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset);
136 break;
137 case ECPGt_interval:
138 ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset);
139 break;
140 case ECPGt_char:
141 case ECPGt_unsigned_char:
142 case ECPGt_string:
143 default:
144 {
145 long datalen = strlen(PQgetvalue(res, row, i)) + 1;
146
147 ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset);
148 break;
149 }
150 }
151 offset = next_offset;
152 }
153 return offset;
154}
155
156
157static long
158sqlda_compat_total_size(const PGresult *res, int row, enum COMPAT_MODE compat)
159{
160 long offset;
161
162 offset = sqlda_compat_empty_size(res);
163
164 if (row < 0)
165 return offset;
166
167 offset = sqlda_common_total_size(res, row, compat, offset);
168 return offset;
169}
170
171static long
172sqlda_native_empty_size(const PGresult *res)
173{
174 long offset;
175 int sqld = PQnfields(res);
176
177 /* Initial size to store main structure and field structures */
178 offset = sizeof(struct sqlda_struct) + (sqld - 1) * sizeof(struct sqlvar_struct);
179
180 /* Add padding to the first field value */
181 ecpg_sqlda_align_add_size(offset, sizeof(int), 0, &offset, NULL);
182
183 return offset;
184}
185
186static long
187sqlda_native_total_size(const PGresult *res, int row, enum COMPAT_MODE compat)
188{
189 long offset;
190
191 offset = sqlda_native_empty_size(res);
192
193 if (row < 0)
194 return offset;
195
196 offset = sqlda_common_total_size(res, row, compat, offset);
197 return offset;
198}
199
200/*
201 * Build "struct sqlda_compat" (metadata only) from PGresult
202 * leaving enough space for the field values in
203 * the given row number
204 */
205struct sqlda_compat *
206ecpg_build_compat_sqlda(int line, PGresult *res, int row, enum COMPAT_MODE compat)
207{
208 struct sqlda_compat *sqlda;
209 struct sqlvar_compat *sqlvar;
210 char *fname;
211 long size;
212 int sqld;
213 int i;
214
215 size = sqlda_compat_total_size(res, row, compat);
216 sqlda = (struct sqlda_compat *) ecpg_alloc(size, line);
217 if (!sqlda)
218 return NULL;
219
220 memset(sqlda, 0, size);
221 sqlvar = (struct sqlvar_compat *) (sqlda + 1);
222 sqld = PQnfields(res);
223 fname = (char *) (sqlvar + sqld);
224
225 sqlda->sqld = sqld;
226 ecpg_log("ecpg_build_compat_sqlda on line %d sqld = %d\n", line, sqld);
227 sqlda->desc_occ = size; /* cheat here, keep the full allocated size */
228 sqlda->sqlvar = sqlvar;
229
230 for (i = 0; i < sqlda->sqld; i++)
231 {
232 sqlda->sqlvar[i].sqltype = sqlda_dynamic_type(PQftype(res, i), compat);
233 strcpy(fname, PQfname(res, i));
234 sqlda->sqlvar[i].sqlname = fname;
235 fname += strlen(sqlda->sqlvar[i].sqlname) + 1;
236
237 /*
238 * this is reserved for future use, so we leave it empty for the time
239 * being
240 */
241 /* sqlda->sqlvar[i].sqlformat = (char *) (long) PQfformat(res, i); */
242 sqlda->sqlvar[i].sqlxid = PQftype(res, i);
243 sqlda->sqlvar[i].sqltypelen = PQfsize(res, i);
244 }
245
246 return sqlda;
247}
248
249/*
250 * Sets values from PGresult.
251 */
252static int16 value_is_null = -1;
253static int16 value_is_not_null = 0;
254
255void
256ecpg_set_compat_sqlda(int lineno, struct sqlda_compat **_sqlda, const PGresult *res, int row, enum COMPAT_MODE compat)
257{
258 struct sqlda_compat *sqlda = (*_sqlda);
259 int i;
260 long offset,
261 next_offset;
262
263 if (row < 0)
264 return;
265
266 /* Offset for the first field value */
267 offset = sqlda_compat_empty_size(res);
268
269 /*
270 * Set sqlvar[i]->sqldata pointers and convert values to correct format
271 */
272 for (i = 0; i < sqlda->sqld; i++)
273 {
274 int isnull;
275 int datalen;
276 bool set_data = true;
277
278 switch (sqlda->sqlvar[i].sqltype)
279 {
280 case ECPGt_short:
281 case ECPGt_unsigned_short:
282 ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset);
283 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
284 sqlda->sqlvar[i].sqllen = sizeof(short);
285 break;
286 case ECPGt_int:
287 case ECPGt_unsigned_int:
288 ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset);
289 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
290 sqlda->sqlvar[i].sqllen = sizeof(int);
291 break;
292 case ECPGt_long:
293 case ECPGt_unsigned_long:
294 ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset);
295 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
296 sqlda->sqlvar[i].sqllen = sizeof(long);
297 break;
298 case ECPGt_long_long:
299 case ECPGt_unsigned_long_long:
300 ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset);
301 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
302 sqlda->sqlvar[i].sqllen = sizeof(long long);
303 break;
304 case ECPGt_bool:
305 ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset);
306 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
307 sqlda->sqlvar[i].sqllen = sizeof(bool);
308 break;
309 case ECPGt_float:
310 ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset);
311 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
312 sqlda->sqlvar[i].sqllen = sizeof(float);
313 break;
314 case ECPGt_double:
315 ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset);
316 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
317 sqlda->sqlvar[i].sqllen = sizeof(double);
318 break;
319 case ECPGt_decimal:
320 ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset);
321 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
322 sqlda->sqlvar[i].sqllen = sizeof(decimal);
323 break;
324 case ECPGt_numeric:
325 {
326 numeric *num;
327 char *val;
328
329 set_data = false;
330
331 ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset);
332 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
333 sqlda->sqlvar[i].sqllen = sizeof(numeric);
334
335 if (PQgetisnull(res, row, i))
336 {
337 ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata);
338 break;
339 }
340
341 val = PQgetvalue(res, row, i);
342 num = PGTYPESnumeric_from_asc(val, NULL);
343 if (!num)
344 {
345 ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata);
346 break;
347 }
348
349 memcpy(sqlda->sqlvar[i].sqldata, num, sizeof(numeric));
350
351 if (num->buf)
352 {
353 ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset);
354 memcpy((char *) sqlda + offset, num->buf, num->digits - num->buf + num->ndigits);
355
356 ((numeric *) sqlda->sqlvar[i].sqldata)->buf = (NumericDigit *) sqlda + offset;
357 ((numeric *) sqlda->sqlvar[i].sqldata)->digits = (NumericDigit *) sqlda + offset + (num->digits - num->buf);
358 }
359
360 PGTYPESnumeric_free(num);
361
362 break;
363 }
364 case ECPGt_date:
365 ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset);
366 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
367 sqlda->sqlvar[i].sqllen = sizeof(date);
368 break;
369 case ECPGt_timestamp:
370 ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset);
371 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
372 sqlda->sqlvar[i].sqllen = sizeof(timestamp);
373 break;
374 case ECPGt_interval:
375 ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset);
376 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
377 sqlda->sqlvar[i].sqllen = sizeof(interval);
378 break;
379 case ECPGt_char:
380 case ECPGt_unsigned_char:
381 case ECPGt_string:
382 default:
383 datalen = strlen(PQgetvalue(res, row, i)) + 1;
384 ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset);
385 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
386 sqlda->sqlvar[i].sqllen = datalen;
387 if (datalen > 32768)
388 sqlda->sqlvar[i].sqlilongdata = sqlda->sqlvar[i].sqldata;
389 break;
390 }
391
392 isnull = PQgetisnull(res, row, i);
393 ecpg_log("ecpg_set_compat_sqlda on line %d row %d col %d %s\n", lineno, row, i, isnull ? "IS NULL" : "IS NOT NULL");
394 sqlda->sqlvar[i].sqlind = isnull ? &value_is_null : &value_is_not_null;
395 sqlda->sqlvar[i].sqlitype = ECPGt_short;
396 sqlda->sqlvar[i].sqlilen = sizeof(short);
397 if (!isnull)
398 {
399 if (set_data)
400 ecpg_get_data(res, row, i, lineno,
401 sqlda->sqlvar[i].sqltype, ECPGt_NO_INDICATOR,
402 sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0,
403 ECPG_ARRAY_NONE, compat, false);
404 }
405 else
406 ECPGset_noind_null(sqlda->sqlvar[i].sqltype, sqlda->sqlvar[i].sqldata);
407
408 offset = next_offset;
409 }
410}
411
412struct sqlda_struct *
413ecpg_build_native_sqlda(int line, PGresult *res, int row, enum COMPAT_MODE compat)
414{
415 struct sqlda_struct *sqlda;
416 long size;
417 int i;
418
419 size = sqlda_native_total_size(res, row, compat);
420 sqlda = (struct sqlda_struct *) ecpg_alloc(size, line);
421 if (!sqlda)
422 return NULL;
423
424 memset(sqlda, 0, size);
425
426 sprintf(sqlda->sqldaid, "SQLDA ");
427 sqlda->sqld = sqlda->sqln = PQnfields(res);
428 ecpg_log("ecpg_build_native_sqlda on line %d sqld = %d\n", line, sqlda->sqld);
429 sqlda->sqldabc = sizeof(struct sqlda_struct) + (sqlda->sqld - 1) * sizeof(struct sqlvar_struct);
430
431 for (i = 0; i < sqlda->sqld; i++)
432 {
433 char *fname;
434
435 sqlda->sqlvar[i].sqltype = sqlda_dynamic_type(PQftype(res, i), compat);
436 fname = PQfname(res, i);
437 sqlda->sqlvar[i].sqlname.length = strlen(fname);
438 strcpy(sqlda->sqlvar[i].sqlname.data, fname);
439 }
440
441 return sqlda;
442}
443
444void
445ecpg_set_native_sqlda(int lineno, struct sqlda_struct **_sqlda, const PGresult *res, int row, enum COMPAT_MODE compat)
446{
447 struct sqlda_struct *sqlda = (*_sqlda);
448 int i;
449 long offset,
450 next_offset;
451
452 if (row < 0)
453 return;
454
455 /* Offset for the first field value */
456 offset = sqlda_native_empty_size(res);
457
458 /*
459 * Set sqlvar[i]->sqldata pointers and convert values to correct format
460 */
461 for (i = 0; i < sqlda->sqld; i++)
462 {
463 int isnull;
464 int datalen;
465 bool set_data = true;
466
467 switch (sqlda->sqlvar[i].sqltype)
468 {
469 case ECPGt_short:
470 case ECPGt_unsigned_short:
471 ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset);
472 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
473 sqlda->sqlvar[i].sqllen = sizeof(short);
474 break;
475 case ECPGt_int:
476 case ECPGt_unsigned_int:
477 ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset);
478 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
479 sqlda->sqlvar[i].sqllen = sizeof(int);
480 break;
481 case ECPGt_long:
482 case ECPGt_unsigned_long:
483 ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset);
484 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
485 sqlda->sqlvar[i].sqllen = sizeof(long);
486 break;
487 case ECPGt_long_long:
488 case ECPGt_unsigned_long_long:
489 ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset);
490 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
491 sqlda->sqlvar[i].sqllen = sizeof(long long);
492 break;
493 case ECPGt_bool:
494 ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset);
495 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
496 sqlda->sqlvar[i].sqllen = sizeof(bool);
497 break;
498 case ECPGt_float:
499 ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset);
500 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
501 sqlda->sqlvar[i].sqllen = sizeof(float);
502 break;
503 case ECPGt_double:
504 ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset);
505 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
506 sqlda->sqlvar[i].sqllen = sizeof(double);
507 break;
508 case ECPGt_decimal:
509 ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset);
510 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
511 sqlda->sqlvar[i].sqllen = sizeof(decimal);
512 break;
513 case ECPGt_numeric:
514 {
515 numeric *num;
516 char *val;
517
518 set_data = false;
519
520 ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset);
521 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
522 sqlda->sqlvar[i].sqllen = sizeof(numeric);
523
524 if (PQgetisnull(res, row, i))
525 {
526 ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata);
527 break;
528 }
529
530 val = PQgetvalue(res, row, i);
531 num = PGTYPESnumeric_from_asc(val, NULL);
532 if (!num)
533 {
534 ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata);
535 break;
536 }
537
538 memcpy(sqlda->sqlvar[i].sqldata, num, sizeof(numeric));
539
540 if (num->buf)
541 {
542 ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset);
543 memcpy((char *) sqlda + offset, num->buf, num->digits - num->buf + num->ndigits);
544
545 ((numeric *) sqlda->sqlvar[i].sqldata)->buf = (NumericDigit *) sqlda + offset;
546 ((numeric *) sqlda->sqlvar[i].sqldata)->digits = (NumericDigit *) sqlda + offset + (num->digits - num->buf);
547 }
548
549 PGTYPESnumeric_free(num);
550
551 break;
552 }
553 case ECPGt_date:
554 ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset);
555 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
556 sqlda->sqlvar[i].sqllen = sizeof(date);
557 break;
558 case ECPGt_timestamp:
559 ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset);
560 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
561 sqlda->sqlvar[i].sqllen = sizeof(timestamp);
562 break;
563 case ECPGt_interval:
564 ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset);
565 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
566 sqlda->sqlvar[i].sqllen = sizeof(interval);
567 break;
568 case ECPGt_char:
569 case ECPGt_unsigned_char:
570 case ECPGt_string:
571 default:
572 datalen = strlen(PQgetvalue(res, row, i)) + 1;
573 ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset);
574 sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
575 sqlda->sqlvar[i].sqllen = datalen;
576 break;
577 }
578
579 isnull = PQgetisnull(res, row, i);
580 ecpg_log("ecpg_set_native_sqlda on line %d row %d col %d %s\n", lineno, row, i, isnull ? "IS NULL" : "IS NOT NULL");
581 sqlda->sqlvar[i].sqlind = isnull ? &value_is_null : &value_is_not_null;
582 if (!isnull)
583 {
584 if (set_data)
585 ecpg_get_data(res, row, i, lineno,
586 sqlda->sqlvar[i].sqltype, ECPGt_NO_INDICATOR,
587 sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0,
588 ECPG_ARRAY_NONE, compat, false);
589 }
590
591 offset = next_offset;
592 }
593}
594