Click here to Skip to main content
15,896,111 members
Articles / Desktop Programming / MFC

MsAccess MdbTools with MFC and .NET

Rate me:
Please Sign up or sign in to vote.
4.82/5 (9 votes)
13 Jan 2012LGPL310 min read 69.3K   9.9K   49  
Viewer of MsAccess databases directly from MFC and .NET - Repair corrupt databases
/* 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

/*
 * code for handling searchable arguments (sargs) used primary by the sql 
 * engine to support where clause handling.  The sargs are configured in 
 * a tree with AND/OR operators connecting the child nodes. NOT operations
 * have only one child on the left side.  Logical operators (=,<,>,etc..)
 * have no children.
 *
 * datatype support is a bit weak at this point.  To add more types create
 * a mdb_test_[type]() function and invoke it from mdb_test_sarg()
 */
#include "../mdbtools.h"
#include <time.h>
#ifdef DMALLOC
#include "dmalloc.h"
#endif

void
mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data)
{
	if (func(node, data))
		return;
	if (node->left) mdb_sql_walk_tree(node->left, func, data);
	if (node->right) mdb_sql_walk_tree(node->right, func, data);
}
int 
mdb_test_string(MdbSargNode *node, char *s)
{
int rc;

	if (node->op == MDB_LIKE) {
		return mdb_like_cmp(s,node->value.s);
	}
	rc = strncmp(node->value.s, s, 255);
	switch (node->op) {
		case MDB_EQUAL:
			if (rc==0) return 1;
			break;
		case MDB_GT:
			if (rc<0) return 1;
			break;
		case MDB_LT:
			if (rc>0) return 1;
			break;
		case MDB_GTEQ:
			if (rc<=0) return 1;
			break;
		case MDB_LTEQ:
			if (rc>=0) return 1;
			break;
		default:
			fprintf(stderr, "Calling mdb_test_sarg on unknown operator.  Add code to mdb_test_string() for operator %d\n",node->op);
			break;
	}
	return 0;
}
int mdb_test_int(MdbSargNode *node, gint32 i)
{
	switch (node->op) {
		case MDB_EQUAL:
			//fprintf(stderr, "comparing %ld and %ld\n", i, node->value.i);
			if (node->value.i == i) return 1;
			break;
		case MDB_GT:
			if (node->value.i < i) return 1;
			break;
		case MDB_LT:
			if (node->value.i > i) return 1;
			break;
		case MDB_GTEQ:
			if (node->value.i <= i) return 1;
			break;
		case MDB_LTEQ:
			if (node->value.i >= i) return 1;
			break;
		default:
			fprintf(stderr, "Calling mdb_test_sarg on unknown operator.  Add code to mdb_test_int() for operator %d\n",node->op);
			break;
	}
	return 0;
}

int
mdb_test_date(MdbSargNode *node, double td)
{
	struct tm found;
	char date_tmp[MDB_BIND_SIZE];	//you should figure out a way to pull mdb_date_to_string in here

	time_t found_t;
	time_t asked_t;

	double diff;

	mdb_date_to_tm(td, &found);

	asked_t = node->value.i;
	found_t = mktime(&found);

	diff = difftime(asked_t, found_t);

	switch (node->op) {
	case MDB_EQUAL:
		if (diff==0) return 1;
		break;
	case MDB_GT:
		if (diff<0) return 1;
		break;
	case MDB_LT:
		if (diff>0) return 1;
		break;
	case MDB_GTEQ:
		if (diff<=0) return 1;
		break;
	case MDB_LTEQ:
		if (diff>=0) return 1;
		break;
	default:
		fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_date() for operator %d\n", node->op);
		break;
	}
	return 0;
}


int
mdb_find_indexable_sargs(MdbSargNode *node, gpointer data)
{
	MdbSarg sarg;

	if (node->op == MDB_OR || node->op == MDB_NOT) return 1;

	/* 
	 * right now all we do is look for sargs that are anded together from
	 * the root.  Later we may put together OR ops into a range, and then 
	 * range scan the leaf pages. That is col1 = 2 or col1 = 4 becomes
	 * col1 >= 2 and col1 <= 4 for the purpose of index scans, and then
	 * extra rows are thrown out when the row is tested against the main
	 * sarg tree.  range scans are generally only a bit better than table
	 * scanning anyway.
	 *
	 * also, later we should support the NOT operator, but it's generally
	 * a pretty worthless test for indexes, ie NOT col1 = 3, we are 
	 * probably better off table scanning.
	 */
	if (mdb_is_relational_op(node->op) && node->col) {
		//printf("op = %d value = %s\n", node->op, node->value.s);
		sarg.op = node->op;
		sarg.value = node->value;
		mdb_add_sarg(node->col, &sarg);
	}
	return 0;
}
int 
mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field)
{
	char tmpbuf[256];

	if (node->op == MDB_ISNULL) {
		if (field->is_null) return 0;
		else return 1;
	} else if (node->op == MDB_NOTNULL) {
		if (field->is_null) return 1;
		else return 0;
	}
	switch (col->col_type) {
		case MDB_BOOL:
			return mdb_test_int(node, !field->is_null);
			break;
		case MDB_BYTE:
			return mdb_test_int(node, (gint32)((char *)field->value)[0]);
			break;
		case MDB_INT:
			return mdb_test_int(node, (gint32)mdb_get_int16(field->value, 0));
			break;
		case MDB_LONGINT:
			return mdb_test_int(node, (gint32)mdb_get_int32(field->value, 0));
			break;
		case MDB_TEXT:
			mdb_unicode2ascii(mdb, field->value, field->siz, tmpbuf, 256);
			return mdb_test_string(node, tmpbuf);
		case MDB_DATETIME:
			return mdb_test_date(node, mdb_get_double(field->value, 0));
		default:
			fprintf(stderr, "Calling mdb_test_sarg on unknown type.  Add code to mdb_test_sarg() for type %d\n",col->col_type);
			break;
	}
	return 1;
}
int
mdb_find_field(int col_num, MdbField *fields, int num_fields)
{
	int i;

	for (i=0;i<num_fields;i++) {
		if (fields[i].colnum == col_num) return i;
	}
	return -1;
}
int
mdb_test_sarg_node(MdbHandle *mdb, MdbSargNode *node, MdbField *fields, int num_fields)
{
	int elem;
	MdbColumn *col;
	int rc;

	if (mdb_is_relational_op(node->op)) {
		col = node->col;
		/* for const = const expressions */
		if (!col) {
			return (node->value.i);
		}
		elem = mdb_find_field(col->col_num, fields, num_fields);
		if (!mdb_test_sarg(mdb, col, node, &fields[elem])) 
			return 0;
	} else { /* logical op */
		switch (node->op) {
		case MDB_NOT:
			rc = mdb_test_sarg_node(mdb, node->left, fields, num_fields);
			return !rc;
			break;
		case MDB_AND:
			if (!mdb_test_sarg_node(mdb, node->left, fields, num_fields))
				return 0;
			return mdb_test_sarg_node(mdb, node->right, fields, num_fields);
			break;
		case MDB_OR:
			if (mdb_test_sarg_node(mdb, node->left, fields, num_fields))
				return 1;
			return mdb_test_sarg_node(mdb, node->right, fields, num_fields);
			break;
		}
	}
	return 1;
}
int 
mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields)
{
	MdbSargNode *node;
	MdbCatalogEntry *entry = table->entry;
	MdbHandle *mdb = entry->mdb;

	node = table->sarg_tree;

	/* there may not be a sarg tree */
	if (!node) return 1;

	return mdb_test_sarg_node(mdb, node, fields, num_fields);
}
#if 0
int mdb_test_sargs(MdbHandle *mdb, MdbColumn *col, int offset, int len)
{
MdbSarg *sarg;
int i;

	for (i=0;i<col->num_sargs;i++) {
		sarg = g_ptr_array_index (col->sargs, i);
		if (!mdb_test_sarg(mdb, col, sarg, offset, len)) {
			/* sarg didn't match, no sense going on */
			return 0;	
		}
	}

	return 1;
}
#endif
int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg)
{
MdbSarg *sarg;
        if (!col->sargs) {
		col->sargs = g_ptr_array_new();
	}
	sarg = g_memdup(in_sarg,sizeof(MdbSarg));
        g_ptr_array_add(col->sargs, sarg);
	col->num_sargs++;

	return 1;
}
int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg)
{
	MdbColumn *col;
	unsigned int i;

	for (i=0;i<table->num_cols;i++) {
		col = g_ptr_array_index (table->columns, i);
		if (!stricmp(col->name,colname)) {
			return mdb_add_sarg(col, in_sarg);
		}
	}
	/* else didn't find the column return 0! */
	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer
Argentina Argentina
System developer from Argentina.

Programmed in VB 5,6,.NET, C#, Java, PL-SQL, Transac-SQL, C, C++ and even some "calculator" language.

Love to build small, useful applications.
Usually building big and complicated apps based on solid, reliable components.

Hobbies: reading, photography, chess, paddle, running.

Comments and Discussions