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 #include "time.h"
22 #include "math.h"
23 
24 #ifdef DMALLOC
25 #include "dmalloc.h"
26 #endif
27 
28 
29 //static int mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg);
30 static int mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum);
31 
32 void
_mdb_put_int16(unsigned char * buf,guint32 offset,guint32 value)33 _mdb_put_int16(unsigned char *buf, guint32 offset, guint32 value)
34 {
35 	buf[offset] = value % 256;
36 	value /= 256;
37 	buf[offset+1] = value % 256;
38 }
39 void
_mdb_put_int32(unsigned char * buf,guint32 offset,guint32 value)40 _mdb_put_int32(unsigned char *buf, guint32 offset, guint32 value)
41 {
42 	buf[offset] = value % 256;
43 	value /= 256;
44 	buf[offset+1] = value % 256;
45 	value /= 256;
46 	buf[offset+2] = value % 256;
47 	value /= 256;
48 	buf[offset+3] = value % 256;
49 }
50 void
_mdb_put_int24(unsigned char * buf,guint32 offset,guint32 value)51 _mdb_put_int24(unsigned char *buf, guint32 offset, guint32 value)
52 {
53 	buf[offset] = value % 256;
54 	value /= 256;
55 	buf[offset+1] = value % 256;
56 	value /= 256;
57 	buf[offset+2] = value % 256;
58 }
59 void
_mdb_put_int24_msb(unsigned char * buf,guint32 offset,guint32 value)60 _mdb_put_int24_msb(unsigned char *buf, guint32 offset, guint32 value)
61 {
62 	buf[offset+2] = value % 256;
63 	value /= 256;
64 	buf[offset+1] = value % 256;
65 	value /= 256;
66 	buf[offset] = value % 256;
67 }
68 ssize_t
mdb_write_pg(MdbHandle * mdb,unsigned long pg)69 mdb_write_pg(MdbHandle *mdb, unsigned long pg)
70 {
71 	ssize_t len;
72 	struct stat status;
73 	off_t offset = pg * mdb->fmt->pg_size;
74 
75 	fstat(mdb->f->fd, &status);
76 	/* is page beyond current size + 1 ? */
77 	if (status.st_size < offset + mdb->fmt->pg_size) {
78 		fprintf(stderr,"offset %lu is beyond EOF\n",offset);
79 		return 0;
80 	}
81 	lseek(mdb->f->fd, offset, SEEK_SET);
82 	len = write(mdb->f->fd,mdb->pg_buf,mdb->fmt->pg_size);
83 	if (len==-1) {
84 		perror("write");
85 		return 0;
86 	} else if (len<mdb->fmt->pg_size) {
87 	/* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->pg_size); */
88 		return 0;
89 	}
90 	mdb->cur_pos = 0;
91 	return len;
92 }
93 
94 static int
mdb_is_col_indexed(MdbTableDef * table,int colnum)95 mdb_is_col_indexed(MdbTableDef *table, int colnum)
96 {
97 	unsigned int i, j;
98 	MdbIndex *idx;
99 
100 	for (i=0;i<table->num_idxs;i++) {
101 		idx = g_ptr_array_index (table->indices, i);
102 		for (j=0;j<idx->num_keys;j++) {
103 			if (idx->key_col_num[j]==colnum) return 1;
104 		}
105 	}
106 	return 0;
107 }
108 
109 static void
mdb_crack_row4(MdbHandle * mdb,int row_start,int row_end,unsigned int bitmask_sz,unsigned int row_var_cols,unsigned int * var_col_offsets)110 mdb_crack_row4(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets)
111 {
112 	unsigned int i;
113 
114 	for (i=0; i<row_var_cols+1; i++) {
115 		var_col_offsets[i] = mdb_get_int16(mdb->pg_buf,
116 			row_end - bitmask_sz - 3 - (i*2));
117 	}
118 }
119 static void
mdb_crack_row3(MdbHandle * mdb,int row_start,int row_end,unsigned int bitmask_sz,unsigned int row_var_cols,unsigned int * var_col_offsets)120 mdb_crack_row3(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets)
121 {
122 	unsigned int i;
123 	unsigned int num_jumps = 0, jumps_used = 0;
124 	unsigned int col_ptr, row_len;
125 
126 	row_len = row_end - row_start + 1;
127 	num_jumps = (row_len - 1) / 256;
128 	col_ptr = row_end - bitmask_sz - num_jumps - 1;
129 	/* If last jump is a dummy value, ignore it */
130 	if ((col_ptr-row_start-row_var_cols)/256 < num_jumps)
131 		num_jumps--;
132 
133 	jumps_used = 0;
134 	for (i=0; i<row_var_cols+1; i++) {
135 		while ((jumps_used < num_jumps)
136 		 && (i == mdb->pg_buf[row_end-bitmask_sz-jumps_used-1])) {
137 			jumps_used++;
138 		}
139 		var_col_offsets[i] = mdb->pg_buf[col_ptr-i]+(jumps_used*256);
140 	}
141 }
142 /**
143  * mdb_crack_row:
144  * @table: Table that the row belongs to
145  * @row_start: offset to start of row on current page
146  * @row_end: offset to end of row on current page
147  * @fields: pointer to MdbField array to be popluated by mdb_crack_row
148  *
149  * Cracks a row buffer apart into its component fields.
150  *
151  * A row buffer is that portion of a data page which contains the values for
152  * that row.  Its beginning and end can be found in the row offset table.
153  *
154  * The resulting MdbField array contains pointers into the row for each field
155  * present.  Be aware that by modifying field[]->value, you would be modifying
156  * the row buffer itself, not a copy.
157  *
158  * This routine is mostly used internally by mdb_fetch_row() but may have some
159  * applicability for advanced application programs.
160  *
161  * Return value: number of fields present.
162  */
163 int
mdb_crack_row(MdbTableDef * table,int row_start,int row_end,MdbField * fields)164 mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields)
165 {
166 	MdbColumn *col;
167 	MdbCatalogEntry *entry = table->entry;
168 	MdbHandle *mdb = entry->mdb;
169 	unsigned char *pg_buf = mdb->pg_buf;
170 	unsigned int row_var_cols=0, row_cols;
171 	unsigned char *nullmask;
172 	unsigned int bitmask_sz;
173 	unsigned int *var_col_offsets = NULL;
174 	unsigned int fixed_cols_found, row_fixed_cols;
175 	unsigned int col_count_size;
176 	unsigned int i;
177 
178 	if (mdb_get_option(MDB_DEBUG_ROW)) {
179 		buffer_dump(pg_buf, row_start, row_end);
180 	}
181 
182 	if (IS_JET4(mdb)) {
183 		row_cols = mdb_get_int16(mdb->pg_buf, row_start);
184 		col_count_size = 2;
185 	} else {
186 		row_cols = pg_buf[row_start];
187 		col_count_size = 1;
188 	}
189 
190 	bitmask_sz = (row_cols + 7) / 8;
191 	nullmask = &pg_buf[row_end - bitmask_sz + 1];
192 
193 	/* read table of variable column locations */
194 	row_var_cols = IS_JET4(mdb) ?
195 		mdb_get_int16(mdb->pg_buf, row_end - bitmask_sz - 1) :
196 		pg_buf[row_end - bitmask_sz];
197 	var_col_offsets = (unsigned int *)g_malloc((row_var_cols+1)*sizeof(int));
198 	if (table->num_var_cols > 0) {
199 		if (IS_JET4(mdb)) {
200 			mdb_crack_row4(mdb, row_start, row_end, bitmask_sz,
201 				 row_var_cols, var_col_offsets);
202 		} else {
203 			mdb_crack_row3(mdb, row_start, row_end, bitmask_sz,
204 				 row_var_cols, var_col_offsets);
205 		}
206 	}
207 
208 	fixed_cols_found = 0;
209 	row_fixed_cols = row_cols - row_var_cols;
210 
211 	if (mdb_get_option(MDB_DEBUG_ROW)) {
212 		fprintf(stdout,"bitmask_sz %d\n", bitmask_sz);
213 		fprintf(stdout,"row_var_cols %d\n", row_var_cols);
214 		fprintf(stdout,"row_fixed_cols %d\n", row_fixed_cols);
215 	}
216 
217 	for (i=0;i<table->num_cols;i++) {
218 		unsigned int byte_num, bit_num;
219 		unsigned int col_start;
220 		col = g_ptr_array_index(table->columns,i);
221 		fields[i].colnum = i;
222 		fields[i].is_fixed = (mdb_is_fixed_col(col)) ? 1 : 0;
223 		byte_num = col->col_num / 8;
224 		bit_num = col->col_num % 8;
225 		/* logic on nulls is reverse, 1 is not null, 0 is null */
226 		fields[i].is_null = nullmask[byte_num] & (1 << bit_num) ? 0 : 1;
227 
228 		if ((fields[i].is_fixed)
229 		 && (fixed_cols_found < row_fixed_cols)) {
230 			col_start = col->fixed_offset + col_count_size;
231 			fields[i].start = row_start + col_start;
232 			fields[i].value = &pg_buf[row_start + col_start];
233 			fields[i].siz = col->col_size;
234 			fixed_cols_found++;
235 		/* Use col->var_col_num because a deleted column is still
236 		 * present in the variable column offsets table for the row */
237 		} else if ((!fields[i].is_fixed)
238 		 && (col->var_col_num < row_var_cols)) {
239 			col_start = var_col_offsets[col->var_col_num];
240 			fields[i].start = row_start + col_start;
241 			fields[i].value = &pg_buf[row_start + col_start];
242 			fields[i].siz = var_col_offsets[(col->var_col_num)+1] -
243 		                col_start;
244 		} else {
245 			fields[i].start = 0;
246 			fields[i].value = NULL;
247 			fields[i].siz = 0;
248 			fields[i].is_null = 1;
249 		}
250 	}
251 
252 	g_free(var_col_offsets);
253 	return row_cols;
254 }
255 
256 static int
mdb_pack_null_mask(unsigned char * buffer,int num_fields,MdbField * fields)257 mdb_pack_null_mask(unsigned char *buffer, int num_fields, MdbField *fields)
258 {
259 	int pos = 0, bit = 0, byte = 0;
260 	int i;
261 
262 	/* 'Not null' bitmap */
263 	for (i=0; i<num_fields; i++) {
264 		/* column is null if bit is clear (0) */
265 		if (!fields[i].is_null) {
266 			byte |= 1 << bit;
267 			//printf("%d %d %d %d\n", i, bit, 1 << bit, byte);
268 		}
269 		bit++;
270 		if (bit==8) {
271 			buffer[pos++] = byte;
272 			bit = byte = 0;
273 		}
274 	}
275 	/* if we've written any bits to the current byte, flush it */
276 	if (bit)
277 		buffer[pos++] = byte;
278 
279 	return pos;
280 }
281 /* fields must be ordered with fixed columns first, then vars, subsorted by
282  * column number */
283 static int
mdb_pack_row4(MdbTableDef * table,unsigned char * row_buffer,unsigned int num_fields,MdbField * fields)284 mdb_pack_row4(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields)
285 {
286 	unsigned int pos = 0;
287 	unsigned int var_cols = 0;
288 	unsigned int i;
289 
290 	row_buffer[pos++] = num_fields & 0xff;
291 	row_buffer[pos++] = (num_fields >> 8) & 0xff;
292 
293 	/* Fixed length columns */
294 	for (i=0;i<num_fields;i++) {
295 		if (fields[i].is_fixed) {
296 			fields[i].offset = pos;
297 			if (!fields[i].is_null) {
298 				memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
299 			}
300 			pos += fields[i].siz;
301 		}
302 	}
303 	/* For tables without variable-length columns */
304 	if (table->num_var_cols == 0) {
305 		pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
306 		return pos;
307 	}
308 	/* Variable length columns */
309 	for (i=0;i<num_fields;i++) {
310 		if (!fields[i].is_fixed) {
311 			var_cols++;
312 			fields[i].offset = pos;
313 			if (! fields[i].is_null) {
314 				memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
315 				pos += fields[i].siz;
316 			}
317 		}
318 	}
319 	/* EOD */
320 	row_buffer[pos] = pos & 0xff;
321 	row_buffer[pos+1] = (pos >> 8) & 0xff;
322 	pos += 2;
323 
324 	/* Offsets of the variable-length columns */
325 	for (i=num_fields; i>0; i--) {
326 		if (!fields[i-1].is_fixed) {
327 			row_buffer[pos++] = fields[i-1].offset & 0xff;
328 			row_buffer[pos++] = (fields[i-1].offset >> 8) & 0xff;
329 		}
330 	}
331 	/* Number of variable-length columns */
332 	row_buffer[pos++] = var_cols & 0xff;
333 	row_buffer[pos++] = (var_cols >> 8) & 0xff;
334 
335 	pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
336 	return pos;
337 }
338 
339 static int
mdb_pack_row3(MdbTableDef * table,unsigned char * row_buffer,unsigned int num_fields,MdbField * fields)340 mdb_pack_row3(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields)
341 {
342 	unsigned int pos = 0;
343 	unsigned int var_cols = 0;
344 	unsigned int i, j;
345 	unsigned char *offset_high;
346 
347 	row_buffer[pos++] = num_fields;
348 
349 	/* Fixed length columns */
350 	for (i=0;i<num_fields;i++) {
351 		if (fields[i].is_fixed) {
352 			fields[i].offset = pos;
353 			if (!fields[i].is_null) {
354 				memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
355 			}
356 			pos += fields[i].siz;
357 		}
358 	}
359 	/* For tables without variable-length columns */
360 	if (table->num_var_cols == 0) {
361 		pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
362 		return pos;
363 	}
364 	/* Variable length columns */
365 	for (i=0;i<num_fields;i++) {
366 		if (!fields[i].is_fixed) {
367 			var_cols++;
368 			fields[i].offset = pos;
369 			if (! fields[i].is_null) {
370 				memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
371 				pos += fields[i].siz;
372 			}
373 		}
374 	}
375 
376 	offset_high = (unsigned char *) g_malloc(var_cols+1);
377 	offset_high[0] = (pos << 8) & 0xff;
378 	j = 1;
379 
380 	/* EOD */
381 	row_buffer[pos] = pos & 0xff;
382 	pos++;
383 
384 	/* Variable length column offsets */
385 	for (i=num_fields; i>0; i--) {
386 		if (!fields[i-1].is_fixed) {
387 			row_buffer[pos++] = fields[i-1].offset & 0xff;
388 			offset_high[j++] = (fields[i-1].offset << 8) & 0xff;
389 		}
390 	}
391 
392 	/* Dummy jump table entry */
393 	if (offset_high[0] < (pos+(num_fields+7)/8-1)/255) {
394 		row_buffer[pos++] = 0xff;
395 	}
396 	/* Jump table */
397 	for (i=0; i<var_cols; i++) {
398 		if (offset_high[i] > offset_high[i+1]) {
399 			row_buffer[pos++] = var_cols-i;
400 		}
401 	}
402 	g_free(offset_high);
403 
404 	row_buffer[pos++] = var_cols;
405 
406 	pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
407 	return pos;
408 }
409 int
mdb_pack_row(MdbTableDef * table,unsigned char * row_buffer,int unsigned num_fields,MdbField * fields)410 mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, int unsigned num_fields, MdbField *fields)
411 {
412 	if (table->is_temp_table) {
413 		unsigned int i;
414 		for (i=0; i<num_fields; i++) {
415 			MdbColumn *c = g_ptr_array_index(table->columns, i);
416 			fields[i].is_null = (fields[i].value) ? 0 : 1;
417 			fields[i].colnum = i;
418 			fields[i].is_fixed = c->is_fixed;
419 			if ((c->col_type != MDB_TEXT)
420 			 && (c->col_type != MDB_MEMO)) {
421 				fields[i].siz = c->col_size;
422 			}
423 		}
424 	}
425 	if (IS_JET4(table->entry->mdb)) {
426 		return mdb_pack_row4(table, row_buffer, num_fields, fields);
427 	} else {
428 		return mdb_pack_row3(table, row_buffer, num_fields, fields);
429 	}
430 }
431 int
mdb_pg_get_freespace(MdbHandle * mdb)432 mdb_pg_get_freespace(MdbHandle *mdb)
433 {
434 	int rows, free_start, free_end;
435 	int row_count_offset = mdb->fmt->row_count_offset;
436 
437 	rows = mdb_get_int16(mdb->pg_buf, row_count_offset);
438 	free_start = row_count_offset + 2 + (rows * 2);
439 	free_end = mdb_get_int16(mdb->pg_buf, row_count_offset + (rows * 2));
440 	mdb_debug(MDB_DEBUG_WRITE,"free space left on page = %d", free_end - free_start);
441 	return (free_end - free_start);
442 }
443 unsigned char *
mdb_new_leaf_pg(MdbCatalogEntry * entry)444 mdb_new_leaf_pg(MdbCatalogEntry *entry)
445 {
446 	MdbHandle *mdb = entry->mdb;
447 	unsigned char *new_pg;
448 
449 	new_pg = (unsigned char *) g_malloc0(mdb->fmt->pg_size);
450 
451 	new_pg[0]=0x04;
452 	new_pg[1]=0x01;
453 	_mdb_put_int32(new_pg, 4, entry->table_pg);
454 
455 	return new_pg;
456 }
457 unsigned char *
mdb_new_data_pg(MdbCatalogEntry * entry)458 mdb_new_data_pg(MdbCatalogEntry *entry)
459 {
460 	MdbFormatConstants *fmt = entry->mdb->fmt;
461 	unsigned char *new_pg;
462 
463 	new_pg = (unsigned char *) g_malloc0(fmt->pg_size);
464 
465 	new_pg[0]=0x01;
466 	new_pg[1]=0x01;
467 	_mdb_put_int16(new_pg, 2, fmt->pg_size - fmt->row_count_offset - 2);
468 	_mdb_put_int32(new_pg, 4, entry->table_pg);
469 
470 	return new_pg;
471 }
472 
473 int
mdb_update_indexes(MdbTableDef * table,int num_fields,MdbField * fields,guint32 pgnum,guint16 rownum)474 mdb_update_indexes(MdbTableDef *table, int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum)
475 {
476 	unsigned int i;
477 	MdbIndex *idx;
478 
479 	for (i=0;i<table->num_idxs;i++) {
480 		idx = g_ptr_array_index (table->indices, i);
481 		mdb_debug(MDB_DEBUG_WRITE,"Updating %s (%d).", idx->name, idx->index_type);
482 		if (idx->index_type==1) {
483 			mdb_update_index(table, idx, num_fields, fields, pgnum, rownum);
484 		}
485 	}
486 	return 1;
487 }
488 
489 int
mdb_init_index_chain(MdbTableDef * table,MdbIndex * idx)490 mdb_init_index_chain(MdbTableDef *table, MdbIndex *idx)
491 {
492 	MdbCatalogEntry *entry = table->entry;
493 	MdbHandle *mdb = entry->mdb;
494 
495 	table->scan_idx = idx;
496 	table->chain = g_malloc0(sizeof(MdbIndexChain));
497 	table->mdbidx = mdb_clone_handle(mdb);
498 	mdb_read_pg(table->mdbidx, table->scan_idx->first_pg);
499 
500 	return 1;
501 }
502 
503 int
mdb_update_index(MdbTableDef * table,MdbIndex * idx,unsigned int num_fields,MdbField * fields,guint32 pgnum,guint16 rownum)504 mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum)
505 {
506 	MdbCatalogEntry *entry = table->entry;
507 	MdbHandle *mdb = entry->mdb;
508 	int idx_xref[16];
509 	unsigned int i, j;
510 	MdbIndexChain *chain;
511 	MdbField idx_fields[10];
512 
513 	for (i = 0; i < idx->num_keys; i++) {
514 		for (j = 0; j < num_fields; j++) {
515 			// key_col_num is 1 based, can't remember why though
516 			if (fields[j].colnum == idx->key_col_num[i]-1) {
517 				idx_xref[i] = j;
518 				idx_fields[i] = fields[j];
519 			}
520 		}
521 	}
522 /*
523 	for (i = 0; i < idx->num_keys; i++) {
524 		fprintf(stdout, "key col %d (%d) is mapped to field %d (%d %d)\n",
525 			i, idx->key_col_num[i], idx_xref[i], fields[idx_xref[i]].colnum,
526 			fields[idx_xref[i]].siz);
527 	}
528 	for (i = 0; i < num_fields; i++) {
529 		fprintf(stdout, "%d (%d %d)\n",
530 			i, fields[i].colnum,
531 			fields[i].siz);
532 	}
533 */
534 
535 	chain = g_malloc0(sizeof(MdbIndexChain));
536 
537 	mdb_index_find_row(mdb, idx, chain, pgnum, rownum);
538 	//printf("chain depth = %d\n", chain->cur_depth);
539 	//printf("pg = %" G_GUINT32_FORMAT "\n",
540 		//chain->pages[chain->cur_depth-1].pg);
541 	//mdb_copy_index_pg(table, idx, &chain->pages[chain->cur_depth-1]);
542 	mdb_add_row_to_leaf_pg(table, idx, &chain->pages[chain->cur_depth-1], idx_fields, pgnum, rownum);
543 
544 	return 1;
545 }
546 
547 int
mdb_insert_row(MdbTableDef * table,int num_fields,MdbField * fields)548 mdb_insert_row(MdbTableDef *table, int num_fields, MdbField *fields)
549 {
550 	int new_row_size;
551 	unsigned char row_buffer[4096];
552 	MdbCatalogEntry *entry = table->entry;
553 	MdbHandle *mdb = entry->mdb;
554 	MdbFormatConstants *fmt = mdb->fmt;
555 	guint32 pgnum;
556 	guint16 rownum;
557 
558 	if (!mdb->f->writable) {
559 		fprintf(stderr, "File is not open for writing\n");
560 		return 0;
561 	}
562 	new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields);
563 	if (mdb_get_option(MDB_DEBUG_WRITE)) {
564 		buffer_dump(row_buffer, 0, new_row_size-1);
565 	}
566 	pgnum = mdb_map_find_next_freepage(table, new_row_size);
567 	if (!pgnum) {
568 		fprintf(stderr, "Unable to allocate new page.\n");
569 		return 0;
570 	}
571 
572 	rownum = mdb_add_row_to_pg(table, row_buffer, new_row_size);
573 
574 	if (mdb_get_option(MDB_DEBUG_WRITE)) {
575 		buffer_dump(mdb->pg_buf, 0, 39);
576 		buffer_dump(mdb->pg_buf, fmt->pg_size - 160, fmt->pg_size-1);
577 	}
578 	mdb_debug(MDB_DEBUG_WRITE, "writing page %d", pgnum);
579 	if (!mdb_write_pg(mdb, pgnum)) {
580 		fprintf(stderr, "write failed! exiting...\n");
581 		exit(1);
582 	}
583 
584 	mdb_update_indexes(table, num_fields, fields, pgnum, rownum);
585 
586 	return 1;
587 }
588 /*
589  * Assumes caller has verfied space is available on page and adds the new
590  * row to the current pg_buf.
591  */
592 guint16
mdb_add_row_to_pg(MdbTableDef * table,unsigned char * row_buffer,int new_row_size)593 mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size)
594 {
595 	unsigned char *new_pg;
596 	int num_rows, i, pos, row_start, row_size;
597 	MdbCatalogEntry *entry = table->entry;
598 	MdbHandle *mdb = entry->mdb;
599 	MdbFormatConstants *fmt = mdb->fmt;
600 
601 	if (table->is_temp_table) {
602 		GPtrArray *pages = table->temp_table_pages;
603 		if (pages->len == 0) {
604 			new_pg = mdb_new_data_pg(entry);
605 			g_ptr_array_add(pages, new_pg);
606 		} else {
607 			new_pg = g_ptr_array_index(pages, pages->len - 1);
608 			if (mdb_get_int16(new_pg, 2) < new_row_size + 2) {
609 				new_pg = mdb_new_data_pg(entry);
610 				g_ptr_array_add(pages, new_pg);
611 			}
612 		}
613 
614 		num_rows = mdb_get_int16(new_pg, fmt->row_count_offset);
615 		pos = (num_rows == 0) ? fmt->pg_size :
616 			mdb_get_int16(new_pg, fmt->row_count_offset + (num_rows*2));
617 	} else {  /* is not a temp table */
618 		new_pg = mdb_new_data_pg(entry);
619 
620 		num_rows = mdb_get_int16(mdb->pg_buf, fmt->row_count_offset);
621 		pos = fmt->pg_size;
622 
623 		/* copy existing rows */
624 		for (i=0;i<num_rows;i++) {
625 			mdb_find_row(mdb, i, &row_start, &row_size);
626 			pos -= row_size;
627 			memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size);
628 			_mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos);
629 		}
630 	}
631 
632 	/* add our new row */
633 	pos -= new_row_size;
634 	memcpy(&new_pg[pos], row_buffer, new_row_size);
635 	/* add row to the row offset table */
636 	_mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (num_rows*2), pos);
637 
638 	/* update number rows on this page */
639 	num_rows++;
640 	_mdb_put_int16(new_pg, fmt->row_count_offset, num_rows);
641 
642 	/* update the freespace */
643 	_mdb_put_int16(new_pg,2,pos - fmt->row_count_offset - 2 - (num_rows*2));
644 
645 	/* copy new page over old */
646 	if (!table->is_temp_table) {
647 		memcpy(mdb->pg_buf, new_pg, fmt->pg_size);
648 		g_free(new_pg);
649 	}
650 
651 	return num_rows;
652 }
653 int
mdb_update_row(MdbTableDef * table)654 mdb_update_row(MdbTableDef *table)
655 {
656 int row_start, row_end;
657 unsigned int i;
658 MdbColumn *col;
659 MdbCatalogEntry *entry = table->entry;
660 MdbHandle *mdb = entry->mdb;
661 MdbField fields[256];
662 unsigned char row_buffer[4096];
663 int old_row_size, new_row_size, delta;
664 unsigned int num_fields;
665 
666 	if (!mdb->f->writable) {
667 		fprintf(stderr, "File is not open for writing\n");
668 		return 0;
669 	}
670 	mdb_find_row(mdb, table->cur_row-1, &row_start, &old_row_size);
671 	row_end = row_start + old_row_size - 1;
672 
673 	row_start &= 0x0FFF; /* remove flags */
674 
675 	mdb_debug(MDB_DEBUG_WRITE,"page %lu row %d start %d end %d", (unsigned long) table->cur_phys_pg, table->cur_row-1, row_start, row_end);
676 	if (mdb_get_option(MDB_DEBUG_LIKE))
677 		buffer_dump(mdb->pg_buf, row_start, row_end);
678 
679 	for (i=0;i<table->num_cols;i++) {
680 		col = g_ptr_array_index(table->columns,i);
681 		if (col->bind_ptr && mdb_is_col_indexed(table,i)) {
682 			fprintf(stderr, "Attempting to update column that is part of an index\n");
683 			return 0;
684 		}
685 	}
686 	num_fields = mdb_crack_row(table, row_start, row_end, fields);
687 
688 	if (mdb_get_option(MDB_DEBUG_WRITE)) {
689 		for (i=0;i<num_fields;i++) {
690 			//printf("col %d %d start %d siz %d fixed 5d\n", i, fields[i].colnum, fields[i].start, fields[i].siz, fields[i].is_fixed);
691 		}
692 	}
693 	for (i=0;i<table->num_cols;i++) {
694 		col = g_ptr_array_index(table->columns,i);
695 		if (col->bind_ptr) {
696 			fields[i].value = col->bind_ptr;
697 			fields[i].siz = *(col->len_ptr);
698 		}
699 	}
700 
701 	new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields);
702 	if (mdb_get_option(MDB_DEBUG_WRITE))
703 		buffer_dump(row_buffer, 0, new_row_size-1);
704 	delta = new_row_size - old_row_size;
705 	if ((mdb_pg_get_freespace(mdb) - delta) < 0) {
706 		fprintf(stderr, "No space left on this page, update will not occur\n");
707 		return 0;
708 	}
709 	/* do it! */
710 	mdb_replace_row(table, table->cur_row-1, row_buffer, new_row_size);
711 	return 0;
712 }
713 int
mdb_replace_row(MdbTableDef * table,int row,unsigned char * new_row,int new_row_size)714 mdb_replace_row(MdbTableDef *table, int row, unsigned char *new_row, int new_row_size)
715 {
716 MdbCatalogEntry *entry = table->entry;
717 MdbHandle *mdb = entry->mdb;
718 int pg_size = mdb->fmt->pg_size;
719 int rco = mdb->fmt->row_count_offset;
720 unsigned char *new_pg;
721 guint16 num_rows;
722 int row_start, row_size;
723 int i, pos;
724 
725 	if (mdb_get_option(MDB_DEBUG_WRITE)) {
726 		buffer_dump(mdb->pg_buf, 0, 39);
727 		buffer_dump(mdb->pg_buf, pg_size - 160, pg_size-1);
728 	}
729 	mdb_debug(MDB_DEBUG_WRITE,"updating row %d on page %lu", row, (unsigned long) table->cur_phys_pg);
730 	new_pg = mdb_new_data_pg(entry);
731 
732 	num_rows = mdb_get_int16(mdb->pg_buf, rco);
733 	_mdb_put_int16(new_pg, rco, num_rows);
734 
735 	pos = pg_size;
736 
737 	/* rows before */
738 	for (i=0;i<row;i++) {
739 		mdb_find_row(mdb, i, &row_start, &row_size);
740 		pos -= row_size;
741 		memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size);
742 		_mdb_put_int16(new_pg, rco + 2 + i*2, pos);
743 	}
744 
745 	/* our row */
746 	pos -= new_row_size;
747 	memcpy(&new_pg[pos], new_row, new_row_size);
748 	_mdb_put_int16(new_pg, rco + 2 + row*2, pos);
749 
750 	/* rows after */
751 	for (i=row+1;i<num_rows;i++) {
752 		mdb_find_row(mdb, i, &row_start, &row_size);
753 		pos -= row_size;
754 		memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size);
755 		_mdb_put_int16(new_pg, rco + 2 + i*2, pos);
756 	}
757 
758 	/* almost done, copy page over current */
759 	memcpy(mdb->pg_buf, new_pg, pg_size);
760 
761 	g_free(new_pg);
762 
763 	_mdb_put_int16(mdb->pg_buf, 2, mdb_pg_get_freespace(mdb));
764 	if (mdb_get_option(MDB_DEBUG_WRITE)) {
765 		buffer_dump(mdb->pg_buf, 0, 39);
766 		buffer_dump(mdb->pg_buf, pg_size - 160, pg_size-1);
767 	}
768 	/* drum roll, please */
769 	if (!mdb_write_pg(mdb, table->cur_phys_pg)) {
770 		fprintf(stderr, "write failed! exiting...\n");
771 		exit(1);
772 	}
773 	return 0;
774 }
775 static int
mdb_add_row_to_leaf_pg(MdbTableDef * table,MdbIndex * idx,MdbIndexPage * ipg,MdbField * idx_fields,guint32 pgnum,guint16 rownum)776 mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum)
777 /*,  guint32 pgnum, guint16 rownum)
778 static int
779 mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg)
780 */
781 {
782 	MdbCatalogEntry *entry = table->entry;
783 	MdbHandle *mdb = entry->mdb;
784 	MdbColumn *col;
785 	guint32 pg;
786 	guint16 row;
787 	unsigned char *new_pg;
788 	unsigned char key_hash[256];
789 	unsigned char iflag;
790 	int keycol;
791 
792 	new_pg = mdb_new_leaf_pg(entry);
793 
794 	/* reinitial ipg pointers to start of page */
795 	mdb_index_page_reset(ipg);
796 	mdb_read_pg(mdb, ipg->pg);
797 
798 	/* do we support this index type yet? */
799 	if (idx->num_keys > 1) {
800 		fprintf(stderr,"multikey indexes not yet supported, aborting\n");
801 		return 0;
802 	}
803 	keycol = idx->key_col_num[0];
804 	col = g_ptr_array_index (table->columns, keycol - 1);
805 	if (!mdb_is_fixed_col(col)) {
806 		fprintf(stderr,"variable length key columns not yet supported, aborting\n");
807 		return 0;
808 	}
809 
810 	while (mdb_index_find_next_on_page(mdb, ipg)) {
811 
812 		/* check for compressed indexes.  */
813 		if (ipg->len < col->col_size + 1) {
814 			fprintf(stderr,"compressed indexes not yet supported, aborting\n");
815 			return 0;
816 		}
817 
818 		pg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 4);
819 		row = mdb->pg_buf[ipg->offset + ipg->len - 1];
820 		iflag = mdb->pg_buf[ipg->offset];
821 
822 		/* turn the key hash back into a value */
823 		mdb_index_swap_n(&mdb->pg_buf[ipg->offset + 1], col->col_size, key_hash);
824 		key_hash[col->col_size - 1] &= 0x7f;
825 
826 		if (mdb_get_option(MDB_DEBUG_WRITE)) {
827 			buffer_dump(mdb->pg_buf, ipg->offset, ipg->offset + ipg->len - 1);
828 			buffer_dump(mdb->pg_buf, ipg->offset + 1, ipg->offset + col->col_size);
829 			buffer_dump(key_hash, 0, col->col_size - 1);
830 		}
831 
832 		memcpy(&new_pg[ipg->offset], &mdb->pg_buf[ipg->offset], ipg->len);
833 		ipg->offset += ipg->len;
834 		ipg->len = 0;
835 
836 		row++;
837 	}
838 	//_mdb_put_int16(new_pg, mdb->fmt->row_count_offset, row);
839 	/* free space left */
840 	_mdb_put_int16(new_pg, 2, mdb->fmt->pg_size - ipg->offset);
841 	//printf("offset = %d\n", ipg->offset);
842 
843 	mdb_index_swap_n(idx_fields[0].value, col->col_size, key_hash);
844 	key_hash[0] |= 0x080;
845 	if (mdb_get_option(MDB_DEBUG_WRITE)) {
846 		printf("key_hash\n");
847 		buffer_dump(idx_fields[0].value, 0, col->col_size-1);
848 		buffer_dump(key_hash, 0, col->col_size-1);
849 		printf("--------\n");
850 	}
851 	new_pg[ipg->offset] = 0x7f;
852 	memcpy(&new_pg[ipg->offset + 1], key_hash, col->col_size);
853 	_mdb_put_int24_msb(new_pg, ipg->offset + 5, pgnum);
854 	new_pg[ipg->offset + 8] = rownum-1;
855 	ipg->idx_starts[row++] = ipg->offset + ipg->len;
856 	//ipg->idx_starts[row] = ipg->offset + ipg->len;
857 	if (mdb_get_option(MDB_DEBUG_WRITE)) {
858 		buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size-1);
859 	}
860 	memcpy(mdb->pg_buf, new_pg, mdb->fmt->pg_size);
861 	mdb_index_pack_bitmap(mdb, ipg);
862 	if (mdb_get_option(MDB_DEBUG_WRITE)) {
863 		buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size-1);
864 	}
865 	g_free(new_pg);
866 
867 	return ipg->len;
868 }
869