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