FreeTDS API: src/dblib/buffering.h Source File

FreeTDS API

Loading...
Searching...
No Matches
buffering.h
1 typedef struct dblib_buffer_row {
3 TDSRESULTINFO *resinfo;
5 unsigned char *row_data;
7 DBINT row;
9 TDS_INT *sizes;
10} DBLIB_BUFFER_ROW;
11
12static void buffer_struct_print(const DBPROC_ROWBUF *buf);
13static RETCODE buffer_save_row(DBPROCESS *dbproc);
14static DBLIB_BUFFER_ROW* buffer_row_address(const DBPROC_ROWBUF * buf, int idx);
15
16#if ENABLE_EXTRA_CHECKS
17static void buffer_check_row_empty(DBLIB_BUFFER_ROW *row)
18{
19 assert(row->resinfo == NULL);
20 assert(row->row_data == NULL);
21 assert(row->sizes == NULL);
22 assert(row->row == 0);
23}
24
25static void buffer_check(const DBPROC_ROWBUF *buf)
26{
27 int i;
28
29 /* no buffering */
30 if (buf->capacity == 0 || buf->capacity == 1) {
31 assert(buf->head == 0);
32 assert(buf->tail == 0 || buf->tail == 1);
33 assert(buf->capacity == 1 || buf->rows == NULL);
34 return;
35 }
36
37 assert(buf->capacity > 0);
38 assert(buf->head >= 0);
39 assert(buf->tail >= 0);
40 assert(buf->head < buf->capacity);
41 assert(buf->tail <= buf->capacity);
42
43 /* check empty */
44 if (buf->tail == buf->capacity) {
45 assert(buf->head == 0);
46 for (i = 0; buf->rows && i < buf->capacity; ++i)
47 buffer_check_row_empty(&buf->rows[i]);
48 return;
49 }
50
51 if (buf->rows == NULL)
52 return;
53
54 /* check filled part */
55 i = buf->tail;
56 do {
57 assert(i >= 0 && i < buf->capacity);
58 assert(buf->rows[i].resinfo != NULL);
59 assert(buf->rows[i].row > 0);
60 assert(buf->rows[i].row <= buf->received);
61 ++i;
62 if (i == buf->capacity)
63 i = 0;
64 } while (i != buf->head);
65
66 /* check empty part */
67 if (buf->head != buf->tail) {
68 i = buf->head;
69 do {
70 assert(i >= 0 && i < buf->capacity);
71 buffer_check_row_empty(&buf->rows[i]);
72 ++i;
73 if (i == buf->capacity)
74 i = 0;
75 } while (i != buf->tail);
76 }
77}
78#define BUFFER_CHECK(buf) buffer_check(buf)
79#else
80#define BUFFER_CHECK(buf) do {} while(0)
81#endif
111static int
112buffer_count(const DBPROC_ROWBUF *buf)
113{
114 BUFFER_CHECK(buf);
115 return (buf->head > buf->tail) ?
116 buf->head - buf->tail : /* |...TddddH....| */
117 buf->capacity - (buf->tail - buf->head); /* |ddddH....Tddd| */
118}
119
123static int
124buffer_is_full(const DBPROC_ROWBUF *buf)
125{
126 BUFFER_CHECK(buf);
127 return buf->capacity == buffer_count(buf) && buf->capacity > 1;
128}
129
130#ifndef NDEBUG
131static int
132buffer_index_valid(const DBPROC_ROWBUF *buf, int idx)
133{
134 BUFFER_CHECK(buf);
135 if (buf->tail <= buf->head)
136 if (buf->head <= idx && idx <= buf->tail)
137 return 1;
138
139 if (0 <= idx && idx <= buf->head)
140 return 1;
141
142 if (buf->tail <= idx && idx < buf->capacity)
143 return 1;
144#if 0
145 tdsdump_log(TDS_DBG_INFO1, "buffer_index_valid: idx = %d\n", idx);
146 buffer_struct_print(buf);
147#endif
148 return 0;
149}
150#endif
151
152static void
153buffer_free_row(DBLIB_BUFFER_ROW *row)
154{
155 if (row->sizes)
156 TDS_ZERO_FREE(row->sizes);
157 if (row->row_data) {
158 tds_free_row(row->resinfo, row->row_data);
159 row->row_data = NULL;
160 }
161 tds_free_results(row->resinfo);
162 row->resinfo = NULL;
163 row->row = 0;
164}
165
166/*
167 * Buffer is freed at slightly odd points, whenever
168 * capacity changes:
169 *
170 * 1. When setting capacity, to release prior buffer.
171 * 2. By dbresults. When called the second time, it has to
172 * release prior storage because the new resultset will have
173 * a different width.
174 * 3. By dbclose(), else open/close/open would leak.
175 */
176static void
177buffer_free(DBPROC_ROWBUF *buf)
178{
179 BUFFER_CHECK(buf);
180 if (buf->rows != NULL) {
181 int i;
182 for (i = 0; i < buf->capacity; ++i)
183 buffer_free_row(&buf->rows[i]);
184 TDS_ZERO_FREE(buf->rows);
185 }
186 BUFFER_CHECK(buf);
187}
188
189/*
190 * When no rows are currently buffered (and the buffer is allocated)
191 * set the indices to their initial positions.
192 */
193static void
194buffer_reset(DBPROC_ROWBUF *buf)
195{
196 buf->head = 0;
197 buf->current = buf->tail = buf->capacity;
198 BUFFER_CHECK(buf);
199}
200
201static int
202buffer_idx_increment(const DBPROC_ROWBUF *buf, int idx)
203{
204 if (++idx >= buf->capacity) {
205 idx = 0;
206 }
207 return idx;
208}
209
214static DBLIB_BUFFER_ROW*
215buffer_row_address(const DBPROC_ROWBUF * buf, int idx)
216{
217 BUFFER_CHECK(buf);
218 if (idx < 0 || idx >= buf->capacity) {
219 tdsdump_log(TDS_DBG_WARN, "idx is %d:\n", idx);
220 buffer_struct_print(buf);
221 return NULL;
222 }
223
224 return &(buf->rows[idx]);
225}
226
230static DBINT
231buffer_idx2row(const DBPROC_ROWBUF *buf, int idx)
232{
233 BUFFER_CHECK(buf);
234 return buffer_row_address(buf, idx)->row;
235}
236
240static int
241buffer_row2idx(const DBPROC_ROWBUF *buf, int row_number)
242{
243 int i = buf->tail;
244#ifndef NDEBUG
245 int ii = 0;
246#endif
247
248 BUFFER_CHECK(buf);
249 if (i == buf->capacity) {
250 assert (buf->head == 0);
251 return -1; /* no rows buffered */
252 }
253
254 /*
255 * March through the buffers from tail to head, stop if we find our row.
256 * A full queue is indicated by tail == head (which means we can't write).
257 */
258 do {
259 if (buffer_idx2row(buf, i) == row_number)
260 return i;
261
262 assert(ii++ < buf->capacity); /* prevent infinite loop */
263
264 i = buffer_idx_increment(buf, i);
265 } while (i != buf->head);
266
267 return -1;
268}
269
274static void
275buffer_delete_rows(DBPROC_ROWBUF * buf, int count)
276{
277 int i;
278
279 BUFFER_CHECK(buf);
280 if (count < 0 || count > buffer_count(buf)) {
281 count = buffer_count(buf);
282 }
283
284 for (i=0; i < count; i++) {
285 if (buf->tail < buf->capacity)
286 buffer_free_row(&buf->rows[buf->tail]);
287 buf->tail = buffer_idx_increment(buf, buf->tail);
288 /*
289 * If deleting rows from the buffer catches the tail to the head,
290 * return to the initial position. Otherwise, it will look full.
291 */
292 if (buf->tail == buf->head) {
293 buffer_reset(buf);
294 break;
295 }
296 }
297#if 0
298 buffer_struct_print(buf);
299#endif
300 BUFFER_CHECK(buf);
301}
302
306static void
307buffer_transfer_bound_data(DBPROC_ROWBUF *buf, TDS_INT res_type, TDS_INT compute_id, DBPROCESS * dbproc, int idx)
308{
309 int i;
310 BYTE *src;
311 const DBLIB_BUFFER_ROW *row;
312
313 tdsdump_log(TDS_DBG_FUNC, "buffer_transfer_bound_data(%p %d %d %p %d)\n", buf, res_type, compute_id, dbproc, idx);
314 BUFFER_CHECK(buf);
315 assert(buffer_index_valid(buf, idx));
316
317 row = buffer_row_address(buf, idx);
318 assert(row->resinfo);
319
320 for (i = 0; i < row->resinfo->num_cols; i++) {
321 TDS_SERVER_TYPE srctype;
322 DBINT srclen;
323 TDSCOLUMN *curcol = row->resinfo->columns[i];
324
325 if (row->sizes)
326 curcol->column_cur_size = row->sizes[i];
327
328 srclen = curcol->column_cur_size;
329
330 if (curcol->column_nullbind) {
331 if (srclen < 0) {
332 *(DBINT *)(curcol->column_nullbind) = -1;
333 } else {
334 *(DBINT *)(curcol->column_nullbind) = 0;
335 }
336 }
337 if (!curcol->column_varaddr)
338 continue;
339
340 if (srclen <= 0) {
341 if (srclen == 0 || !curcol->column_nullbind)
342 dbgetnull(dbproc, curcol->column_bindtype, curcol->column_bindlen,
343 (BYTE *) curcol->column_varaddr);
344 continue;
345 }
346
347 srctype = tds_get_conversion_type(curcol->column_type, curcol->column_size);
348
349 if (row->row_data)
350 src = &row->row_data[curcol->column_data - row->resinfo->current_row];
351 else
352 src = curcol->column_data;
353 if (is_blob_col(curcol))
354 src = (BYTE *) ((TDSBLOB *) src)->textvalue;
355
356 copy_data_to_host_var(dbproc, srctype, src, srclen,
357 (BYTE *) curcol->column_varaddr, curcol->column_bindlen,
358 curcol->column_bindtype, (DBINT*) curcol->column_nullbind);
359 }
360
361 /*
362 * This function always bumps current. Usually, it's called
363 * by dbnextrow(), so bumping current is a pretty obvious choice.
364 * It can also be called by dbgetrow(), but that function also
365 * causes the bump. If you call dbgetrow() for row N, a subsequent
366 * call to dbnextrow() yields N+1.
367 */
368 buf->current = buffer_idx_increment(buf, buf->current);
369
370} /* end buffer_transfer_bound_data() */
371
372static void
373buffer_struct_print(const DBPROC_ROWBUF *buf)
374{
375 assert(buf);
376
377 tdsdump_log(TDS_DBG_INFO1, "%d rows in buffer\n", buffer_count(buf));
378
379 tdsdump_log(TDS_DBG_INFO1, "head = %d\n", buf->head);
380 tdsdump_log(TDS_DBG_INFO1, "tail = %d\n", buf->tail);
381 tdsdump_log(TDS_DBG_INFO1, "current = %d\n", buf->current);
382 tdsdump_log(TDS_DBG_INFO1, "capacity = %d\n", buf->capacity);
383 tdsdump_log(TDS_DBG_INFO1, "head row number = %d\n", buf->received);
384}
385
386/* * * Functions called only by public db-lib API take DBPROCESS* * */
387
404static int
405buffer_current_index(const DBPROCESS *dbproc)
406{
407 const DBPROC_ROWBUF *buf = &dbproc->row_buf;
408#if 0
409 buffer_struct_print(buf);
410#endif
411 if (buf->capacity <= 1) /* no buffering */
412 return -1;
413 if (buf->current == buf->head || buf->current == buf->capacity)
414 return -1;
415
416 assert(buf->current >= 0);
417 assert(buf->current < buf->capacity);
418
419 if( buf->tail < buf->head) {
420 assert(buf->tail < buf->current);
421 assert(buf->current < buf->head);
422 } else {
423 if (buf->current > buf->head)
424 assert(buf->current > buf->tail);
425 }
426 return buf->current;
427}
428
429/*
430 * Normally called by dbsetopt() to prepare for buffering
431 * Called with nrows == 0 by dbopen to safely set buf->rows to NULL.
432 */
433static void
434buffer_set_capacity(DBPROCESS *dbproc, int nrows)
435{
436 DBPROC_ROWBUF *buf = &dbproc->row_buf;
437
438 buffer_free(buf);
439
440 memset(buf, 0, sizeof(DBPROC_ROWBUF));
441
442 if (0 == nrows) {
443 buf->capacity = 1;
444 BUFFER_CHECK(buf);
445 return;
446 }
447
448 assert(0 < nrows);
449
450 buf->capacity = nrows;
451 BUFFER_CHECK(buf);
452}
453
454/*
455 * Called only by dbresults(); capacity must be >= 1.
456 * Sybase's documents say dbresults() cannot return FAIL if the prior calls worked,
457 * which is a little strange, because (for FreeTDS, at least), dbresults
458 * is when we learn about the result set's width. Without that information, we
459 * can't allocate memory for the buffer. But if we *fail* to allocate memory,
460 * we're not to communicate it back to the caller?
461 */
462static void
463buffer_alloc(DBPROCESS *dbproc)
464{
465 DBPROC_ROWBUF *buf = &dbproc->row_buf;
466
467 /* Call this function only after setting capacity. */
468
469 assert(buf);
470 assert(buf->capacity > 0);
471 assert(buf->rows == NULL);
472
473 buf->rows = tds_new0(DBLIB_BUFFER_ROW, buf->capacity);
474
475 assert(buf->rows);
476
477 buffer_reset(buf);
478
479 buf->received = 0;
480}
481
486static int
487buffer_add_row(DBPROCESS *dbproc, TDSRESULTINFO *resinfo)
488{
489 DBPROC_ROWBUF *buf = &dbproc->row_buf;
490 DBLIB_BUFFER_ROW *row;
491 int i;
492
493 assert(buf->capacity >= 0);
494
495 if (buffer_is_full(buf))
496 return -1;
497
498 row = buffer_row_address(buf, buf->head);
499
500 /* bump the row number, write it, and move the data to head */
501 if (row->resinfo) {
502 tds_free_row(row->resinfo, row->row_data);
503 tds_free_results(row->resinfo);
504 }
505 row->row = ++buf->received;
506 ++resinfo->ref_count;
507 row->resinfo = resinfo;
508 row->row_data = NULL;
509 if (row->sizes)
510 free(row->sizes);
511 row->sizes = tds_new0(TDS_INT, resinfo->num_cols);
512 for (i = 0; i < resinfo->num_cols; ++i)
513 row->sizes[i] = resinfo->columns[i]->column_cur_size;
514
515 /* initial condition is head == 0 and tail == capacity */
516 if (buf->tail == buf->capacity) {
517 /* bumping this tail will set it to zero */
518 assert(buf->head == 0);
519 buf->tail = 0;
520 }
521
522 /* update current, bump the head */
523 buf->current = buf->head;
524 buf->head = buffer_idx_increment(buf, buf->head);
525
526 return buf->current;
527}
528
532static RETCODE
533buffer_save_row(DBPROCESS *dbproc)
534{
535 DBPROC_ROWBUF *buf = &dbproc->row_buf;
536 DBLIB_BUFFER_ROW *row;
537 int idx = buf->head - 1;
538
539 if (buf->capacity <= 1)
540 return SUCCEED;
541
542 if (idx < 0)
543 idx = buf->capacity - 1;
544 if (idx >= 0 && idx < buf->capacity) {
545 row = &buf->rows[idx];
546
547 if (row->resinfo && !row->row_data) {
548 row->row_data = row->resinfo->current_row;
549 tds_alloc_row(row->resinfo);
550 }
551 }
552
553 return SUCCEED;
554}
555
dbgetnull
RETCODE dbgetnull(DBPROCESS *dbproc, int bindtype, int varlen, BYTE *varaddr)
Definition dblib.c:546
tds_alloc_row
TDSRET tds_alloc_row(TDSRESULTINFO *res_info)
Allocate space for row store return NULL on out of memory.
Definition mem.c:534
dblib_buffer_row
Definition buffering.h:1
dblib_buffer_row::row
DBINT row
row number
Definition buffering.h:7
dblib_buffer_row::row_data
unsigned char * row_data
row data, NULL for resinfo->current_row
Definition buffering.h:5
dblib_buffer_row::sizes
TDS_INT * sizes
save old sizes
Definition buffering.h:9
dblib_buffer_row::resinfo
TDSRESULTINFO * resinfo
pointer to result information
Definition buffering.h:3
DBPROC_ROWBUF
Definition dblib.h:52
tds_dblib_dbprocess
Definition dblib.h:123
tds_blob
Information about blobs (e.g.
Definition tds.h:587
tds_column
Metadata about columns in regular and compute rows.
Definition tds.h:683
tds_column::column_size
TDS_INT column_size
Maximun size of data.
Definition tds.h:691
tds_column::column_type
TDS_SERVER_TYPE column_type
This type can be different from wire type because conversion (e.g.
Definition tds.h:699
tds_column::column_cur_size
TDS_INT column_cur_size
size written in variable (ie: char, text, binary).
Definition tds.h:747
tds_result_info
Hold information for any results.
Definition tds.h:780