mdbtools/src/libmdb/write.c

847 lines
23 KiB
C
Raw Normal View History

/* MDB Tools - A library for reading MS Access database file
* Copyright (C) 2000 Brian Bruns
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "mdbtools.h"
#include "time.h"
#include "math.h"
#ifdef DMALLOC
#include "dmalloc.h"
#endif
2004-05-02 19:39:25 +08:00
//static int mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg);
static int mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum);
2003-01-02 06:29:39 +08:00
void
_mdb_put_int16(void *buf, guint32 offset, guint32 value)
2003-01-02 06:29:39 +08:00
{
2005-06-23 15:04:17 +08:00
value = GINT32_TO_LE(value);
memcpy(buf + offset, &value, 2);
2003-01-02 06:29:39 +08:00
}
void
_mdb_put_int32(void *buf, guint32 offset, guint32 value)
2003-01-02 06:29:39 +08:00
{
2005-06-23 15:04:17 +08:00
value = GINT32_TO_LE(value);
memcpy(buf + offset, &value, 4);
2003-01-02 06:29:39 +08:00
}
void
_mdb_put_int32_msb(void *buf, guint32 offset, guint32 value)
{
2005-06-23 15:04:17 +08:00
value = GINT32_TO_BE(value);
memcpy(buf + offset, &value, 4);
}
ssize_t
2003-01-02 06:29:39 +08:00
mdb_write_pg(MdbHandle *mdb, unsigned long pg)
{
ssize_t len;
struct stat status;
off_t offset = pg * mdb->fmt->pg_size;
2003-01-02 06:29:39 +08:00
fstat(mdb->f->fd, &status);
/* is page beyond current size + 1 ? */
if (status.st_size < offset + mdb->fmt->pg_size) {
fprintf(stderr,"offset %lu is beyond EOF\n",offset);
return 0;
}
lseek(mdb->f->fd, offset, SEEK_SET);
len = write(mdb->f->fd,mdb->pg_buf,mdb->fmt->pg_size);
if (len==-1) {
perror("write");
return 0;
} else if (len<mdb->fmt->pg_size) {
/* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->pg_size); */
return 0;
}
mdb->cur_pos = 0;
return len;
}
static int
mdb_is_col_indexed(MdbTableDef *table, int colnum)
{
2004-07-09 20:47:04 +08:00
unsigned int i, j;
MdbIndex *idx;
for (i=0;i<table->num_idxs;i++) {
idx = g_ptr_array_index (table->indices, i);
for (j=0;j<idx->num_keys;j++) {
if (idx->key_col_num[j]==colnum) return 1;
}
}
return 0;
}
2005-02-26 13:20:11 +08:00
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)
{
2004-07-09 20:47:04 +08:00
unsigned int i;
2005-02-26 13:20:11 +08:00
for (i=0; i<row_var_cols+1; i++) {
var_col_offsets[i] = mdb_get_int16(mdb->pg_buf,
2005-02-26 13:20:11 +08:00
row_end - bitmask_sz - 3 - (i*2));
}
2005-02-26 13:20:11 +08:00
}
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)
{
unsigned int i;
unsigned int num_jumps = 0, jumps_used = 0;
unsigned int col_ptr, row_len;
2005-02-26 13:20:11 +08:00
row_len = row_end - row_start + 1;
num_jumps = (row_len - 1) / 256;
col_ptr = row_end - bitmask_sz - num_jumps - 1;
/* If last jump is a dummy value, ignore it */
if ((col_ptr-row_start-row_var_cols)/256 < num_jumps)
num_jumps--;
2005-02-26 13:20:11 +08:00
jumps_used = 0;
for (i=0; i<row_var_cols+1; i++) {
while ((jumps_used < num_jumps)
2005-02-26 13:20:11 +08:00
&& (i == mdb->pg_buf[row_end-bitmask_sz-jumps_used-1])) {
jumps_used++;
}
2005-02-26 13:20:11 +08:00
var_col_offsets[i] = mdb->pg_buf[col_ptr-i]+(jumps_used*256);
}
2004-02-06 10:34:20 +08:00
}
2005-02-26 13:20:11 +08:00
/**
* mdb_crack_row:
* @table: Table that the row belongs to
* @row_start: offset to start of row on current page
* @row_end: offset to end of row on current page
* @fields: pointer to MdbField array to be popluated by mdb_crack_row
*
* Cracks a row buffer apart into its component fields.
*
* A row buffer is that portion of a data page which contains the values for
* that row. Its beginning and end can be found in the row offset table.
*
* The resulting MdbField array contains pointers into the row for each field
* present. Be aware that by modifying field[]->value, you would be modifying
* the row buffer itself, not a copy.
*
* This routine is mostly used internally by mdb_fetch_row() but may have some
* applicability for advanced application programs.
*
* Return value: number of fields present.
*/
int
mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields)
2004-02-06 10:34:20 +08:00
{
2005-02-26 13:20:11 +08:00
MdbColumn *col;
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
void *pg_buf = mdb->pg_buf;
2005-02-26 13:20:11 +08:00
unsigned int row_var_cols=0, row_cols;
unsigned char *nullmask;
2004-08-27 11:45:53 +08:00
unsigned int bitmask_sz;
unsigned int *var_col_offsets = NULL;
2005-02-26 13:20:11 +08:00
unsigned int fixed_cols_found, row_fixed_cols;
unsigned int col_count_size;
unsigned int i;
if (mdb_get_option(MDB_DEBUG_ROW)) {
buffer_dump(pg_buf, row_start, row_end - row_start + 1);
}
2004-02-06 10:34:20 +08:00
2005-02-26 13:20:11 +08:00
if (IS_JET4(mdb)) {
row_cols = mdb_get_int16(pg_buf, row_start);
2005-02-26 13:20:11 +08:00
col_count_size = 2;
} else {
row_cols = mdb_get_byte(pg_buf, row_start);
2005-02-26 13:20:11 +08:00
col_count_size = 1;
}
2004-08-27 11:45:53 +08:00
bitmask_sz = (row_cols + 7) / 8;
nullmask = pg_buf + row_end - bitmask_sz + 1;
2004-08-27 11:45:53 +08:00
/* read table of variable column locations */
if (table->num_var_cols > 0) {
row_var_cols = IS_JET4(mdb) ?
mdb_get_int16(pg_buf, row_end - bitmask_sz - 1) :
mdb_get_byte(pg_buf, row_end - bitmask_sz);
var_col_offsets = (unsigned int *)g_malloc((row_var_cols+1)*sizeof(int));
2005-02-26 13:20:11 +08:00
if (IS_JET4(mdb)) {
mdb_crack_row4(mdb, row_start, row_end, bitmask_sz,
row_var_cols, var_col_offsets);
} else {
mdb_crack_row3(mdb, row_start, row_end, bitmask_sz,
row_var_cols, var_col_offsets);
2004-02-06 10:34:20 +08:00
}
}
2005-02-26 13:20:11 +08:00
2004-02-06 10:34:20 +08:00
fixed_cols_found = 0;
2004-08-27 11:45:53 +08:00
row_fixed_cols = row_cols - row_var_cols;
2004-04-16 23:45:12 +08:00
if (mdb_get_option(MDB_DEBUG_ROW)) {
2005-02-26 13:20:11 +08:00
fprintf(stdout,"bitmask_sz %d\n", bitmask_sz);
2004-08-27 11:45:53 +08:00
fprintf(stdout,"row_var_cols %d\n", row_var_cols);
fprintf(stdout,"row_fixed_cols %d\n", row_fixed_cols);
2004-04-16 23:45:12 +08:00
}
2004-08-27 11:45:53 +08:00
2004-04-16 23:45:12 +08:00
for (i=0;i<table->num_cols;i++) {
2005-02-26 13:20:11 +08:00
unsigned int byte_num, bit_num;
unsigned int col_start;
col = g_ptr_array_index(table->columns,i);
2004-08-27 11:45:53 +08:00
fields[i].colnum = i;
2005-06-23 13:42:02 +08:00
fields[i].is_fixed = col->is_fixed;
2004-08-27 11:45:53 +08:00
byte_num = col->col_num / 8;
bit_num = col->col_num % 8;
/* logic on nulls is reverse, 1 is not null, 0 is null */
fields[i].is_null = nullmask[byte_num] & (1 << bit_num) ? 0 : 1;
if ((fields[i].is_fixed)
&& (fixed_cols_found < row_fixed_cols)) {
2005-02-26 13:20:11 +08:00
col_start = col->fixed_offset + col_count_size;
2004-08-27 11:45:53 +08:00
fields[i].start = row_start + col_start;
fields[i].value = pg_buf + row_start + col_start;
2004-08-27 11:45:53 +08:00
fields[i].siz = col->col_size;
2004-04-16 23:45:12 +08:00
fixed_cols_found++;
2005-02-26 13:20:11 +08:00
/* Use col->var_col_num because a deleted column is still
* present in the variable column offsets table for the row */
2004-08-27 11:45:53 +08:00
} else if ((!fields[i].is_fixed)
2005-02-26 13:20:11 +08:00
&& (col->var_col_num < row_var_cols)) {
col_start = var_col_offsets[col->var_col_num];
2004-08-27 11:45:53 +08:00
fields[i].start = row_start + col_start;
fields[i].value = pg_buf + row_start + col_start;
2005-02-26 13:20:11 +08:00
fields[i].siz = var_col_offsets[(col->var_col_num)+1] -
col_start;
2004-08-27 11:45:53 +08:00
} else {
fields[i].start = 0;
fields[i].value = NULL;
fields[i].siz = 0;
fields[i].is_null = 1;
2004-04-16 23:45:12 +08:00
}
}
2005-02-26 13:20:11 +08:00
g_free(var_col_offsets);
2004-08-27 11:45:53 +08:00
return row_cols;
}
static int
mdb_pack_null_mask(unsigned char *buffer, int num_fields, MdbField *fields)
{
int pos = 0, bit = 0, byte = 0;
int i;
/* 'Not null' bitmap */
for (i=0; i<num_fields; i++) {
/* column is null if bit is clear (0) */
if (!fields[i].is_null) {
byte |= 1 << bit;
//printf("%d %d %d %d\n", i, bit, 1 << bit, byte);
}
bit++;
if (bit==8) {
buffer[pos++] = byte;
bit = byte = 0;
}
}
/* if we've written any bits to the current byte, flush it */
if (bit)
buffer[pos++] = byte;
return pos;
}
2003-01-02 06:29:39 +08:00
/* fields must be ordered with fixed columns first, then vars, subsorted by
* column number */
static int
2004-07-09 20:47:04 +08:00
mdb_pack_row4(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields)
{
unsigned int pos = 0;
unsigned int var_cols = 0;
unsigned int i;
2003-01-02 06:29:39 +08:00
row_buffer[pos++] = num_fields & 0xff;
row_buffer[pos++] = (num_fields >> 8) & 0xff;
/* Fixed length columns */
2003-01-02 06:29:39 +08:00
for (i=0;i<num_fields;i++) {
if (fields[i].is_fixed) {
fields[i].offset = pos;
2004-09-13 03:04:59 +08:00
if (!fields[i].is_null) {
memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
}
2003-01-02 06:29:39 +08:00
pos += fields[i].siz;
}
}
/* For tables without variable-length columns */
if (table->num_var_cols == 0) {
pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
return pos;
}
/* Variable length columns */
2003-01-02 06:29:39 +08:00
for (i=0;i<num_fields;i++) {
if (!fields[i].is_fixed) {
var_cols++;
fields[i].offset = pos;
2004-03-13 23:07:18 +08:00
if (! fields[i].is_null) {
memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
pos += fields[i].siz;
}
2003-01-02 06:29:39 +08:00
}
}
/* EOD */
row_buffer[pos] = pos & 0xff;
row_buffer[pos+1] = (pos >> 8) & 0xff;
pos += 2;
/* Offsets of the variable-length columns */
for (i=num_fields; i>0; i--) {
if (!fields[i-1].is_fixed) {
row_buffer[pos++] = fields[i-1].offset & 0xff;
row_buffer[pos++] = (fields[i-1].offset >> 8) & 0xff;
}
}
/* Number of variable-length columns */
row_buffer[pos++] = var_cols & 0xff;
row_buffer[pos++] = (var_cols >> 8) & 0xff;
pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
return pos;
}
static int
2004-07-09 20:47:04 +08:00
mdb_pack_row3(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields)
{
unsigned int pos = 0;
unsigned int var_cols = 0;
unsigned int i, j;
unsigned char *offset_high;
row_buffer[pos++] = num_fields;
2003-01-02 06:29:39 +08:00
/* Fixed length columns */
for (i=0;i<num_fields;i++) {
if (fields[i].is_fixed) {
fields[i].offset = pos;
2004-09-13 03:04:59 +08:00
if (!fields[i].is_null) {
memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
}
pos += fields[i].siz;
}
}
/* For tables without variable-length columns */
if (table->num_var_cols == 0) {
pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
return pos;
}
/* Variable length columns */
for (i=0;i<num_fields;i++) {
if (!fields[i].is_fixed) {
var_cols++;
fields[i].offset = pos;
2004-03-13 23:07:18 +08:00
if (! fields[i].is_null) {
memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
pos += fields[i].siz;
}
}
}
offset_high = (unsigned char *) g_malloc(var_cols+1);
offset_high[0] = (pos >> 8) & 0xff;
j = 1;
2003-01-02 06:29:39 +08:00
/* EOD */
row_buffer[pos] = pos & 0xff;
pos++;
2003-01-02 06:29:39 +08:00
/* Variable length column offsets */
for (i=num_fields; i>0; i--) {
if (!fields[i-1].is_fixed) {
row_buffer[pos++] = fields[i-1].offset & 0xff;
offset_high[j++] = (fields[i-1].offset >> 8) & 0xff;
}
2003-01-02 06:29:39 +08:00
}
/* Dummy jump table entry */
if (offset_high[0] < (pos+(num_fields+7)/8-1)/255) {
row_buffer[pos++] = 0xff;
}
/* Jump table */
for (i=0; i<var_cols; i++) {
if (offset_high[i] > offset_high[i+1]) {
row_buffer[pos++] = var_cols-i;
}
}
g_free(offset_high);
2003-01-02 06:29:39 +08:00
row_buffer[pos++] = var_cols;
pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
2003-01-02 06:29:39 +08:00
return pos;
}
int
2004-07-09 20:47:04 +08:00
mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, int unsigned num_fields, MdbField *fields)
{
2004-09-13 03:04:59 +08:00
if (table->is_temp_table) {
unsigned int i;
for (i=0; i<num_fields; i++) {
MdbColumn *c = g_ptr_array_index(table->columns, i);
fields[i].is_null = (fields[i].value) ? 0 : 1;
fields[i].colnum = i;
fields[i].is_fixed = c->is_fixed;
if ((c->col_type != MDB_TEXT)
&& (c->col_type != MDB_MEMO)) {
fields[i].siz = c->col_size;
}
}
}
if (IS_JET4(table->entry->mdb)) {
return mdb_pack_row4(table, row_buffer, num_fields, fields);
} else {
return mdb_pack_row3(table, row_buffer, num_fields, fields);
}
}
int
mdb_pg_get_freespace(MdbHandle *mdb)
{
2004-09-09 11:44:35 +08:00
int rows, free_start, free_end;
int row_count_offset = mdb->fmt->row_count_offset;
rows = mdb_get_int16(mdb->pg_buf, row_count_offset);
2004-09-09 11:44:35 +08:00
free_start = row_count_offset + 2 + (rows * 2);
free_end = mdb_get_int16(mdb->pg_buf, row_count_offset + (rows * 2));
2004-02-16 10:00:45 +08:00
mdb_debug(MDB_DEBUG_WRITE,"free space left on page = %d", free_end - free_start);
2004-09-09 11:44:35 +08:00
return (free_end - free_start);
}
void *
2004-02-06 10:34:20 +08:00
mdb_new_leaf_pg(MdbCatalogEntry *entry)
{
MdbHandle *mdb = entry->mdb;
void *new_pg = g_malloc0(mdb->fmt->pg_size);
2004-02-06 10:34:20 +08:00
_mdb_put_int16(new_pg, 2, 0x0104);
2004-02-06 10:34:20 +08:00
_mdb_put_int32(new_pg, 4, entry->table_pg);
return new_pg;
}
void *
2003-02-10 07:19:21 +08:00
mdb_new_data_pg(MdbCatalogEntry *entry)
{
2004-09-09 11:44:35 +08:00
MdbFormatConstants *fmt = entry->mdb->fmt;
void *new_pg = g_malloc0(fmt->pg_size);
2003-02-10 07:19:21 +08:00
_mdb_put_int16(new_pg, 2, 0x0101);
2004-09-09 11:44:35 +08:00
_mdb_put_int16(new_pg, 2, fmt->pg_size - fmt->row_count_offset - 2);
2003-02-10 07:19:21 +08:00
_mdb_put_int32(new_pg, 4, entry->table_pg);
return new_pg;
}
int
2004-02-06 10:34:20 +08:00
mdb_update_indexes(MdbTableDef *table, int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum)
2003-02-10 07:19:21 +08:00
{
2004-07-09 20:47:04 +08:00
unsigned int i;
2003-02-10 07:19:21 +08:00
MdbIndex *idx;
for (i=0;i<table->num_idxs;i++) {
idx = g_ptr_array_index (table->indices, i);
2004-02-16 10:00:45 +08:00
mdb_debug(MDB_DEBUG_WRITE,"Updating %s (%d).", idx->name, idx->index_type);
if (idx->index_type==1) {
2004-02-06 10:34:20 +08:00
mdb_update_index(table, idx, num_fields, fields, pgnum, rownum);
}
}
return 1;
}
int
mdb_init_index_chain(MdbTableDef *table, MdbIndex *idx)
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
table->scan_idx = idx;
table->chain = g_malloc0(sizeof(MdbIndexChain));
table->mdbidx = mdb_clone_handle(mdb);
mdb_read_pg(table->mdbidx, table->scan_idx->first_pg);
return 1;
}
2004-02-06 10:34:20 +08:00
int
2004-07-17 22:21:43 +08:00
mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum)
{
2004-02-06 10:34:20 +08:00
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
int idx_xref[16];
2004-07-17 22:21:43 +08:00
unsigned int i, j;
2004-02-06 10:34:20 +08:00
MdbIndexChain *chain;
2004-05-02 19:39:25 +08:00
MdbField idx_fields[10];
for (i = 0; i < idx->num_keys; i++) {
for (j = 0; j < num_fields; j++) {
// key_col_num is 1 based, can't remember why though
2004-05-02 19:39:25 +08:00
if (fields[j].colnum == idx->key_col_num[i]-1) {
idx_xref[i] = j;
2004-05-02 19:39:25 +08:00
idx_fields[i] = fields[j];
}
}
}
2005-02-12 00:24:01 +08:00
/*
for (i = 0; i < idx->num_keys; i++) {
fprintf(stdout, "key col %d (%d) is mapped to field %d (%d %d)\n",
i, idx->key_col_num[i], idx_xref[i], fields[idx_xref[i]].colnum,
fields[idx_xref[i]].siz);
}
for (i = 0; i < num_fields; i++) {
fprintf(stdout, "%d (%d %d)\n",
i, fields[i].colnum,
fields[i].siz);
2003-02-10 07:19:21 +08:00
}
2005-02-12 00:24:01 +08:00
*/
2004-02-06 10:34:20 +08:00
chain = g_malloc0(sizeof(MdbIndexChain));
mdb_index_find_row(mdb, idx, chain, pgnum, rownum);
2005-02-12 00:24:01 +08:00
//printf("chain depth = %d\n", chain->cur_depth);
//printf("pg = %" G_GUINT32_FORMAT "\n",
//chain->pages[chain->cur_depth-1].pg);
2004-05-02 19:39:25 +08:00
//mdb_copy_index_pg(table, idx, &chain->pages[chain->cur_depth-1]);
mdb_add_row_to_leaf_pg(table, idx, &chain->pages[chain->cur_depth-1], idx_fields, pgnum, rownum);
return 1;
2003-02-10 07:19:21 +08:00
}
int
mdb_insert_row(MdbTableDef *table, int num_fields, MdbField *fields)
{
int new_row_size;
2003-02-10 07:19:21 +08:00
unsigned char row_buffer[4096];
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
MdbFormatConstants *fmt = mdb->fmt;
guint32 pgnum;
2004-02-06 10:34:20 +08:00
guint16 rownum;
2003-02-10 07:19:21 +08:00
if (!mdb->f->writable) {
fprintf(stderr, "File is not open for writing\n");
return 0;
}
new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields);
2004-02-16 10:00:45 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(row_buffer, 0, new_row_size);
2004-02-16 10:00:45 +08:00
}
2003-02-10 07:19:21 +08:00
pgnum = mdb_map_find_next_freepage(table, new_row_size);
if (!pgnum) {
fprintf(stderr, "Unable to allocate new page.\n");
return 0;
}
2004-02-06 10:34:20 +08:00
rownum = mdb_add_row_to_pg(table, row_buffer, new_row_size);
2004-02-16 10:00:45 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, 40);
buffer_dump(mdb->pg_buf, fmt->pg_size - 160, 160);
2004-02-16 10:00:45 +08:00
}
mdb_debug(MDB_DEBUG_WRITE, "writing page %d", pgnum);
if (!mdb_write_pg(mdb, pgnum)) {
fprintf(stderr, "write failed! exiting...\n");
exit(1);
}
2004-02-06 10:34:20 +08:00
mdb_update_indexes(table, num_fields, fields, pgnum, rownum);
return 1;
}
/*
* Assumes caller has verfied space is available on page and adds the new
* row to the current pg_buf.
*/
2004-02-06 10:34:20 +08:00
guint16
mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size)
{
void *new_pg;
int num_rows, i, pos, row_start;
size_t row_size;
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
MdbFormatConstants *fmt = mdb->fmt;
2003-02-10 07:19:21 +08:00
2004-09-09 11:44:35 +08:00
if (table->is_temp_table) {
GPtrArray *pages = table->temp_table_pages;
if (pages->len == 0) {
new_pg = mdb_new_data_pg(entry);
g_ptr_array_add(pages, new_pg);
} else {
new_pg = g_ptr_array_index(pages, pages->len - 1);
if (mdb_get_int16(new_pg, 2) < new_row_size + 2) {
new_pg = mdb_new_data_pg(entry);
g_ptr_array_add(pages, new_pg);
}
}
2003-02-10 07:19:21 +08:00
2004-09-09 11:44:35 +08:00
num_rows = mdb_get_int16(new_pg, fmt->row_count_offset);
pos = (num_rows == 0) ? fmt->pg_size :
mdb_get_int16(new_pg, fmt->row_count_offset + (num_rows*2));
} else { /* is not a temp table */
new_pg = mdb_new_data_pg(entry);
num_rows = mdb_get_int16(mdb->pg_buf, fmt->row_count_offset);
2004-09-09 11:44:35 +08:00
pos = fmt->pg_size;
/* copy existing rows */
for (i=0;i<num_rows;i++) {
2005-03-07 12:28:12 +08:00
mdb_find_row(mdb, i, &row_start, &row_size);
2004-09-09 11:44:35 +08:00
pos -= row_size;
memcpy(new_pg + pos, mdb->pg_buf + row_start, row_size);
2004-09-09 11:44:35 +08:00
_mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos);
}
2003-02-10 07:19:21 +08:00
}
/* add our new row */
pos -= new_row_size;
memcpy(new_pg + pos, row_buffer, new_row_size);
/* add row to the row offset table */
2003-02-10 07:19:21 +08:00
_mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (num_rows*2), pos);
/* update number rows on this page */
2003-02-10 07:19:21 +08:00
num_rows++;
_mdb_put_int16(new_pg, fmt->row_count_offset, num_rows);
/* update the freespace */
2004-09-09 11:44:35 +08:00
_mdb_put_int16(new_pg,2,pos - fmt->row_count_offset - 2 - (num_rows*2));
/* copy new page over old */
if (!table->is_temp_table) {
memcpy(mdb->pg_buf, new_pg, fmt->pg_size);
g_free(new_pg);
}
2004-02-06 10:34:20 +08:00
return num_rows;
2003-02-10 07:19:21 +08:00
}
int
mdb_update_row(MdbTableDef *table)
{
int row_start, row_end;
2004-07-09 20:47:04 +08:00
unsigned int i;
MdbColumn *col;
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
MdbField fields[256];
unsigned char row_buffer[4096];
size_t old_row_size, new_row_size;
2004-07-09 20:47:04 +08:00
unsigned int num_fields;
2003-01-02 06:29:39 +08:00
if (!mdb->f->writable) {
fprintf(stderr, "File is not open for writing\n");
return 0;
}
2005-03-07 12:28:12 +08:00
mdb_find_row(mdb, table->cur_row-1, &row_start, &old_row_size);
row_end = row_start + old_row_size - 1;
row_start &= 0x0FFF; /* remove flags */
2004-02-16 10:00:45 +08:00
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);
if (mdb_get_option(MDB_DEBUG_LIKE))
buffer_dump(mdb->pg_buf, row_start, old_row_size);
2003-01-02 06:29:39 +08:00
for (i=0;i<table->num_cols;i++) {
col = g_ptr_array_index(table->columns,i);
if (col->bind_ptr && mdb_is_col_indexed(table,i)) {
fprintf(stderr, "Attempting to update column that is part of an index\n");
return 0;
}
}
num_fields = mdb_crack_row(table, row_start, row_end, fields);
2004-02-16 10:00:45 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE)) {
for (i=0;i<num_fields;i++) {
2005-02-12 00:24:01 +08:00
//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);
2004-02-16 10:00:45 +08:00
}
}
2003-01-02 06:29:39 +08:00
for (i=0;i<table->num_cols;i++) {
col = g_ptr_array_index(table->columns,i);
if (col->bind_ptr) {
fields[i].value = col->bind_ptr;
fields[i].siz = *(col->len_ptr);
}
}
new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields);
2004-02-16 10:00:45 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE))
buffer_dump(row_buffer, 0, new_row_size);
if (new_row_size > (old_row_size + mdb_pg_get_freespace(mdb))) {
fprintf(stderr, "No space left on this page, update will not occur\n");
return 0;
}
2003-01-02 06:29:39 +08:00
/* do it! */
mdb_replace_row(table, table->cur_row-1, row_buffer, new_row_size);
return 0;
2003-01-02 06:29:39 +08:00
}
int
mdb_replace_row(MdbTableDef *table, int row, void *new_row, int new_row_size)
2003-01-02 06:29:39 +08:00
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
2005-03-07 12:28:12 +08:00
int pg_size = mdb->fmt->pg_size;
int rco = mdb->fmt->row_count_offset;
void *new_pg;
2003-01-02 06:29:39 +08:00
guint16 num_rows;
int row_start;
size_t row_size;
2003-01-02 06:29:39 +08:00
int i, pos;
2004-02-16 10:00:45 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, 40);
buffer_dump(mdb->pg_buf, pg_size - 160, 160);
2004-02-16 10:00:45 +08:00
}
mdb_debug(MDB_DEBUG_WRITE,"updating row %d on page %lu", row, (unsigned long) table->cur_phys_pg);
2003-02-10 07:19:21 +08:00
new_pg = mdb_new_data_pg(entry);
2003-01-02 06:29:39 +08:00
num_rows = mdb_get_int16(mdb->pg_buf, rco);
2005-03-07 12:28:12 +08:00
_mdb_put_int16(new_pg, rco, num_rows);
2003-01-02 06:29:39 +08:00
2005-03-07 12:28:12 +08:00
pos = pg_size;
2003-01-02 06:29:39 +08:00
/* rows before */
for (i=0;i<row;i++) {
2005-03-07 12:28:12 +08:00
mdb_find_row(mdb, i, &row_start, &row_size);
2003-01-02 06:29:39 +08:00
pos -= row_size;
memcpy(new_pg + pos, mdb->pg_buf + row_start, row_size);
2005-03-07 12:28:12 +08:00
_mdb_put_int16(new_pg, rco + 2 + i*2, pos);
2003-01-02 06:29:39 +08:00
}
/* our row */
pos -= new_row_size;
memcpy(new_pg + pos, new_row, new_row_size);
2005-03-07 12:28:12 +08:00
_mdb_put_int16(new_pg, rco + 2 + row*2, pos);
2003-01-02 06:29:39 +08:00
/* rows after */
for (i=row+1;i<num_rows;i++) {
2005-03-07 12:28:12 +08:00
mdb_find_row(mdb, i, &row_start, &row_size);
2003-01-02 06:29:39 +08:00
pos -= row_size;
memcpy(new_pg + pos, mdb->pg_buf + row_start, row_size);
2005-03-07 12:28:12 +08:00
_mdb_put_int16(new_pg, rco + 2 + i*2, pos);
2003-01-02 06:29:39 +08:00
}
/* almost done, copy page over current */
2005-03-07 12:28:12 +08:00
memcpy(mdb->pg_buf, new_pg, pg_size);
2003-01-02 06:29:39 +08:00
2003-02-10 07:19:21 +08:00
g_free(new_pg);
2003-01-02 06:29:39 +08:00
_mdb_put_int16(mdb->pg_buf, 2, mdb_pg_get_freespace(mdb));
2004-02-16 10:00:45 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, 40);
buffer_dump(mdb->pg_buf, pg_size - 160, 160);
2004-02-16 10:00:45 +08:00
}
2003-01-02 06:29:39 +08:00
/* drum roll, please */
if (!mdb_write_pg(mdb, table->cur_phys_pg)) {
fprintf(stderr, "write failed! exiting...\n");
exit(1);
}
return 0;
}
2004-05-02 19:39:25 +08:00
static int
mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum)
2004-05-02 19:39:25 +08:00
/*, guint32 pgnum, guint16 rownum)
static int
2004-04-12 22:27:07 +08:00
mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg)
2004-05-02 19:39:25 +08:00
*/
2004-02-06 10:34:20 +08:00
{
MdbCatalogEntry *entry = table->entry;
MdbHandle *mdb = entry->mdb;
2004-04-12 22:27:07 +08:00
MdbColumn *col;
2005-06-23 15:04:17 +08:00
guint32 pg, pg_row;
2004-04-12 22:27:07 +08:00
guint16 row;
void *new_pg;
2004-04-12 22:27:07 +08:00
unsigned char key_hash[256];
unsigned char iflag;
int keycol;
2004-02-06 10:34:20 +08:00
new_pg = mdb_new_leaf_pg(entry);
2005-02-12 00:24:01 +08:00
/* reinitial ipg pointers to start of page */
2004-02-06 10:34:20 +08:00
mdb_index_page_reset(ipg);
mdb_read_pg(mdb, ipg->pg);
2004-04-12 22:27:07 +08:00
/* do we support this index type yet? */
if (idx->num_keys > 1) {
fprintf(stderr,"multikey indexes not yet supported, aborting\n");
return 0;
}
keycol = idx->key_col_num[0];
col = g_ptr_array_index (table->columns, keycol - 1);
2005-06-23 13:42:02 +08:00
if (!col->is_fixed) {
2004-04-12 22:27:07 +08:00
fprintf(stderr,"variable length key columns not yet supported, aborting\n");
return 0;
}
2004-02-06 10:34:20 +08:00
while (mdb_index_find_next_on_page(mdb, ipg)) {
2004-04-12 22:27:07 +08:00
2004-05-02 19:39:25 +08:00
/* check for compressed indexes. */
2004-04-12 22:27:07 +08:00
if (ipg->len < col->col_size + 1) {
fprintf(stderr,"compressed indexes not yet supported, aborting\n");
return 0;
}
2004-05-02 19:39:25 +08:00
2005-06-23 15:04:17 +08:00
pg_row = mdb_get_int32_msb(mdb->pg_buf, ipg->offset + ipg->len - 4);
pg = pg_row >> 8;
row = pg_row & 0xff;
2004-04-12 22:27:07 +08:00
iflag = mdb->pg_buf[ipg->offset];
/* turn the key hash back into a value */
2004-04-12 22:27:07 +08:00
mdb_index_swap_n(&mdb->pg_buf[ipg->offset + 1], col->col_size, key_hash);
key_hash[col->col_size - 1] &= 0x7f;
2005-02-12 00:24:01 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, ipg->offset, ipg->len);
buffer_dump(mdb->pg_buf, ipg->offset + 1, col->col_size);
buffer_dump(key_hash, 0, col->col_size);
2005-02-12 00:24:01 +08:00
}
memcpy(new_pg + ipg->offset, mdb->pg_buf + ipg->offset, ipg->len);
2004-02-06 10:34:20 +08:00
ipg->offset += ipg->len;
ipg->len = 0;
2004-04-12 22:27:07 +08:00
row++;
2004-02-06 10:34:20 +08:00
}
//_mdb_put_int16(new_pg, mdb->fmt->row_count_offset, row);
/* free space left */
_mdb_put_int16(new_pg, 2, mdb->fmt->pg_size - ipg->offset);
2005-02-12 00:24:01 +08:00
//printf("offset = %d\n", ipg->offset);
mdb_index_swap_n(idx_fields[0].value, col->col_size, key_hash);
2005-02-12 00:24:01 +08:00
key_hash[0] |= 0x080;
if (mdb_get_option(MDB_DEBUG_WRITE)) {
printf("key_hash\n");
buffer_dump(idx_fields[0].value, 0, col->col_size);
buffer_dump(key_hash, 0, col->col_size);
2005-02-12 00:24:01 +08:00
printf("--------\n");
}
((char *)new_pg)[ipg->offset] = 0x7f;
memcpy(new_pg + ipg->offset + 1, key_hash, col->col_size);
2005-06-23 15:04:17 +08:00
pg_row = (pgnum << 8) | ((rownum-1) & 0xff);
_mdb_put_int32_msb(new_pg, ipg->offset + 5, pg_row);
ipg->idx_starts[row++] = ipg->offset + ipg->len;
//ipg->idx_starts[row] = ipg->offset + ipg->len;
2005-02-12 00:24:01 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size);
2005-02-12 00:24:01 +08:00
}
memcpy(mdb->pg_buf, new_pg, mdb->fmt->pg_size);
mdb_index_pack_bitmap(mdb, ipg);
2005-02-12 00:24:01 +08:00
if (mdb_get_option(MDB_DEBUG_WRITE)) {
buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size);
2005-02-12 00:24:01 +08:00
}
2004-02-06 10:34:20 +08:00
g_free(new_pg);
return ipg->len;
}