Coverage Report

Created: 2023-09-25 06:55

/src/mdbtools/src/libmdb/table.c
Line
Count
Source (jump to first uncovered line)
1
/* MDB Tools - A library for reading MS Access database file
2
 * Copyright (C) 2000 Brian Bruns
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Library General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Library General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Library General Public
15
 * License along with this library; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17
 */
18
19
#include "mdbtools.h"
20
#include "mdbprivate.h"
21
22
static gint mdb_col_comparer(MdbColumn **a, MdbColumn **b)
23
1.13k
{
24
1.13k
  if ((*a)->col_num > (*b)->col_num)
25
490
    return 1;
26
646
  else if ((*a)->col_num < (*b)->col_num)
27
547
    return -1;
28
99
  else
29
99
    return 0;
30
1.13k
}
31
32
MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry)
33
280
{
34
280
  MdbTableDef *table = g_malloc0(sizeof(MdbTableDef));
35
280
  table->entry=entry;
36
280
  snprintf(table->name, sizeof(table->name), "%s", entry->object_name);
37
38
280
  return table; 
39
280
}
40
void mdb_free_tabledef(MdbTableDef *table)
41
280
{
42
280
  if (!table) return;
43
280
  if (table->is_temp_table) {
44
0
    guint i;
45
    /* Temp table pages are being stored in memory */
46
0
    for (i=0; i<table->temp_table_pages->len; i++)
47
0
      g_free(g_ptr_array_index(table->temp_table_pages,i));
48
0
    g_ptr_array_free(table->temp_table_pages, TRUE);
49
    /* Temp tables use dummy entries */
50
0
    g_free(table->entry);
51
0
  }
52
280
  mdb_free_columns(table->columns);
53
280
  mdb_free_indices(table->indices);
54
280
  g_free(table->usage_map);
55
280
  g_free(table->free_usage_map);
56
280
  g_free(table);
57
280
}
58
MdbTableDef *mdb_read_table(MdbCatalogEntry *entry)
59
1.67k
{
60
1.67k
  MdbTableDef *table;
61
1.67k
  MdbHandle *mdb = entry->mdb;
62
1.67k
  MdbFormatConstants *fmt = mdb->fmt;
63
1.67k
  int row_start, pg_row;
64
1.67k
  void *buf, *pg_buf = mdb->pg_buf;
65
1.67k
  guint i;
66
67
1.67k
  if (!mdb_read_pg(mdb, entry->table_pg)) {
68
179
        fprintf(stderr, "mdb_read_table: Unable to read page %lu\n", entry->table_pg);
69
179
        return NULL;
70
179
    }
71
1.50k
  if (mdb_get_byte(pg_buf, 0) != 0x02) {
72
1.22k
        fprintf(stderr, "mdb_read_table: Page %lu [size=%d] is not a valid table definition page (First byte = 0x%02X, expected 0x02)\n",
73
1.22k
                entry->table_pg, (int)fmt->pg_size, mdb_get_byte(pg_buf, 0));
74
1.22k
    return NULL;
75
1.22k
    }
76
280
  table = mdb_alloc_tabledef(entry);
77
78
280
  mdb_get_int16(pg_buf, 8); /* len */
79
80
  /* Note that num_rows may be zero if the database was improperly closed.
81
   * See https://github.com/mdbtools/mdbtools/issues/120 for discussion. */
82
280
  table->num_rows = mdb_get_int32(pg_buf, fmt->tab_num_rows_offset);
83
280
  table->num_var_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset-2);
84
280
  table->num_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset);
85
280
  table->num_idxs = mdb_get_int32(pg_buf, fmt->tab_num_idxs_offset);
86
280
  table->num_real_idxs = mdb_get_int32(pg_buf, fmt->tab_num_ridxs_offset);
87
88
  /* grab a copy of the usage map */
89
280
  pg_row = mdb_get_int32(pg_buf, fmt->tab_usage_map_offset);
90
280
  if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->map_sz))) {
91
244
        fprintf(stderr, "mdb_read_table: Unable to find page row %d\n", pg_row);
92
244
    mdb_free_tabledef(table);
93
244
    return NULL;
94
244
  }
95
36
  table->usage_map = g_memdup2((char*)buf + row_start, table->map_sz);
96
36
  if (mdb_get_option(MDB_DEBUG_USAGE)) 
97
0
    mdb_buffer_dump(buf, row_start, table->map_sz);
98
36
  mdb_debug(MDB_DEBUG_USAGE,"usage map found on page %ld row %d start %d len %d",
99
36
    pg_row >> 8, pg_row & 0xff, row_start, table->map_sz);
100
101
  /* grab a copy of the free space page map */
102
36
  pg_row = mdb_get_int32(pg_buf, fmt->tab_free_map_offset);
103
36
  if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->freemap_sz))) {
104
0
        fprintf(stderr, "mdb_read_table: Unable to find page row %d\n", pg_row);
105
0
    mdb_free_tabledef(table);
106
0
    return NULL;
107
0
  }
108
36
  table->free_usage_map = g_memdup2((char*)buf + row_start, table->freemap_sz);
109
36
  mdb_debug(MDB_DEBUG_USAGE,"free map found on page %ld row %d start %d len %d\n",
110
36
    pg_row >> 8, pg_row & 0xff, row_start, table->freemap_sz);
111
112
36
  table->first_data_pg = mdb_get_int16(pg_buf, fmt->tab_first_dpg_offset);
113
114
36
  if (entry->props)
115
0
    for (i=0; i<entry->props->len; ++i) {
116
0
      MdbProperties *props = g_ptr_array_index(entry->props, i);
117
0
      if (!props->name)
118
0
        table->props = props;
119
0
    }
120
121
36
  return table;
122
36
}
123
MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type)
124
0
{
125
0
  unsigned int i;
126
0
  MdbCatalogEntry *entry;
127
128
0
  mdb_read_catalog(mdb, obj_type);
129
130
0
  for (i=0; i<mdb->num_catalog; i++) {
131
0
    entry = g_ptr_array_index(mdb->catalog, i);
132
0
    if (!g_ascii_strcasecmp(entry->object_name, table_name))
133
0
      return mdb_read_table(entry);
134
0
  }
135
136
0
  return NULL;
137
0
}
138
139
140
guint32 
141
read_pg_if_32(MdbHandle *mdb, int *cur_pos)
142
0
{
143
0
  char c[4];
144
145
0
  read_pg_if_n(mdb, c, cur_pos, 4);
146
0
  return mdb_get_int32(c, 0);
147
0
}
148
guint16 
149
read_pg_if_16(MdbHandle *mdb, int *cur_pos)
150
0
{
151
0
  char c[2];
152
153
0
  read_pg_if_n(mdb, c, cur_pos, 2);
154
0
  return mdb_get_int16(c, 0);
155
0
}
156
guint8
157
read_pg_if_8(MdbHandle *mdb, int *cur_pos)
158
404
{
159
404
  guint8 c;
160
161
404
  read_pg_if_n(mdb, &c, cur_pos, 1);
162
404
  return c;
163
404
}
164
/*
165
 * Read data into a buffer, advancing pages and setting the
166
 * page cursor as needed.  In the case that buf in NULL, pages
167
 * are still advanced and the page cursor is still updated.
168
 */
169
void * 
170
read_pg_if_n(MdbHandle *mdb, void *buf, int *cur_pos, size_t len)
171
1.21k
{
172
1.21k
  char* _buf = buf;
173
1.21k
  char* _end = buf ? buf + len : NULL;
174
175
1.21k
  if (*cur_pos < 0)
176
0
    return NULL;
177
178
  /* Advance to page which contains the first byte */
179
1.22k
  while (*cur_pos >= mdb->fmt->pg_size) {
180
14
    if (!mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4)))
181
7
      return NULL;
182
7
    *cur_pos -= (mdb->fmt->pg_size - 8);
183
7
  }
184
  /* Copy pages into buffer */
185
1.21k
  while (*cur_pos + len >= (size_t)mdb->fmt->pg_size) {
186
0
    size_t piece_len = mdb->fmt->pg_size - *cur_pos;
187
0
    if (_buf) {
188
0
      if (_buf + piece_len > _end)
189
0
        return NULL;
190
0
      memcpy(_buf, mdb->pg_buf + *cur_pos, piece_len);
191
0
      _buf += piece_len;
192
0
    }
193
0
    len -= piece_len;
194
0
    if (!mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4)))
195
0
      return NULL;
196
0
    *cur_pos = 8;
197
0
  }
198
  /* Copy into buffer from final page */
199
1.21k
  if (len && _buf) {
200
1.17k
    if (_buf + len > _end)
201
0
      return NULL;
202
1.17k
    memcpy(_buf, mdb->pg_buf + *cur_pos, len);
203
1.17k
  }
204
1.21k
  *cur_pos += len;
205
1.21k
  return _buf;
206
1.21k
}
207
208
209
void mdb_append_column(GPtrArray *columns, MdbColumn *in_col)
210
0
{
211
0
  g_ptr_array_add(columns, g_memdup2(in_col,sizeof(MdbColumn)));
212
0
}
213
void mdb_free_columns(GPtrArray *columns)
214
287
{
215
287
  guint i, j;
216
287
  MdbColumn *col;
217
218
287
  if (!columns) return;
219
440
  for (i=0; i<columns->len; i++) {
220
404
    col = (MdbColumn *) g_ptr_array_index(columns, i);
221
404
    if (col->sargs) {
222
0
      for (j=0; j<col->sargs->len; j++) {
223
0
        g_free( g_ptr_array_index(col->sargs, j));
224
0
      }
225
0
      g_ptr_array_free(col->sargs, TRUE);
226
0
    }
227
404
    g_free(col);
228
404
  }
229
36
  g_ptr_array_free(columns, TRUE);
230
36
}
231
GPtrArray *mdb_read_columns(MdbTableDef *table)
232
36
{
233
36
  MdbHandle *mdb = table->entry->mdb;
234
36
  MdbFormatConstants *fmt = mdb->fmt;
235
36
  MdbColumn *pcol;
236
36
  unsigned char *col;
237
36
  unsigned int i;
238
36
  guint j;
239
36
  int cur_pos;
240
36
  size_t name_sz;
241
36
  GPtrArray *allprops;
242
  
243
36
  table->columns = g_ptr_array_new();
244
245
36
  col = g_malloc(fmt->tab_col_entry_size);
246
247
36
  cur_pos = fmt->tab_cols_start_offset + 
248
36
    (table->num_real_idxs * fmt->tab_ridx_entry_size);
249
250
  /* new code based on patch submitted by Tim Nelson 2000.09.27 */
251
252
  /* 
253
  ** column attributes 
254
  */
255
440
  for (i=0;i<table->num_cols;i++) {
256
411
#ifdef MDB_DEBUG
257
  /* printf("column %d\n", i);
258
  mdb_buffer_dump(mdb->pg_buf, cur_pos, fmt->tab_col_entry_size); */
259
411
#endif
260
411
    if (!read_pg_if_n(mdb, col, &cur_pos, fmt->tab_col_entry_size)) {
261
7
      g_free(col);
262
7
      mdb_free_columns(table->columns);
263
7
      return table->columns = NULL;
264
7
    }
265
404
    pcol = g_malloc0(sizeof(MdbColumn));
266
267
404
    pcol->table = table;
268
269
404
    pcol->col_type = col[0];
270
271
    // col_num_offset == 1 or 5
272
404
    pcol->col_num = col[fmt->col_num_offset];
273
274
    //fprintf(stdout,"----- column %d -----\n",pcol->col_num);
275
    // col_var == 3 or 7
276
404
    pcol->var_col_num = mdb_get_int16(col, fmt->tab_col_offset_var);
277
    //fprintf(stdout,"var column pos %d\n",pcol->var_col_num);
278
279
    // col_var == 5 or 9
280
404
    pcol->row_col_num = mdb_get_int16(col, fmt->tab_row_col_num_offset);
281
    //fprintf(stdout,"row column num %d\n",pcol->row_col_num);
282
283
404
    if (pcol->col_type == MDB_NUMERIC || pcol->col_type == MDB_MONEY ||
284
404
        pcol->col_type == MDB_FLOAT || pcol->col_type == MDB_DOUBLE) {
285
37
      pcol->col_scale = col[fmt->col_scale_offset];
286
37
      pcol->col_prec = col[fmt->col_prec_offset];
287
37
    }
288
289
    // col_flags_offset == 13 or 15
290
404
    pcol->is_fixed = col[fmt->col_flags_offset] & 0x01 ? 1 : 0;
291
404
    pcol->is_long_auto = col[fmt->col_flags_offset] & 0x04 ? 1 : 0;
292
404
    pcol->is_uuid_auto = col[fmt->col_flags_offset] & 0x40 ? 1 : 0;
293
294
    // tab_col_offset_fixed == 14 or 21
295
404
    pcol->fixed_offset = mdb_get_int16(col, fmt->tab_col_offset_fixed);
296
    //fprintf(stdout,"fixed column offset %d\n",pcol->fixed_offset);
297
    //fprintf(stdout,"col type %s\n",pcol->is_fixed ? "fixed" : "variable");
298
299
404
    if (pcol->col_type != MDB_BOOL) {
300
      // col_size_offset == 16 or 23
301
348
      pcol->col_size = mdb_get_int16(col, fmt->col_size_offset);
302
348
    } else {
303
56
      pcol->col_size=0;
304
56
    }
305
    
306
404
    g_ptr_array_add(table->columns, pcol);
307
404
  }
308
309
29
  g_free (col);
310
311
  /* 
312
  ** column names - ordered the same as the column attributes table
313
  */
314
433
  for (i=0;i<table->num_cols;i++) {
315
404
    char *tmp_buf;
316
404
    pcol = g_ptr_array_index(table->columns, i);
317
318
404
    if (IS_JET3(mdb))
319
404
      name_sz = read_pg_if_8(mdb, &cur_pos);
320
0
    else
321
0
      name_sz = read_pg_if_16(mdb, &cur_pos);
322
404
    tmp_buf = g_malloc(name_sz);
323
404
    if (read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz))
324
404
      mdb_unicode2ascii(mdb, tmp_buf, name_sz, pcol->name, sizeof(pcol->name));
325
404
    g_free(tmp_buf);
326
404
  }
327
328
  /* Sort the columns by col_num */
329
29
  g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer);
330
331
29
  allprops = table->entry->props;
332
29
  if (allprops)
333
0
    for (i=0;i<table->num_cols;i++) {
334
0
      pcol = g_ptr_array_index(table->columns, i);
335
0
      for (j=0; j<allprops->len; ++j) {
336
0
        MdbProperties *props = g_ptr_array_index(allprops, j);
337
0
        if (props->name && !strcmp(props->name, pcol->name)) {
338
0
          pcol->props = props;
339
0
          break;
340
0
        }
341
342
0
      }
343
0
    }
344
29
  table->index_start = cur_pos;
345
29
  return table->columns;
346
36
}
347
348
void mdb_table_dump(MdbCatalogEntry *entry)
349
0
{
350
0
MdbTableDef *table;
351
0
MdbColumn *col;
352
0
int coln;
353
0
MdbIndex *idx;
354
0
unsigned int i, bitn;
355
0
guint32 pgnum;
356
357
0
  table = mdb_read_table(entry);
358
0
  fprintf(stdout,"definition page     = %lu\n",entry->table_pg);
359
0
  fprintf(stdout,"number of datarows  = %d\n",table->num_rows);
360
0
  fprintf(stdout,"number of columns   = %d\n",table->num_cols);
361
0
  fprintf(stdout,"number of indices   = %d\n",table->num_real_idxs);
362
363
0
  if (table->props)
364
0
    mdb_dump_props(table->props, stdout, 0);
365
0
  mdb_read_columns(table);
366
0
  mdb_read_indices(table);
367
368
0
  for (i=0;i<table->num_cols;i++) {
369
0
    col = g_ptr_array_index(table->columns,i);
370
  
371
0
    fprintf(stdout,"column %d Name: %-20s Type: %s(%d)\n",
372
0
      i, col->name,
373
0
      mdb_get_colbacktype_string(col),
374
0
      col->col_size);
375
0
    if (col->props)
376
0
      mdb_dump_props(col->props, stdout, 0);
377
0
  }
378
379
0
  for (i=0;i<table->num_idxs;i++) {
380
0
    idx = g_ptr_array_index (table->indices, i);
381
0
    mdb_index_dump(table, idx);
382
0
  }
383
0
  if (table->usage_map) {
384
0
    printf("pages reserved by this object\n");
385
0
    printf("usage map pg %" G_GUINT32_FORMAT "\n",
386
0
      table->map_base_pg);
387
0
    printf("free map pg %" G_GUINT32_FORMAT "\n",
388
0
      table->freemap_base_pg);
389
0
    pgnum = mdb_get_int32(table->usage_map,1);
390
    /* the first 5 bytes of the usage map mean something */
391
0
    coln = 0;
392
0
    for (i=5;i<table->map_sz;i++) {
393
0
      for (bitn=0;bitn<8;bitn++) {
394
0
        if (table->usage_map[i] & 1 << bitn) {
395
0
          coln++;
396
0
          printf("%6" G_GUINT32_FORMAT, pgnum);
397
0
          if (coln==10) {
398
0
            printf("\n");
399
0
            coln = 0;
400
0
          } else {
401
0
            printf(" ");
402
0
          }
403
0
        }
404
0
        pgnum++;
405
0
      }
406
0
    }
407
0
    printf("\n");
408
0
  }
409
0
}
410
411
int mdb_is_user_table(MdbCatalogEntry *entry)
412
0
{
413
0
  return ((entry->object_type == MDB_TABLE)
414
0
   && !(entry->flags & 0x80000002)) ? 1 : 0;
415
0
}
416
int mdb_is_system_table(MdbCatalogEntry *entry)
417
0
{
418
0
  return ((entry->object_type == MDB_TABLE)
419
0
   && (entry->flags & 0x80000002)) ? 1 : 0;
420
0
}
421
422
const char *
423
0
mdb_table_get_prop(const MdbTableDef *table, const gchar *key) {
424
0
  if (!table->props)
425
0
    return NULL;
426
0
  return g_hash_table_lookup(table->props->hash, key);
427
0
}
428
429
const char *
430
42
mdb_col_get_prop(const MdbColumn *col, const gchar *key) {
431
42
  if (!col->props)
432
42
    return NULL;
433
0
  return g_hash_table_lookup(col->props->hash, key);
434
42
}
435
436
42
int mdb_col_is_shortdate(const MdbColumn *col) {
437
42
    const char *format = mdb_col_get_prop(col, "Format");
438
42
    return format && !strcmp(format, "Short Date");
439
42
}