summaryrefslogtreecommitdiffstats
path: root/keximdb/src/mdbtools/libmdb/sargs.c
blob: 44a75c4fd4297fda43363eb74d5f93a38a7c3815 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/* 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.
 */

/*
 * 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"

#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:
			
			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;
}
#if 0
#endif
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) {
		
		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);
		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 (!strcasecmp(col->name,colname)) {
			return mdb_add_sarg(col, in_sarg);
		}
	}
	/* else didn't find the column return 0! */
	return 0;
}