FreeTDS API
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups 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 int buffer_save_row(DBPROCESS *dbproc);
14 static DBLIB_BUFFER_ROW* buffer_row_address(const DBPROC_ROWBUF * buf, int idx);
15 
45 static int
46 buffer_count(const DBPROC_ROWBUF *buf)
47 {
48  return (buf->head > buf->tail) ?
49  buf->head - buf->tail : /* |...TddddH....| */
50  buf->capacity - (buf->tail - buf->head); /* |ddddH....Tddd| */
51 }
52 
56 static int
57 buffer_is_full(const DBPROC_ROWBUF *buf)
58 {
59  return buf->capacity == buffer_count(buf) && buf->capacity > 1;
60 }
61 
62 #ifndef NDEBUG
63 static int
64 buffer_index_valid(const DBPROC_ROWBUF *buf, int idx)
65 {
66  if (buf->tail <= buf->head)
67  if (buf->head <= idx && idx <= buf->tail)
68  return 1;
69 
70  if (0 <= idx && idx <= buf->head)
71  return 1;
72 
73  if (buf->tail <= idx && idx < buf->capacity)
74  return 1;
75 #if 0
76  printf("buffer_index_valid: idx = %d\n", idx);
77  buffer_struct_print(buf);
78 #endif
79  return 0;
80 }
81 #endif
82 
83 static void
84 buffer_free_row(DBLIB_BUFFER_ROW *row)
85 {
86  if (row->sizes)
87  TDS_ZERO_FREE(row->sizes);
88  if (row->row_data) {
89  tds_free_row(row->resinfo, row->row_data);
90  row->row_data = NULL;
91  }
92  tds_free_results(row->resinfo);
93  row->resinfo = NULL;
94 }
95 
96 /*
97  * Buffer is freed at slightly odd points, whenever
98  * capacity changes:
99  *
100  * 1. When setting capacity, to release prior buffer.
101  * 2. By dbresults. When called the second time, it has to
102  * release prior storage because the new resultset will have
103  * a different width.
104  * 3. By dbclose(), else open/close/open would leak.
105  */
106 static void
107 buffer_free(DBPROC_ROWBUF *buf)
108 {
109  if (buf->rows != NULL) {
110  int i;
111  for (i = 0; i < buf->capacity; ++i)
112  buffer_free_row(&buf->rows[i]);
113  TDS_ZERO_FREE(buf->rows);
114  }
115 }
116 
117 /*
118  * When no rows are currently buffered (and the buffer is allocated)
119  * set the indices to their initial postions.
120  */
121 static void
122 buffer_reset(DBPROC_ROWBUF *buf)
123 {
124  buf->head = 0;
125  buf->current = buf->tail = buf->capacity;
126 }
127 
128 static int
129 buffer_idx_increment(const DBPROC_ROWBUF *buf, int idx)
130 {
131  if (++idx >= buf->capacity) {
132  idx = 0;
133  }
134  return idx;
135 }
136 
141 static DBLIB_BUFFER_ROW*
142 buffer_row_address(const DBPROC_ROWBUF * buf, int idx)
143 {
144  if (!(idx >= 0 && idx < buf->capacity)) {
145  printf("idx is %d:\n", idx);
146  buffer_struct_print(buf);
147  assert(idx >= 0);
148  assert(idx < buf->capacity);
149  }
150 
151  return &(buf->rows[idx]);
152 }
153 
157 static DBINT
158 buffer_idx2row(const DBPROC_ROWBUF *buf, int idx)
159 {
160  return buffer_row_address(buf, idx)->row;
161 }
162 
166 static int
167 buffer_row2idx(const DBPROC_ROWBUF *buf, int row_number)
168 {
169  int i, ii, idx = -1;
170 
171  if (buf->tail == buf->capacity) {
172  assert (buf->head == 0);
173  return -1; /* no rows buffered */
174  }
175 
176  /*
177  * March through the buffers from tail to head, stop if we find our row.
178  * A full queue is indicated by tail == head (which means we can't write).
179  */
180  for (ii=0, i = buf->tail; i != buf->head || ii == 0; i = buffer_idx_increment(buf, i)) {
181  if( buffer_idx2row(buf, i) == row_number) {
182  idx = i;
183  break;
184  }
185  assert(ii++ < buf->capacity); /* prevent infinite loop */
186  }
187 
188  return idx;
189 }
190 
196 static void
197 buffer_delete_rows(DBPROC_ROWBUF * buf, int count)
198 {
199  int i;
200 
201  if (count < 0 || count > buffer_count(buf)) {
202  count = buffer_count(buf);
203  }
204 
205  for (i=0; i < count; i++) {
206  if (buf->tail < buf->capacity)
207  buffer_free_row(&buf->rows[i]);
208  buf->tail = buffer_idx_increment(buf, buf->tail);
209  /*
210  * If deleting rows from the buffer catches the tail to the head,
211  * return to the initial postion. Otherwise, it will look full.
212  */
213  if (buf->tail == buf->head) {
214  buffer_reset(buf);
215  break;
216  }
217  }
218 #if 0
219  buffer_struct_print(buf);
220 #endif
221 }
222 
223 static void
224 buffer_transfer_bound_data(DBPROC_ROWBUF *buf, TDS_INT res_type, TDS_INT compute_id, DBPROCESS * dbproc, int idx)
225 {
226  int i;
227  int srctype, desttype;
228  BYTE *src;
229  const DBLIB_BUFFER_ROW *row;
230 
231  tdsdump_log(TDS_DBG_FUNC, "buffer_transfer_bound_data(%p %d %d %p %d)\n", buf, res_type, compute_id, dbproc, idx);
232  assert(buffer_index_valid(buf, idx));
233 
234  row = buffer_row_address(buf, idx);
235  assert(row->resinfo);
236 
237  for (i = 0; i < row->resinfo->num_cols; i++) {
238  DBINT srclen;
239  TDSCOLUMN *curcol = row->resinfo->columns[i];
240 
241  if (row->sizes)
242  curcol->column_cur_size = row->sizes[i];
243 
244  if (curcol->column_nullbind) {
245  if (curcol->column_cur_size < 0) {
246  *(DBINT *)(curcol->column_nullbind) = -1;
247  } else {
248  *(DBINT *)(curcol->column_nullbind) = 0;
249  }
250  }
251  if (!curcol->column_varaddr)
252  continue;
253 
254  if (row->row_data)
255  src = &row->row_data[curcol->column_data - row->resinfo->current_row];
256  else
257  src = curcol->column_data;
258  srclen = curcol->column_cur_size;
259  if (is_blob_type(curcol->column_type)) {
260  src = (BYTE *) ((TDSBLOB *) src)->textvalue;
261  }
262  desttype = _db_get_server_type(curcol->column_bindtype);
263  srctype = tds_get_conversion_type(curcol->column_type, curcol->column_size);
264 
265  if (srclen <= 0) {
266  if (srclen == 0 || !curcol->column_nullbind)
267  dbgetnull(dbproc, curcol->column_bindtype, curcol->column_bindlen,
268  (BYTE *) curcol->column_varaddr);
269  } else {
270  copy_data_to_host_var(dbproc, srctype, src, srclen, desttype,
271  (BYTE *) curcol->column_varaddr, curcol->column_bindlen,
272  curcol->column_bindtype, curcol->column_nullbind);
273  }
274  }
275 
276  /*
277  * This function always bumps current. Usually, it's called
278  * by dbnextrow(), so bumping current is a pretty obvious choice.
279  * It can also be called by dbgetrow(), but that function also
280  * causes the bump. If you call dbgetrow() for row N, a subsequent
281  * call to dbnextrow() yields N+1.
282  */
283  buf->current = buffer_idx_increment(buf, buf->current);
284 
285 } /* end buffer_transfer_bound_data() */
286 
287 static void
288 buffer_struct_print(const DBPROC_ROWBUF *buf)
289 {
290  assert(buf);
291 
292  printf("\t%d rows in buffer\n", buffer_count(buf));
293 
294  printf("\thead = %d\t", buf->head);
295  printf("\ttail = %d\t", buf->tail);
296  printf("\tcurrent = %d\n", buf->current);
297  printf("\tcapacity = %d\t", buf->capacity);
298  printf("\thead row number = %d\n", buf->received);
299 }
300 
301 /* * * Functions called only by public db-lib API take DBPROCESS* * */
302 
319 static int
320 buffer_current_index(const DBPROCESS *dbproc)
321 {
322  const DBPROC_ROWBUF *buf = &dbproc->row_buf;
323 #if 0
324  buffer_struct_print(buf);
325 #endif
326  if (buf->capacity <= 1) /* no buffering */
327  return -1;
328  if (buf->current == buf->head || buf->current == buf->capacity)
329  return -1;
330 
331  assert(buf->current >= 0);
332  assert(buf->current < buf->capacity);
333 
334  if( buf->tail < buf->head) {
335  assert(buf->tail < buf->current);
336  assert(buf->current < buf->head);
337  } else {
338  if (buf->current > buf->head)
339  assert(buf->current > buf->tail);
340  }
341  return buf->current;
342 }
343 
344 /*
345  * Normally called by dbsetopt() to prepare for buffering
346  * Called with nrows == 0 by dbopen to safely set buf->rows to NULL.
347  */
348 static void
349 buffer_set_capacity(DBPROCESS *dbproc, int nrows)
350 {
351  DBPROC_ROWBUF *buf = &dbproc->row_buf;
352 
353  buffer_free(buf);
354 
355  memset(buf, 0, sizeof(DBPROC_ROWBUF));
356 
357  if (0 == nrows) {
358  buf->capacity = 1;
359  return;
360  }
361 
362  assert(0 < nrows);
363 
364  buf->capacity = nrows;
365 }
366 
367 /*
368  * Called only by dbresults(); capacity must be >= 1.
369  * Sybase's documents say dbresults() cannot return FAIL if the prior calls worked,
370  * which is a little strange, because (for FreeTDS, at least), dbresults
371  * is when we learn about the result set's width. Without that information, we
372  * can't allocate memory for the buffer. But if we *fail* to allocate memory,
373  * we're not to communicate it back to the caller?
374  */
375 static void
376 buffer_alloc(DBPROCESS *dbproc)
377 {
378  DBPROC_ROWBUF *buf = &dbproc->row_buf;
379 
380  /* Call this function only after setting capacity. */
381 
382  assert(buf);
383  assert(buf->capacity > 0);
384  assert(buf->rows == NULL);
385 
386  buf->rows = (DBLIB_BUFFER_ROW *) calloc(buf->capacity, sizeof(DBLIB_BUFFER_ROW));
387 
388  assert(buf->rows);
389 
390  buffer_reset(buf);
391 
392  buf->received = 0;
393 }
394 
399 static int
400 buffer_add_row(DBPROCESS *dbproc, TDSRESULTINFO *resinfo)
401 {
402  DBPROC_ROWBUF *buf = &dbproc->row_buf;
403  DBLIB_BUFFER_ROW *row;
404  int i;
405 
406  assert(buf->capacity >= 0);
407 
408  if (buffer_is_full(buf))
409  return -1;
410 
411  /* initial condition is head == 0 and tail == capacity */
412  if (buf->tail == buf->capacity) {
413  /* bumping this tail will set it to zero */
414  assert(buf->head == 0);
415  buf->tail = buffer_idx_increment(buf, buf->tail);
416  }
417 
418  row = buffer_row_address(buf, buf->head);
419 
420  /* bump the row number, write it, and move the data to head */
421  if (row->resinfo) {
422  tds_free_row(row->resinfo, row->row_data);
423  tds_free_results(row->resinfo);
424  }
425  row->row = ++buf->received;
426  ++resinfo->ref_count;
427  row->resinfo = resinfo;
428  row->row_data = NULL;
429  if (row->sizes)
430  free(row->sizes);
431  row->sizes = (TDS_INT *) calloc(resinfo->num_cols, sizeof(TDS_INT));
432  for (i = 0; i < resinfo->num_cols; ++i)
433  row->sizes[i] = resinfo->columns[i]->column_cur_size;
434 
435  /* update current, bump the head */
436  buf->current = buf->head;
437  buf->head = buffer_idx_increment(buf, buf->head);
438 
439  return buf->current;
440 }
441 
442 static int
443 buffer_save_row(DBPROCESS *dbproc)
444 {
445  DBPROC_ROWBUF *buf = &dbproc->row_buf;
446  DBLIB_BUFFER_ROW *row;
447  int idx = buf->head - 1;
448 
449  if (buf->capacity <= 1)
450  return SUCCEED;
451 
452  if (idx < 0)
453  idx = buf->capacity - 1;
454  if (idx >= 0 && idx < buf->capacity) {
455  row = &buf->rows[idx];
456 
457  if (row->resinfo && !row->row_data) {
458  row->row_data = row->resinfo->current_row;
459  tds_alloc_row(row->resinfo);
460  }
461  }
462 
463  return SUCCEED;
464 }
465 
TDS_INT * sizes
save old sizes
Definition: buffering.h:9
int tds_get_conversion_type(int srctype, int colsize)
Return type suitable for conversions (convert all nullable types to fixed type)
Definition: convert.c:152
Definition: dblib.h:53
unsigned char * row_data
row data, NULL for resinfo-&gt;current_row
Definition: buffering.h:5
TDSRESULTINFO * resinfo
pointer to result informations
Definition: buffering.h:3
Information about blobs (e.g.
Definition: tds.h:895
Definition: buffering.h:1
int tds_alloc_row(TDSRESULTINFO *res_info)
Allocate space for row store return NULL on out of memory.
Definition: mem.c:416
TDS_SMALLINT column_type
This type can be different from wire type because conversion (e.g.
Definition: tds.h:950
Definition: dblib.h:141
static RETCODE dbgetnull(DBPROCESS *dbproc, int bindtype, int varlen, BYTE *varaddr)
Definition: dblib.c:528
Hold information for any results.
Definition: tds.h:1026
DBINT row
row number
Definition: buffering.h:7
TDS_INT column_cur_size
size written in variable (ie: char, text, binary).
Definition: tds.h:996
TDS_INT column_size
maximun size of data.
Definition: tds.h:958
void tdsdump_log(const char *file, unsigned int level_line, const char *fmt,...)
This function write a message to the debug log.
Definition: log.c:371
Metadata about columns in regular and compute rows.
Definition: tds.h:948