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 /*
21 * code for handling searchable arguments (sargs) used primary by the sql
22 * engine to support where clause handling. The sargs are configured in
23 * a tree with AND/OR operators connecting the child nodes. NOT operations
24 * have only one child on the left side. Logical operators (=,<,>,etc..)
25 * have no children.
26 *
27 * datatype support is a bit weak at this point. To add more types create
28 * a mdb_test_[type]() function and invoke it from mdb_test_sarg()
29 */
30 #include "mdbtools.h"
31
32 #ifdef DMALLOC
33 #include "dmalloc.h"
34 #endif
35
36 void
mdb_sql_walk_tree(MdbSargNode * node,MdbSargTreeFunc func,gpointer data)37 mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data)
38 {
39 if (func(node, data))
40 return;
41 if (node->left) mdb_sql_walk_tree(node->left, func, data);
42 if (node->right) mdb_sql_walk_tree(node->right, func, data);
43 }
44 int
mdb_test_string(MdbSargNode * node,char * s)45 mdb_test_string(MdbSargNode *node, char *s)
46 {
47 int rc;
48
49 if (node->op == MDB_LIKE) {
50 return mdb_like_cmp(s,node->value.s);
51 }
52 rc = strncmp(node->value.s, s, 255);
53 switch (node->op) {
54 case MDB_EQUAL:
55 if (rc==0) return 1;
56 break;
57 case MDB_GT:
58 if (rc<0) return 1;
59 break;
60 case MDB_LT:
61 if (rc>0) return 1;
62 break;
63 case MDB_GTEQ:
64 if (rc<=0) return 1;
65 break;
66 case MDB_LTEQ:
67 if (rc>=0) return 1;
68 break;
69 default:
70 fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_string() for operator %d\n",node->op);
71 break;
72 }
73 return 0;
74 }
mdb_test_int(MdbSargNode * node,gint32 i)75 int mdb_test_int(MdbSargNode *node, gint32 i)
76 {
77 switch (node->op) {
78 case MDB_EQUAL:
79 //fprintf(stderr, "comparing %ld and %ld\n", i, node->value.i);
80 if (node->value.i == i) return 1;
81 break;
82 case MDB_GT:
83 if (node->value.i < i) return 1;
84 break;
85 case MDB_LT:
86 if (node->value.i > i) return 1;
87 break;
88 case MDB_GTEQ:
89 if (node->value.i <= i) return 1;
90 break;
91 case MDB_LTEQ:
92 if (node->value.i >= i) return 1;
93 break;
94 default:
95 fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_int() for operator %d\n",node->op);
96 break;
97 }
98 return 0;
99 }
100 #if 0
101 #endif
102 int
mdb_find_indexable_sargs(MdbSargNode * node,gpointer data)103 mdb_find_indexable_sargs(MdbSargNode *node, gpointer data)
104 {
105 MdbSarg sarg;
106
107 if (node->op == MDB_OR || node->op == MDB_NOT) return 1;
108
109 /*
110 * right now all we do is look for sargs that are anded together from
111 * the root. Later we may put together OR ops into a range, and then
112 * range scan the leaf pages. That is col1 = 2 or col1 = 4 becomes
113 * col1 >= 2 and col1 <= 4 for the purpose of index scans, and then
114 * extra rows are thrown out when the row is tested against the main
115 * sarg tree. range scans are generally only a bit better than table
116 * scanning anyway.
117 *
118 * also, later we should support the NOT operator, but it's generally
119 * a pretty worthless test for indexes, ie NOT col1 = 3, we are
120 * probably better off table scanning.
121 */
122 if (mdb_is_relational_op(node->op) && node->col) {
123 //printf("op = %d value = %s\n", node->op, node->value.s);
124 sarg.op = node->op;
125 sarg.value = node->value;
126 mdb_add_sarg(node->col, &sarg);
127 }
128 return 0;
129 }
130 int
mdb_test_sarg(MdbHandle * mdb,MdbColumn * col,MdbSargNode * node,MdbField * field)131 mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field)
132 {
133 char tmpbuf[256];
134
135 if (node->op == MDB_ISNULL) {
136 if (field->is_null) return 0;
137 else return 1;
138 } else if (node->op == MDB_NOTNULL) {
139 if (field->is_null) return 1;
140 else return 0;
141 }
142 switch (col->col_type) {
143 case MDB_BOOL:
144 return mdb_test_int(node, !field->is_null);
145 break;
146 case MDB_BYTE:
147 return mdb_test_int(node, (gint32)((char *)field->value)[0]);
148 break;
149 case MDB_INT:
150 return mdb_test_int(node, (gint32)mdb_get_int16(field->value, 0));
151 break;
152 case MDB_LONGINT:
153 return mdb_test_int(node, (gint32)mdb_get_int32(field->value, 0));
154 break;
155 case MDB_TEXT:
156 mdb_unicode2ascii(mdb, field->value, field->siz, tmpbuf, 256);
157 return mdb_test_string(node, tmpbuf);
158 default:
159 fprintf(stderr, "Calling mdb_test_sarg on unknown type. Add code to mdb_test_sarg() for type %d\n",col->col_type);
160 break;
161 }
162 return 1;
163 }
164 int
mdb_find_field(int col_num,MdbField * fields,int num_fields)165 mdb_find_field(int col_num, MdbField *fields, int num_fields)
166 {
167 int i;
168
169 for (i=0;i<num_fields;i++) {
170 if (fields[i].colnum == col_num) return i;
171 }
172 return -1;
173 }
174 int
mdb_test_sarg_node(MdbHandle * mdb,MdbSargNode * node,MdbField * fields,int num_fields)175 mdb_test_sarg_node(MdbHandle *mdb, MdbSargNode *node, MdbField *fields, int num_fields)
176 {
177 int elem;
178 MdbColumn *col;
179 int rc;
180
181 if (mdb_is_relational_op(node->op)) {
182 col = node->col;
183 /* for const = const expressions */
184 if (!col) {
185 return (node->value.i);
186 }
187 elem = mdb_find_field(col->col_num, fields, num_fields);
188 if (!mdb_test_sarg(mdb, col, node, &fields[elem]))
189 return 0;
190 } else { /* logical op */
191 switch (node->op) {
192 case MDB_NOT:
193 rc = mdb_test_sarg_node(mdb, node->left, fields, num_fields);
194 return !rc;
195 break;
196 case MDB_AND:
197 if (!mdb_test_sarg_node(mdb, node->left, fields, num_fields))
198 return 0;
199 return mdb_test_sarg_node(mdb, node->right, fields, num_fields);
200 break;
201 case MDB_OR:
202 if (mdb_test_sarg_node(mdb, node->left, fields, num_fields))
203 return 1;
204 return mdb_test_sarg_node(mdb, node->right, fields, num_fields);
205 break;
206 }
207 }
208 return 1;
209 }
210 int
mdb_test_sargs(MdbTableDef * table,MdbField * fields,int num_fields)211 mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields)
212 {
213 MdbSargNode *node;
214 MdbCatalogEntry *entry = table->entry;
215 MdbHandle *mdb = entry->mdb;
216
217 node = table->sarg_tree;
218
219 /* there may not be a sarg tree */
220 if (!node) return 1;
221
222 return mdb_test_sarg_node(mdb, node, fields, num_fields);
223 }
224 #if 0
225 int mdb_test_sargs(MdbHandle *mdb, MdbColumn *col, int offset, int len)
226 {
227 MdbSarg *sarg;
228 int i;
229
230 for (i=0;i<col->num_sargs;i++) {
231 sarg = g_ptr_array_index (col->sargs, i);
232 if (!mdb_test_sarg(mdb, col, sarg, offset, len)) {
233 /* sarg didn't match, no sense going on */
234 return 0;
235 }
236 }
237
238 return 1;
239 }
240 #endif
mdb_add_sarg(MdbColumn * col,MdbSarg * in_sarg)241 int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg)
242 {
243 MdbSarg *sarg;
244 if (!col->sargs) {
245 col->sargs = g_ptr_array_new();
246 }
247 sarg = g_memdup(in_sarg,sizeof(MdbSarg));
248 g_ptr_array_add(col->sargs, sarg);
249 col->num_sargs++;
250
251 return 1;
252 }
mdb_add_sarg_by_name(MdbTableDef * table,char * colname,MdbSarg * in_sarg)253 int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg)
254 {
255 MdbColumn *col;
256 unsigned int i;
257
258 for (i=0;i<table->num_cols;i++) {
259 col = g_ptr_array_index (table->columns, i);
260 if (!strcasecmp(col->name,colname)) {
261 return mdb_add_sarg(col, in_sarg);
262 }
263 }
264 /* else didn't find the column return 0! */
265 return 0;
266 }
267