1
/* MDB Tools - A library for reading MS Access database file
2
* Copyright (C) 2000 Brian Bruns
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.
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.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the
16
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
* Boston, MA 02110-1301, USA.
27
static gint mdb_col_comparer(MdbColumn **a, MdbColumn **b)
29
if ((*a)->col_num > (*b)->col_num)
31
else if ((*a)->col_num < (*b)->col_num)
37
unsigned char mdb_col_needs_size(int col_type)
39
if (col_type == MDB_TEXT) {
46
MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry)
50
table = (MdbTableDef *) g_malloc0(sizeof(MdbTableDef));
52
strcpy(table->name, entry->object_name);
56
void mdb_free_tabledef(MdbTableDef *table)
59
if (table->is_temp_table) {
61
/* Temp table pages are being stored in memory */
62
for (i=0; i<table->temp_table_pages->len; i++)
63
g_free(g_ptr_array_index(table->temp_table_pages,i));
64
g_ptr_array_free(table->temp_table_pages, TRUE);
65
/* Temp tables use dummy entries */
68
mdb_free_columns(table->columns);
69
mdb_free_indices(table->indices);
70
g_free(table->usage_map);
71
g_free(table->free_usage_map);
74
MdbTableDef *mdb_read_table(MdbCatalogEntry *entry)
77
MdbHandle *mdb = entry->mdb;
78
MdbFormatConstants *fmt = mdb->fmt;
79
int len, row_start, pg_row;
80
void *buf, *pg_buf = mdb->pg_buf;
82
mdb_read_pg(mdb, entry->table_pg);
83
if (mdb_get_byte(pg_buf, 0) != 0x02) /* not a valid table def page */
85
table = mdb_alloc_tabledef(entry);
87
len = mdb_get_int16(pg_buf, 8);
89
table->num_rows = mdb_get_int32(pg_buf, fmt->tab_num_rows_offset);
90
table->num_var_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset-2);
91
table->num_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset);
92
table->num_idxs = mdb_get_int32(pg_buf, fmt->tab_num_idxs_offset);
93
table->num_real_idxs = mdb_get_int32(pg_buf, fmt->tab_num_ridxs_offset);
95
/* grab a copy of the usage map */
96
pg_row = mdb_get_int32(pg_buf, fmt->tab_usage_map_offset);
97
mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->map_sz));
98
table->usage_map = g_memdup((char*)buf + row_start, table->map_sz);
99
if (mdb_get_option(MDB_DEBUG_USAGE))
100
buffer_dump(buf, row_start, table->map_sz);
101
mdb_debug(MDB_DEBUG_USAGE,"usage map found on page %ld row %d start %d len %d",
102
pg_row >> 8, pg_row & 0xff, row_start, table->map_sz);
104
/* grab a copy of the free space page map */
105
pg_row = mdb_get_int32(pg_buf, fmt->tab_free_map_offset);
106
mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->freemap_sz));
107
table->free_usage_map = g_memdup((char*)buf + row_start, table->freemap_sz);
108
mdb_debug(MDB_DEBUG_USAGE,"free map found on page %ld row %d start %d len %d\n",
109
pg_row >> 8, pg_row & 0xff, row_start, table->freemap_sz);
111
table->first_data_pg = mdb_get_int16(pg_buf, fmt->tab_first_dpg_offset);
115
MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type)
118
MdbCatalogEntry *entry;
120
mdb_read_catalog(mdb, obj_type);
122
for (i=0; i<mdb->num_catalog; i++) {
123
entry = g_ptr_array_index(mdb->catalog, i);
124
if (!strcasecmp(entry->object_name, table_name))
125
return mdb_read_table(entry);
133
read_pg_if_32(MdbHandle *mdb, int *cur_pos)
137
read_pg_if_n(mdb, c, cur_pos, 4);
138
return mdb_get_int32(c, 0);
141
read_pg_if_16(MdbHandle *mdb, int *cur_pos)
145
read_pg_if_n(mdb, c, cur_pos, 2);
146
return mdb_get_int16(c, 0);
149
read_pg_if_8(MdbHandle *mdb, int *cur_pos)
153
read_pg_if_n(mdb, &c, cur_pos, 1);
157
* Read data into a buffer, advancing pages and setting the
158
* page cursor as needed. In the case that buf in NULL, pages
159
* are still advanced and the page cursor is still updated.
162
read_pg_if_n(MdbHandle *mdb, void *buf, int *cur_pos, size_t len)
164
char *buf_char = (char *)buf;
165
/* Advance to page which contains the first byte */
166
while (*cur_pos >= mdb->fmt->pg_size) {
167
mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4));
168
*cur_pos -= (mdb->fmt->pg_size - 8);
170
/* Copy pages into buffer */
171
while (*cur_pos + len >= mdb->fmt->pg_size) {
172
int piece_len = mdb->fmt->pg_size - *cur_pos;
174
memcpy(buf_char, mdb->pg_buf + *cur_pos, piece_len);
175
buf_char += piece_len;
178
mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4));
181
/* Copy into buffer from final page */
182
if (len && buf_char) {
183
memcpy(buf_char, mdb->pg_buf + *cur_pos, len);
190
void mdb_append_column(GPtrArray *columns, MdbColumn *in_col)
192
g_ptr_array_add(columns, g_memdup(in_col,sizeof(MdbColumn)));
194
void mdb_free_columns(GPtrArray *columns)
198
if (!columns) return;
199
for (i=0; i<columns->len; i++)
200
g_free (g_ptr_array_index(columns, i));
201
g_ptr_array_free(columns, TRUE);
203
GPtrArray *mdb_read_columns(MdbTableDef *table)
205
MdbHandle *mdb = table->entry->mdb;
206
MdbFormatConstants *fmt = mdb->fmt;
213
table->columns = g_ptr_array_new();
215
col = (unsigned char *) g_malloc(fmt->tab_col_entry_size);
217
cur_pos = fmt->tab_cols_start_offset +
218
(table->num_real_idxs * fmt->tab_ridx_entry_size);
220
/* new code based on patch submitted by Tim Nelson 2000.09.27 */
225
for (i=0;i<table->num_cols;i++) {
227
/* printf("column %d\n", i);
228
buffer_dump(mdb->pg_buf, cur_pos, fmt->tab_col_entry_size); */
230
read_pg_if_n(mdb, col, &cur_pos, fmt->tab_col_entry_size);
231
pcol = (MdbColumn *) g_malloc0(sizeof(MdbColumn));
233
pcol->col_type = col[0];
236
pcol->col_num = col[fmt->col_num_offset];
240
pcol->var_col_num = mdb_get_int16(col, fmt->tab_col_offset_var);
244
pcol->row_col_num = mdb_get_int16(col, fmt->tab_row_col_num_offset);
247
/* FIXME: can this be right in Jet3 and Jet4? */
248
if (pcol->col_type == MDB_NUMERIC) {
249
pcol->col_prec = col[11];
250
pcol->col_scale = col[12];
254
pcol->is_fixed = col[fmt->col_fixed_offset] & 0x01 ? 1 : 0;
257
pcol->fixed_offset = mdb_get_int16(col, fmt->tab_col_offset_fixed);
261
if (pcol->col_type != MDB_BOOL) {
263
pcol->col_size = mdb_get_int16(col, fmt->col_size_offset);
268
g_ptr_array_add(table->columns, pcol);
274
** column names - ordered the same as the column attributes table
276
for (i=0;i<table->num_cols;i++) {
278
pcol = g_ptr_array_index(table->columns, i);
281
name_sz = read_pg_if_16(mdb, &cur_pos);
282
} else if (IS_JET3(mdb)) {
283
name_sz = read_pg_if_8(mdb, &cur_pos);
285
fprintf(stderr,"Unknown MDB version\n");
288
tmp_buf = (char *) g_malloc(name_sz);
289
read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz);
290
mdb_unicode2ascii(mdb, tmp_buf, name_sz, pcol->name, MDB_MAX_OBJ_NAME);
295
/* Sort the columns by col_num */
296
g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer);
298
table->index_start = cur_pos;
299
return table->columns;
303
void mdb_table_dump(MdbCatalogEntry *entry)
309
MdbHandle *mdb = entry->mdb;
310
unsigned int i, bitn;
313
table = mdb_read_table(entry);
314
fprintf(stdout,"definition page = %lu\n",entry->table_pg);
315
fprintf(stdout,"number of datarows = %d\n",table->num_rows);
316
fprintf(stdout,"number of columns = %d\n",table->num_cols);
317
fprintf(stdout,"number of indices = %d\n",table->num_real_idxs);
319
mdb_read_columns(table);
320
mdb_read_indices(table);
322
for (i=0;i<table->num_cols;i++) {
323
col = g_ptr_array_index(table->columns,i);
325
fprintf(stdout,"column %d Name: %-20s Type: %s(%d)\n",
327
mdb_get_coltype_string(mdb->default_backend, col->col_type),
331
for (i=0;i<table->num_idxs;i++) {
332
idx = g_ptr_array_index (table->indices, i);
333
mdb_index_dump(table, idx);
335
if (table->usage_map) {
336
printf("pages reserved by this object\n");
337
printf("usage map pg %" G_GUINT32_FORMAT "\n",
339
printf("free map pg %" G_GUINT32_FORMAT "\n",
340
table->freemap_base_pg);
341
pgnum = mdb_get_int32(table->usage_map,1);
342
/* the first 5 bytes of the usage map mean something */
344
for (i=5;i<table->map_sz;i++) {
345
for (bitn=0;bitn<8;bitn++) {
346
if (table->usage_map[i] & 1 << bitn) {
348
printf("%6" G_GUINT32_FORMAT, pgnum);
364
int mdb_is_user_table(MdbCatalogEntry *entry)
366
return ((entry->object_type == MDB_TABLE)
367
&& !(entry->flags & 0x80000002)) ? 1 : 0;
369
int mdb_is_system_table(MdbCatalogEntry *entry)
371
return ((entry->object_type == MDB_TABLE)
372
&& (entry->flags & 0x80000002)) ? 1 : 0;