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
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "mdbtools.h"
21
22 #ifdef DMALLOC
23 #include "dmalloc.h"
24 #endif
25
26
mdb_col_comparer(MdbColumn ** a,MdbColumn ** b)27 static gint mdb_col_comparer(MdbColumn **a, MdbColumn **b)
28 {
29 if ((*a)->col_num > (*b)->col_num)
30 return 1;
31 else if ((*a)->col_num < (*b)->col_num)
32 return -1;
33 else
34 return 0;
35 }
36
mdb_col_needs_size(int col_type)37 unsigned char mdb_col_needs_size(int col_type)
38 {
39 if (col_type == MDB_TEXT) {
40 return TRUE;
41 } else {
42 return FALSE;
43 }
44 }
45
mdb_alloc_tabledef(MdbCatalogEntry * entry)46 MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry)
47 {
48 MdbTableDef *table;
49
50 table = (MdbTableDef *) g_malloc0(sizeof(MdbTableDef));
51 table->entry=entry;
52 strcpy(table->name, entry->object_name);
53
54 return table;
55 }
mdb_free_tabledef(MdbTableDef * table)56 void mdb_free_tabledef(MdbTableDef *table)
57 {
58 if (!table) return;
59 if (table->is_temp_table) {
60 unsigned int i;
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 */
66 g_free(table->entry);
67 }
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);
72 g_free(table);
73 }
mdb_read_table(MdbCatalogEntry * entry)74 MdbTableDef *mdb_read_table(MdbCatalogEntry *entry)
75 {
76 MdbTableDef *table;
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;
81
82 mdb_read_pg(mdb, entry->table_pg);
83 if (mdb_get_byte(pg_buf, 0) != 0x02) /* not a valid table def page */
84 return NULL;
85 table = mdb_alloc_tabledef(entry);
86
87 len = mdb_get_int16(pg_buf, 8);
88
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);
94
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(buf + row_start, table->map_sz);
99 if (mdb_get_option(MDB_DEBUG_USAGE))
100 buffer_dump(buf, row_start, row_start+table->map_sz-1);
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);
103
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(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);
110
111 table->first_data_pg = mdb_get_int16(pg_buf, fmt->tab_first_dpg_offset);
112
113 return table;
114 }
mdb_read_table_by_name(MdbHandle * mdb,gchar * table_name,int obj_type)115 MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type)
116 {
117 unsigned int i;
118 MdbCatalogEntry *entry;
119
120 mdb_read_catalog(mdb, obj_type);
121
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);
126 }
127
128 return NULL;
129 }
130
131 /*
132 ** read the next page if offset is > pg_size
133 ** return true if page was read
134 */
135 int
read_pg_if(MdbHandle * mdb,int * cur_pos,int offset)136 read_pg_if(MdbHandle *mdb, int *cur_pos, int offset)
137 {
138 if (*cur_pos + offset >= mdb->fmt->pg_size) {
139 mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4));
140 *cur_pos = 8 - (mdb->fmt->pg_size - (*cur_pos));
141 return 1;
142 }
143 return 0;
144 }
145 guint32
read_pg_if_32(MdbHandle * mdb,int * cur_pos)146 read_pg_if_32(MdbHandle *mdb, int *cur_pos)
147 {
148 unsigned char c[4];
149 int i, rc = 0;
150
151 for (i=0;i<4;i++) {
152 rc += read_pg_if(mdb, cur_pos, i);
153 c[i] = mdb->pg_buf[(*cur_pos) + i];
154 }
155 return mdb_get_int32(c, 0);
156 }
157 guint16
read_pg_if_16(MdbHandle * mdb,int * cur_pos)158 read_pg_if_16(MdbHandle *mdb, int *cur_pos)
159 {
160 unsigned char low_byte, high_byte;
161 int rc = 0;
162
163 rc += read_pg_if(mdb, cur_pos, 0);
164 low_byte = mdb->pg_buf[*cur_pos];
165 rc += read_pg_if(mdb, cur_pos, 1);
166 high_byte = mdb->pg_buf[(*cur_pos) + 1];
167
168 return (high_byte * 256 + low_byte);
169 }
170 guint16
read_pg_if_n(MdbHandle * mdb,unsigned char * buf,int * cur_pos,int len)171 read_pg_if_n(MdbHandle *mdb, unsigned char *buf, int *cur_pos, int len)
172 {
173 if (*cur_pos + len < mdb->fmt->pg_size) {
174 memcpy(buf, &mdb->pg_buf[*cur_pos], len);
175 return 0;
176 } else {
177 int half = mdb->fmt->pg_size - *cur_pos;
178 memcpy(buf, &mdb->pg_buf[*cur_pos], half);
179 mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4));
180 memcpy(buf + half, &mdb->pg_buf[8], len - half);
181 *cur_pos = 8 - half;
182 return 1;
183 }
184 }
185
mdb_append_column(GPtrArray * columns,MdbColumn * in_col)186 void mdb_append_column(GPtrArray *columns, MdbColumn *in_col)
187 {
188 g_ptr_array_add(columns, g_memdup(in_col,sizeof(MdbColumn)));
189 }
mdb_free_columns(GPtrArray * columns)190 void mdb_free_columns(GPtrArray *columns)
191 {
192 unsigned int i;
193
194 if (!columns) return;
195 for (i=0; i<columns->len; i++)
196 g_free (g_ptr_array_index(columns, i));
197 g_ptr_array_free(columns, TRUE);
198 }
mdb_read_columns(MdbTableDef * table)199 GPtrArray *mdb_read_columns(MdbTableDef *table)
200 {
201 MdbHandle *mdb = table->entry->mdb;
202 MdbFormatConstants *fmt = mdb->fmt;
203 MdbColumn *pcol;
204 unsigned char *col;
205 unsigned int i;
206 int cur_pos, name_sz;
207
208 table->columns = g_ptr_array_new();
209
210 col = (unsigned char *) g_malloc(fmt->tab_col_entry_size);
211
212 cur_pos = fmt->tab_cols_start_offset +
213 (table->num_real_idxs * fmt->tab_ridx_entry_size);
214
215 /* new code based on patch submitted by Tim Nelson 2000.09.27 */
216
217 /*
218 ** column attributes
219 */
220 for (i=0;i<table->num_cols;i++) {
221 #ifdef MDB_DEBUG
222 /* printf("column %d\n", i);
223 buffer_dump(mdb->pg_buf, cur_pos ,cur_pos + 18); */
224 #endif
225 read_pg_if_n(mdb, col, &cur_pos, fmt->tab_col_entry_size);
226 cur_pos += fmt->tab_col_entry_size;
227 pcol = (MdbColumn *) g_malloc0(sizeof(MdbColumn));
228
229 pcol->col_type = col[0];
230
231 // col_num_offset == 1 or 5
232 pcol->col_num = col[fmt->col_num_offset];
233
234 //fprintf(stdout,"----- column %d -----\n",pcol->col_num);
235 // col_var == 3 or 7
236 pcol->var_col_num = mdb_get_int16(col, fmt->tab_col_offset_var);
237 //fprintf(stdout,"var column pos %d\n",pcol->var_col_num);
238
239 // col_var == 5 or 9
240 pcol->row_col_num = mdb_get_int16(col, fmt->tab_row_col_num_offset);
241 //fprintf(stdout,"row column num %d\n",pcol->row_col_num);
242
243 /* FIXME: can this be right in Jet3 and Jet4? */
244 if (pcol->col_type == MDB_NUMERIC) {
245 pcol->col_prec = col[11];
246 pcol->col_scale = col[12];
247 }
248
249 // col_fixed_offset == 13 or 15
250 pcol->is_fixed = col[fmt->col_fixed_offset] & 0x01 ? 1 : 0;
251
252 // col_fixed_offset == 13 or 15
253 pcol->fixed_offset = mdb_get_int16(col, fmt->tab_col_offset_fixed);
254 //fprintf(stdout,"fixed column offset %d\n",pcol->fixed_offset);
255 //fprintf(stdout,"col type %s\n",pcol->is_fixed ? "fixed" : "variable");
256
257 if (pcol->col_type != MDB_BOOL) {
258 // col_size_offset == 16 or 23
259 pcol->col_size = mdb_get_int16(col, fmt->col_size_offset);
260 } else {
261 pcol->col_size=0;
262 }
263
264 g_ptr_array_add(table->columns, pcol);
265 }
266
267 g_free (col);
268
269 /*
270 ** column names - ordered the same as the column attributes table
271 */
272 for (i=0;i<table->num_cols;i++) {
273 unsigned char *tmp_buf;
274 pcol = g_ptr_array_index(table->columns, i);
275
276 if (IS_JET4(mdb)) {
277 name_sz = read_pg_if_16(mdb, &cur_pos);
278 cur_pos += 2;
279 } else if (IS_JET3(mdb)) {
280 read_pg_if(mdb, &cur_pos, 0);
281 name_sz = mdb->pg_buf[cur_pos];
282 cur_pos++;
283 } else {
284 fprintf(stderr,"Unknown MDB version\n");
285 continue;
286 }
287 tmp_buf = (unsigned char *) g_malloc(name_sz);
288 read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz);
289 mdb_unicode2ascii(mdb, tmp_buf, name_sz, pcol->name, MDB_MAX_OBJ_NAME);
290 g_free(tmp_buf);
291 cur_pos += name_sz;
292
293 }
294
295 /* Sort the columns by col_num */
296 g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer);
297
298 table->index_start = cur_pos;
299 return table->columns;
300 }
301
mdb_table_dump(MdbCatalogEntry * entry)302 void mdb_table_dump(MdbCatalogEntry *entry)
303 {
304 MdbTableDef *table;
305 MdbColumn *col;
306 int coln;
307 MdbIndex *idx;
308 MdbHandle *mdb = entry->mdb;
309 unsigned int i, bitn;
310 guint32 pgnum;
311
312 table = mdb_read_table(entry);
313 fprintf(stdout,"definition page = %lu\n",entry->table_pg);
314 fprintf(stdout,"number of datarows = %d\n",table->num_rows);
315 fprintf(stdout,"number of columns = %d\n",table->num_cols);
316 fprintf(stdout,"number of indices = %d\n",table->num_real_idxs);
317
318 mdb_read_columns(table);
319 mdb_read_indices(table);
320
321 for (i=0;i<table->num_cols;i++) {
322 col = g_ptr_array_index(table->columns,i);
323
324 fprintf(stdout,"column %d Name: %-20s Type: %s(%d)\n",
325 i, col->name,
326 mdb_get_coltype_string(mdb->default_backend, col->col_type),
327 col->col_size);
328 }
329
330 for (i=0;i<table->num_idxs;i++) {
331 idx = g_ptr_array_index (table->indices, i);
332 mdb_index_dump(table, idx);
333 }
334 if (table->usage_map) {
335 printf("pages reserved by this object\n");
336 printf("usage map pg %" G_GUINT32_FORMAT "\n",
337 table->map_base_pg);
338 printf("free map pg %" G_GUINT32_FORMAT "\n",
339 table->freemap_base_pg);
340 pgnum = mdb_get_int32(table->usage_map,1);
341 /* the first 5 bytes of the usage map mean something */
342 coln = 0;
343 for (i=5;i<table->map_sz;i++) {
344 for (bitn=0;bitn<8;bitn++) {
345 if (table->usage_map[i] & 1 << bitn) {
346 coln++;
347 printf("%6" G_GUINT32_FORMAT, pgnum);
348 if (coln==10) {
349 printf("\n");
350 coln = 0;
351 } else {
352 printf(" ");
353 }
354 }
355 pgnum++;
356 }
357 }
358 printf("\n");
359 }
360 }
361
mdb_is_user_table(MdbCatalogEntry * entry)362 int mdb_is_user_table(MdbCatalogEntry *entry)
363 {
364 return ((entry->object_type == MDB_TABLE)
365 && !(entry->flags & 0x80000002)) ? 1 : 0;
366 }
mdb_is_system_table(MdbCatalogEntry * entry)367 int mdb_is_system_table(MdbCatalogEntry *entry)
368 {
369 return ((entry->object_type == MDB_TABLE)
370 && (entry->flags & 0x80000002)) ? 1 : 0;
371 }
372