NCBI C++ ToolKit
buffering.h
Go to the documentation of this file.

Go to the SVN repository for this file.

1 typedef struct dblib_buffer_row {
2  /** pointer to result informations */
4  /** row data, NULL for resinfo->current_row */
5  unsigned char *row_data;
6  /** row number */
8  /** save old sizes */
11 
12 static void buffer_struct_print(const DBPROC_ROWBUF *buf);
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 /**
82  * A few words on the buffering design.
83  *
84  * DBPROC_ROWBUF::buf is a block of row buffers,
85  * managed as a ring, indexed by head, tail, and current:
86  *
87  * head -- where new elements are inserted.
88  * tail -- oldest element.
89  * current -- active row (read by dbgetrow/dbnextrow)
90  *
91  * capacity is the number of rows that buf can hold.
92  *
93  * Each element in buf is preceded by its row_number:
94  * the result_set row number, determined by counting the rows
95  * as they're received from the server. Applications communicate
96  * to db-lib in row numbers, not buffer indices.
97  *
98  * Semantics:
99  * head == 0 && tail == capacity is the initial condition.
100  * head == tail means the buffer is full, except when capacity is 1.
101  * head < tail means the buffer has wrapped around.
102  *
103  * Whether or not buffering is active is governed by
104  * dbproc->dbopts[DBBUFFER].optactive.
105  */
106 
107 /**
108  * number of rows in the buffer
109  */
110 static int
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 
119 /**
120  * Can the buffer be written to?
121  */
122 static int
124 {
125  BUFFER_CHECK(buf);
126  return buf->capacity == buffer_count(buf) && buf->capacity > 1;
127 }
128 
129 #ifndef NDEBUG
130 static int
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  tdsdump_log(TDS_DBG_FUNC, "buffer_index_valid: idx = %d\n", idx);
146 #endif
147  return 0;
148 }
149 #endif
150 
151 static void
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  }
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
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
194 {
195  buf->head = 0;
196  buf->current = buf->tail = buf->capacity;
197  BUFFER_CHECK(buf);
198 }
199 
200 static int
202 {
203  if (++idx >= buf->capacity) {
204  idx = 0;
205  }
206  return idx;
207 }
208 
209 /**
210  * Given an index, return the row storage, including
211  * the DBINT row number prefix.
212  */
213 static DBLIB_BUFFER_ROW*
215 {
216  BUFFER_CHECK(buf);
217  if (idx < 0 || idx >= buf->capacity) {
218  tdsdump_log(TDS_DBG_WARN, "idx is %d:\n", idx);
220  return NULL;
221  }
222 
223  return &(buf->rows[idx]);
224 }
225 
226 /**
227  * Convert an index to a row number.
228  */
229 static DBINT
231 {
232  BUFFER_CHECK(buf);
233  return buffer_row_address(buf, idx)->row;
234 }
235 
236 /**
237  * Convert a row number to an index.
238  */
239 static int
240 buffer_row2idx(const DBPROC_ROWBUF *buf, int row_number)
241 {
242  int i = buf->tail;
243 #ifndef NDEBUG
244  int ii = 0;
245 #endif
246 
247  BUFFER_CHECK(buf);
248  if (i == buf->capacity) {
249  assert (buf->head == 0);
250  return -1; /* no rows buffered */
251  }
252 
253  /*
254  * March through the buffers from tail to head, stop if we find our row.
255  * A full queue is indicated by tail == head (which means we can't write).
256  */
257  do {
258  if (buffer_idx2row(buf, i) == row_number)
259  return i;
260 
261  assert(ii++ < buf->capacity); /* prevent infinite loop */
262 
264  } while (i != buf->head);
265 
266  return -1;
267 }
268 
269 /**
270  * Deleting a row from the buffer doesn't affect memory allocation.
271  * It just makes the space available for a different row.
272  */
273 static void
275 {
276  int i;
277 
278  BUFFER_CHECK(buf);
279  if (count < 0 || count > buffer_count(buf)) {
280  count = buffer_count(buf);
281  }
282 
283  for (i=0; i < count; i++) {
284  if (buf->tail < buf->capacity)
285  buffer_free_row(&buf->rows[buf->tail]);
286  buf->tail = buffer_idx_increment(buf, buf->tail);
287  /*
288  * If deleting rows from the buffer catches the tail to the head,
289  * return to the initial position. Otherwise, it will look full.
290  */
291  if (buf->tail == buf->head) {
292  buffer_reset(buf);
293  break;
294  }
295  }
296 #if 0
298 #endif
299  BUFFER_CHECK(buf);
300 }
301 
302 static void
304 {
305  int i;
306  BYTE *src;
307  const DBLIB_BUFFER_ROW *row;
308 
309  tdsdump_log(TDS_DBG_FUNC, "buffer_transfer_bound_data(%p %d %d %p %d)\n", buf, res_type, compute_id, dbproc, idx);
310  BUFFER_CHECK(buf);
312 
313  row = buffer_row_address(buf, idx);
314  assert(row->resinfo);
315 
316  for (i = 0; i < row->resinfo->num_cols; i++) {
317  TDS_SERVER_TYPE srctype;
318  DBINT srclen;
319  TDSCOLUMN *curcol = row->resinfo->columns[i];
320 
321  if (row->sizes)
322  curcol->column_cur_size = row->sizes[i];
323 
324  srclen = curcol->column_cur_size;
325 
326  if (curcol->column_nullbind) {
327  if (srclen < 0) {
328  *(DBINT *)(curcol->column_nullbind) = -1;
329  } else {
330  *(DBINT *)(curcol->column_nullbind) = 0;
331  }
332  }
333  if (!curcol->column_varaddr)
334  continue;
335 
336  if (srclen <= 0) {
337  if (srclen == 0 || !curcol->column_nullbind)
339  (BYTE *) curcol->column_varaddr);
340  continue;
341  }
342 
343  srctype = tds_get_conversion_type(curcol->column_type, curcol->column_size);
344 
345  if (row->row_data)
346  src = &row->row_data[curcol->column_data - row->resinfo->current_row];
347  else
348  src = curcol->column_data;
349  if (is_blob_col(curcol))
350  src = (BYTE *) ((TDSBLOB *) src)->textvalue;
351 
352  copy_data_to_host_var(dbproc, srctype, src, srclen,
353  (BYTE *) curcol->column_varaddr, curcol->column_bindlen,
354  curcol->column_bindtype, (DBINT*) curcol->column_nullbind);
355  }
356 
357  /*
358  * This function always bumps current. Usually, it's called
359  * by dbnextrow(), so bumping current is a pretty obvious choice.
360  * It can also be called by dbgetrow(), but that function also
361  * causes the bump. If you call dbgetrow() for row N, a subsequent
362  * call to dbnextrow() yields N+1.
363  */
364  buf->current = buffer_idx_increment(buf, buf->current);
365 
366 } /* end buffer_transfer_bound_data() */
367 
368 static void
370 {
371  assert(buf);
372 
373  tdsdump_log(TDS_DBG_FUNC, "%d rows in buffer\n", buffer_count(buf));
374 
375  tdsdump_log(TDS_DBG_FUNC, "head = %d\n", buf->head);
376  tdsdump_log(TDS_DBG_FUNC, "tail = %d\n", buf->tail);
377  tdsdump_log(TDS_DBG_FUNC, "current = %d\n", buf->current);
378  tdsdump_log(TDS_DBG_FUNC, "capacity = %d\n", buf->capacity);
379  tdsdump_log(TDS_DBG_FUNC, "head row number = %d\n", buf->received);
380 }
381 
382 /* * * Functions called only by public db-lib API take DBPROCESS* * */
383 
384 /**
385  * Return the current row buffer index.
386  * We strive to validate it first. It must be:
387  * between zero and capacity (obviously), and
388  * between the head and the tail, logically.
389  *
390  * If the head has wrapped the tail, it shouldn't be in no man's land.
391  * IOW, if capacity is 9, head is 3 and tail is 7, good rows are 7-8 and 0-2.
392  * (Row 3 is about-to-be-inserted, and 4-6 are not in use.) Here's a diagram:
393  * d d d ! ! ! ! d d
394  * 0 1 2 3 4 5 6 7 8
395  * ^ ^
396  * Head Tail
397  *
398  * The special case is capacity == 1, meaning there's no buffering, and head == tail === 0.
399  */
400 static int
402 {
403  const DBPROC_ROWBUF *buf = &dbproc->row_buf;
404 #if 0
406 #endif
407  if (buf->capacity <= 1) /* no buffering */
408  return -1;
409  if (buf->current == buf->head || buf->current == buf->capacity)
410  return -1;
411 
412  assert(buf->current >= 0);
413  assert(buf->current < buf->capacity);
414 
415  if( buf->tail < buf->head) {
416  assert(buf->tail < buf->current);
417  assert(buf->current < buf->head);
418  } else {
419  if (buf->current > buf->head)
420  assert(buf->current > buf->tail);
421  }
422  return buf->current;
423 }
424 
425 /*
426  * Normally called by dbsetopt() to prepare for buffering
427  * Called with nrows == 0 by dbopen to safely set buf->rows to NULL.
428  */
429 static void
431 {
433 
434  buffer_free(buf);
435 
436  memset(buf, 0, sizeof(DBPROC_ROWBUF));
437 
438  if (0 == nrows) {
439  buf->capacity = 1;
440  BUFFER_CHECK(buf);
441  return;
442  }
443 
444  assert(0 < nrows);
445 
446  buf->capacity = nrows;
447  BUFFER_CHECK(buf);
448 }
449 
450 /*
451  * Called only by dbresults(); capacity must be >= 1.
452  * Sybase's documents say dbresults() cannot return FAIL if the prior calls worked,
453  * which is a little strange, because (for FreeTDS, at least), dbresults
454  * is when we learn about the result set's width. Without that information, we
455  * can't allocate memory for the buffer. But if we *fail* to allocate memory,
456  * we're not to communicate it back to the caller?
457  */
458 static void
460 {
462 
463  /* Call this function only after setting capacity. */
464 
465  assert(buf);
466  assert(buf->capacity > 0);
467  assert(buf->rows == NULL);
468 
469  buf->rows = tds_new0(DBLIB_BUFFER_ROW, buf->capacity);
470 
471  assert(buf->rows);
472 
473  buffer_reset(buf);
474 
475  buf->received = 0;
476 }
477 
478 /**
479  * Called by dbnextrow
480  * Returns a row buffer index, or -1 to indicate the buffer is full.
481  */
482 static int
484 {
486  DBLIB_BUFFER_ROW *row;
487  int i;
488 
489  assert(buf->capacity >= 0);
490 
491  if (buffer_is_full(buf))
492  return -1;
493 
494  row = buffer_row_address(buf, buf->head);
495 
496  /* bump the row number, write it, and move the data to head */
497  if (row->resinfo) {
498  tds_free_row(row->resinfo, row->row_data);
500  }
501  row->row = ++buf->received;
502  ++resinfo->ref_count;
503  row->resinfo = resinfo;
504  row->row_data = NULL;
505  if (row->sizes)
506  free(row->sizes);
507  row->sizes = tds_new0(TDS_INT, resinfo->num_cols);
508  for (i = 0; i < resinfo->num_cols; ++i)
509  row->sizes[i] = resinfo->columns[i]->column_cur_size;
510 
511  /* initial condition is head == 0 and tail == capacity */
512  if (buf->tail == buf->capacity) {
513  /* bumping this tail will set it to zero */
514  assert(buf->head == 0);
515  buf->tail = 0;
516  }
517 
518  /* update current, bump the head */
519  buf->current = buf->head;
520  buf->head = buffer_idx_increment(buf, buf->head);
521 
522  return buf->current;
523 }
524 
525 static RETCODE
527 {
529  DBLIB_BUFFER_ROW *row;
530  int idx = buf->head - 1;
531 
532  if (buf->capacity <= 1)
533  return SUCCEED;
534 
535  if (idx < 0)
536  idx = buf->capacity - 1;
537  if (idx >= 0 && idx < buf->capacity) {
538  row = &buf->rows[idx];
539 
540  if (row->resinfo && !row->row_data) {
541  row->row_data = row->resinfo->current_row;
542  tds_alloc_row(row->resinfo);
543  }
544  }
545 
546  return SUCCEED;
547 }
548 
static void buffer_alloc(DBPROCESS *dbproc)
Definition: buffering.h:459
static void buffer_delete_rows(DBPROC_ROWBUF *buf, int count)
Deleting a row from the buffer doesn't affect memory allocation.
Definition: buffering.h:274
static int buffer_is_full(const DBPROC_ROWBUF *buf)
Can the buffer be written to?
Definition: buffering.h:123
static int buffer_count(const DBPROC_ROWBUF *buf)
A few words on the buffering design.
Definition: buffering.h:111
static DBINT buffer_idx2row(const DBPROC_ROWBUF *buf, int idx)
Convert an index to a row number.
Definition: buffering.h:230
static void buffer_set_capacity(DBPROCESS *dbproc, int nrows)
Definition: buffering.h:430
static int buffer_index_valid(const DBPROC_ROWBUF *buf, int idx)
Definition: buffering.h:131
static DBLIB_BUFFER_ROW * buffer_row_address(const DBPROC_ROWBUF *buf, int idx)
Given an index, return the row storage, including the DBINT row number prefix.
Definition: buffering.h:214
static int buffer_add_row(DBPROCESS *dbproc, TDSRESULTINFO *resinfo)
Called by dbnextrow Returns a row buffer index, or -1 to indicate the buffer is full.
Definition: buffering.h:483
#define BUFFER_CHECK(buf)
Definition: buffering.h:79
static void buffer_free(DBPROC_ROWBUF *buf)
Definition: buffering.h:176
static void buffer_transfer_bound_data(DBPROC_ROWBUF *buf, TDS_INT res_type, TDS_INT compute_id, DBPROCESS *dbproc, int idx)
Definition: buffering.h:303
static void buffer_reset(DBPROC_ROWBUF *buf)
Definition: buffering.h:193
static int buffer_row2idx(const DBPROC_ROWBUF *buf, int row_number)
Convert a row number to an index.
Definition: buffering.h:240
struct dblib_buffer_row DBLIB_BUFFER_ROW
static RETCODE buffer_save_row(DBPROCESS *dbproc)
Definition: buffering.h:526
static void buffer_struct_print(const DBPROC_ROWBUF *buf)
Definition: buffering.h:369
static int buffer_current_index(const DBPROCESS *dbproc)
Return the current row buffer index.
Definition: buffering.h:401
static int buffer_idx_increment(const DBPROC_ROWBUF *buf, int idx)
Definition: buffering.h:201
static void buffer_free_row(DBLIB_BUFFER_ROW *row)
Definition: buffering.h:152
#define head
Definition: ct_nlmzip_i.h:138
#define NULL
Definition: ncbistd.hpp:225
char * buf
int i
if(yy_accept[yy_current_state])
TDS_SERVER_TYPE
Definition: proto.h:161
#define dbgetnull
#define copy_data_to_host_var
#define tds_free_row
#define tds_alloc_row
#define tds_get_conversion_type
#define tds_free_results
#define assert(x)
Definition: srv_diag.hpp:58
DBINT row
row number
Definition: buffering.h:7
unsigned char * row_data
row data, NULL for resinfo->current_row
Definition: buffering.h:5
TDS_INT * sizes
save old sizes
Definition: buffering.h:9
TDSRESULTINFO * resinfo
pointer to result informations
Definition: buffering.h:3
Information about blobs (e.g.
Definition: tds.h:658
Metadata about columns in regular and compute rows.
Definition: tds.h:761
TDS_SMALLINT * column_nullbind
Definition: tds.h:818
TDS_INT column_size
maximun size of data.
Definition: tds.h:766
TDS_UINT column_bindlen
Definition: tds.h:817
TDS_SMALLINT column_bindtype
Definition: tds.h:815
unsigned char * column_data
Definition: tds.h:793
TDS_SERVER_TYPE column_type
This type can be different from wire type because conversion (e.g.
Definition: tds.h:768
TDS_CHAR * column_varaddr
Definition: tds.h:819
TDS_INT column_cur_size
size written in variable (ie: char, text, binary).
Definition: tds.h:811
DBPROC_ROWBUF row_buf
Definition: dblib.h:125
Hold information for any results.
Definition: tds.h:842
unsigned char * current_row
Definition: tds.h:849
TDS_USMALLINT num_cols
Definition: tds.h:845
TDS_INT ref_count
Definition: tds.h:847
TDSCOLUMN ** columns
Definition: tds.h:844
int RETCODE
Definition: sybdb.h:121
unsigned char BYTE
Definition: sybdb.h:334
#define SUCCEED
Definition: sybdb.h:585
Int4 DBINT
Definition: sybdb.h:255
DBPROCESS * dbproc
Definition: t0013.c:18
tds_sysdep_int32_type TDS_INT
Definition: tds.h:149
#define tdsdump_log
Definition: tds.h:1561
#define TDS_DBG_WARN
Definition: tds.h:902
#define is_blob_col(x)
Definition: tds.h:445
#define tds_new0(type, n)
Definition: tds.h:1393
#define TDS_ZERO_FREE(x)
Definition: tds.h:359
#define TDS_DBG_FUNC
Definition: tds.h:898
void free(voidpf ptr)
Modified on Thu Dec 07 10:08:51 2023 by modify_doxy.py rev. 669887