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 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 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 } 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 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 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 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 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 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 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 } 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