diff options
-rw-r--r-- | source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c | 405 |
1 files changed, 299 insertions, 106 deletions
diff --git a/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c b/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c index 9ed137c4f5..25efc8ced2 100644 --- a/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c +++ b/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c @@ -1,7 +1,6 @@ /* ldb database library - Copyright (C) Andrew Tridgell 2004 Copyright (C) Derrell Lipman 2005 ** NOTE! The following LGPL license applies to the ldb @@ -37,6 +36,7 @@ #include "includes.h" #include "ldb/include/ldb.h" #include "ldb/include/ldb_private.h" +#include "ldb/include/ldb_parse.h" #include "ldb/ldb_sqlite3/ldb_sqlite3.h" #ifndef FALSE @@ -294,124 +294,314 @@ lsqlite3_add_msg_attr(struct ldb_context *ldb, } #endif -/* - * search for matching records - */ -static int -lsqlite3_search(struct ldb_module *module, - const char *base, - enum ldb_scope scope, - const char *expression, - const char * const attrs[], - struct ldb_message ***res) +static char * +lsqlite3_parsetree_to_sql(struct ldb_module *module, + char * hTalloc, + const struct ldb_parse_tree *t) { -#warning "lsqlite3_search() not yet implemented" -#if 0 - int count; - int msg_count; - struct ldb_context * ldb = module->ldb; - struct lsqlite3_private * lsqlite3 = module->private_data; + int i; + char * child; + char * p; + char * ret = NULL; + char * pAttrName; + - if (base == NULL) { - base = ""; - } + switch(t->operation) { + case LDB_OP_SIMPLE: + break; - lsqlite3->last_rc = ldap_search_s(lsqlite3->ldap, base, (int)scope, - expression, - discard_const_p(char *, attrs), - 0, &ldapres); - if (lsqlite3->last_rc != LDAP_SUCCESS) { - return -1; - } + case LDB_OP_AND: + ret = lsqlite3_parsetree_to_sql(module, + hTalloc, + t->u.list.elements[0]); + + for (i = 1; i < t->u.list.num_elements; i++) { + child = + lsqlite3_parsetree_to_sql( + module, + hTalloc, + t->u.list.elements[i]); + ret = talloc_asprintf_append(ret, + "INTERSECT\n" + "%s\n", + child); + talloc_free(child); + } - count = ldap_count_entries(lsqlite3->ldap, ldapres); - if (count == -1 || count == 0) { - ldap_msgfree(ldapres); - return count; - } + child = ret; + ret = talloc_asprintf("(\n" + "%s\n" + ")\n", + child); + talloc_free(child); + return ret; + + case LDB_OP_OR: + child = + lsqlite3_parsetree_to_sql( + module, + hTalloc, + t->u.list.elements[0]); + + for (i = 1; i < t->u.list.num_elements; i++) { + child = + lsqlite3_parsetree_to_sql( + module, + hTalloc, + t->u.list.elements[i]); + ret = talloc_asprintf_append(ret, + "UNION\n" + "%s\n", + child); + talloc_free(child); + } + child = ret; + ret = talloc_asprintf("(\n" + "%s\n" + ")\n", + child); + talloc_free(child); + return ret; + + case LDB_OP_NOT: + child = + lsqlite3_parsetree_to_sql( + module, + hTalloc, + t->u.not.child); + ret = talloc_asprintf(hTalloc, + "(\n" + " SELECT eid\n" + " FROM ldb_entry\n" + " WHERE eid NOT IN %s\n" + ")\n", + child); + talloc_free(child); + return ret; + + default: + /* should never occur */ + abort(); + }; + + /* Get a case-folded copy of the attribute name */ + pAttrName = ldb_casefold((struct ldb_context *) module, + t->u.simple.attr); - (*res) = talloc_array(lsqlite3, struct ldb_message *, count+1); - if (! *res) { - ldap_msgfree(ldapres); - errno = ENOMEM; - return -1; - } + /* + * For simple searches, we want to retrieve the list of EIDs that + * match the criteria. We accomplish this by searching the + * appropriate table, ldb_attr_<attributeName>, for the eid + * corresponding to all matching values. + */ + if (t->u.simple.value.length == 1 && + (*(const char *) t->u.simple.value.data) == '*') { + /* + * Special case for "attr_name=*". In this case, we want the + * eid corresponding to all values in the specified attribute + * table. + */ + if ((p = sqlite3_mprintf("(\n" + " SELECT eid\n" + " FROM ldb_attr_%q\n" + ")\n", + pAttrName)) == NULL) { + return NULL; + } - (*res)[0] = NULL; + ret = talloc_strdup(hTalloc, p); + sqlite3_free(p); - msg_count = 0; + } else if (strcasecmp(t->u.simple.attr, "objectclass") == 0) { + /* + * For object classes, we want to search for all objectclasses + * that are subclasses as well. + */ + if ((p = sqlite3_mprintf( + "(\n" + " SELECT eid\n" + " FROM ldb_attr_objectclass\n" + " WHERE attr_name IN\n" + " (SELECT class_name\n" + " FROM ldb_objectclasses\n" + " WHERE tree_key GLOB\n" + " (SELECT tree_key\n" + " FROM ldb_objectclasses\n" + " WHERE class_name = %Q) || '*')\n" + ")\n", + t->u.simple.value.data)) == NULL) { + return NULL; + } - /* loop over all messages */ - for (msg=ldap_first_entry(lsqlite3->ldap, ldapres); - msg; - msg=ldap_next_entry(lsqlite3->ldap, msg)) { - BerElement *berptr = NULL; - char *attr, *dn; + ret = talloc_strdup(hTalloc, p); + sqlite3_free(p); - if (msg_count == count) { - /* hmm, got too many? */ - ldb_debug(ldb, LDB_DEBUG_FATAL, "Fatal: ldap message count inconsistent\n"); - break; - } + } else { + /* A normal query. */ + if ((p = sqlite3_mprintf("(\n" + " SELECT eid\n" + " FROM ldb_attr_%q\n" + " WHERE attr_value = %Q\n" + ")\n", + pAttrName, + t->u.simple.value.data)) == NULL) { + return NULL; + } - (*res)[msg_count] = talloc(*res, struct ldb_message); - if (!(*res)[msg_count]) { - goto failed; - } - (*res)[msg_count+1] = NULL; + ret = talloc_strdup(hTalloc, p); + sqlite3_free(p); + } + return ret; +} - dn = ldap_get_dn(lsqlite3->ldap, msg); - if (!dn) { - goto failed; - } - (*res)[msg_count]->dn = talloc_strdup((*res)[msg_count], dn); - ldap_memfree(dn); - if (!(*res)[msg_count]->dn) { - goto failed; - } +static char * +lsqlite3_parsetree_to_tablelist(struct ldb_module *module, + char * hTalloc, + const struct ldb_parse_tree *t) +{ +#warning "obtain talloc'ed array of attribute names for table list" + return NULL; +} - (*res)[msg_count]->num_elements = 0; - (*res)[msg_count]->elements = NULL; - (*res)[msg_count]->private_data = NULL; +/* + * search for matching records + */ +static int +lsqlite3_search(struct ldb_module * module, + const char * pBaseDN, + enum ldb_scope scope, + const char * pExpression, + const char * const attrs[], + struct ldb_message *** res) +{ + int ret; + int bLoop; + long long eid; + char * sql; + char * sql_constraints; + char * table_list; + char * hTalloc; + const char * pTail; + sqlite3_stmt * pStmt; + struct ldb_parse_tree * pTree; + struct lsqlite3_private * lsqlite3 = module->private_data; + + if (pBaseDN == NULL) { + pBaseDN = ""; + } - /* loop over all attributes */ - for (attr=ldap_first_attribute(lsqlite3->ldap, msg, &berptr); - attr; - attr=ldap_next_attribute(lsqlite3->ldap, msg, berptr)) { - struct berval **bval; - bval = ldap_get_values_len(lsqlite3->ldap, msg, attr); + /* + * Obtain the eid of the base DN + */ + if ((pTail = sqlite3_mprintf("SELECT eid " + " FROM ldb_attr_dn " + " WHERE attr_value = %Q;", + pBaseDN)) == NULL) { + return -1; + } - if (bval) { - lsqlite3_add_msg_attr(ldb, (*res)[msg_count], attr, bval); - ldap_value_free_len(bval); - } - - ldap_memfree(attr); - } - if (berptr) ber_free(berptr, 0); + for (bLoop = TRUE; bLoop; ) { - msg_count++; - } + /* Compile the SQL statement into sqlite virtual machine */ + if ((ret = sqlite3_prepare(lsqlite3->sqlite, + pTail, + -1, + &pStmt, + &pTail)) != SQLITE_OK) { + ret = -1; + break; + } + + /* One row expected */ + if ((ret = sqlite3_step(pStmt)) == SQLITE_SCHEMA) { + (void) sqlite3_finalize(pStmt); + continue; + } else if (ret != SQLITE_ROW) { + (void) sqlite3_finalize(pStmt); + ret = -1; + break; + } + + /* Retrieve the EID */ + eid = sqlite3_column_int64(pStmt, 0); + + /* Free the virtual machine */ + if ((ret = sqlite3_finalize(pStmt)) == SQLITE_SCHEMA) { + (void) sqlite3_finalize(pStmt); + continue; + } else if (ret != SQLITE_OK) { + (void) sqlite3_finalize(pStmt); + ret = -1; + break; + } - ldap_msgfree(ldapres); + /* + * Normal condition is only one time through loop. Loop is + * rerun in error conditions, via "continue", above. + */ + ret = 0; + bLoop = FALSE; + } - return msg_count; + /* Parse the filter expression into a tree we can work with */ + if ((pTree = ldb_parse_tree(module->ldb, pExpression)) == NULL) { + return -1; + } + + /* Allocate a temporary talloc context */ + hTalloc = talloc_new(module); -failed: - if (*res) lsqlite3_search_free(module, *res); - return -1; -#else - return -1; -#endif + /* Move the parse tree to our temporary context */ + talloc_steal(hTalloc, pTree); + + /* Convert filter into a series of SQL statements (constraints) */ + sql_constraints = lsqlite3_parsetree_to_sql(module, hTalloc, pTree); + + /* Get the list of attribute names to use as our extra table list */ + table_list = lsqlite3_parsetree_to_tablelist(module, hTalloc, pTree); + + switch(scope) { + case LDB_SCOPE_DEFAULT: + case LDB_SCOPE_SUBTREE: + sql = sqlite3_mprintf( + "SELECT entry.entry_data\n" + " FROM ldb_entry AS entry\n" + " WHERE entry.eid IN\n" + " (SELECT ldb_entry.eid\n" + " FROM ldb_entry,\n" + " ldb_descendants,\n" + " %q\n" + " WHERE ldb_descendants.aeid = %lld\n" + " AND ldb_entry.eid = ldb_descendants.deid\n" + " AND ldap_entry.eid IN\n" + "%s);", + table_list, + sql_constraints); + break; + +#warning "scope BASE and ONLEVEL not yet implemented" + case LDB_SCOPE_BASE: + break; + + case LDB_SCOPE_ONELEVEL: + break; + } + + return ret; } static int -lsqlite3_new_attr(struct lsqlite3_private * lsqlite3, +lsqlite3_new_attr(struct ldb_module * module, char * pAttrName) { + struct lsqlite3_private * lsqlite3 = module->private_data; + + /* Get a case-folded copy of the attribute name */ + pAttrName = ldb_casefold((struct ldb_context *) module, pAttrName); + QUERY_NOROWS(lsqlite3, FALSE, "CREATE TABLE ldb_attr_%q " @@ -429,12 +619,13 @@ lsqlite3_new_attr(struct lsqlite3_private * lsqlite3, * requests in the ldb_message */ static int -lsqlite3_msg_to_sql(struct ldb_module *module, - const struct ldb_message *msg, +lsqlite3_msg_to_sql(struct ldb_module * module, + const struct ldb_message * msg, long long eid, int use_flags) { int flags; + char * pAttrName; unsigned int i; unsigned int j; struct lsqlite3_private * lsqlite3 = module->private_data; @@ -450,7 +641,7 @@ lsqlite3_msg_to_sql(struct ldb_module *module, if (flags == LDB_FLAG_MOD_ADD) { /* Create the attribute table if it doesn't exist */ - if (lsqlite3_new_attr(lsqlite3, el->name) != 0) { + if (lsqlite3_new_attr(module, el->name) != 0) { return -1; } } @@ -458,6 +649,10 @@ lsqlite3_msg_to_sql(struct ldb_module *module, /* For each value of the specified attribute name... */ for (j = 0; j < el->num_values; j++) { + /* Get a case-folded copy of the attribute name */ + pAttrName = ldb_casefold((struct ldb_context *) module, + el->name); + /* ... bind the attribute value, if necessary */ switch (flags) { case LDB_FLAG_MOD_ADD: @@ -467,6 +662,7 @@ lsqlite3_msg_to_sql(struct ldb_module *module, " (eid, attr_value) " " VALUES " " (%lld, %Q);", + pAttrName, eid, el->values[j].data); QUERY_NOROWS(lsqlite3, FALSE, @@ -486,6 +682,7 @@ lsqlite3_msg_to_sql(struct ldb_module *module, "UPDATE ldb_attr_%q " " SET attr_value = %Q " " WHERE eid = %lld;", + pAttrName, el->values[j].data, eid); QUERY_NOROWS(lsqlite3, @@ -506,6 +703,7 @@ lsqlite3_msg_to_sql(struct ldb_module *module, "DELETE FROM ldb_attr_%q " " WHERE eid = %lld " " AND attr_value = %Q;", + pAttrName, eid, el->values[j].data); QUERY_NOROWS(lsqlite3, @@ -903,16 +1101,11 @@ lsqlite3_initialize(struct lsqlite3_private *lsqlite3, ; /* Skip protocol indicator of url */ - if (strchr(url, ':')) { - if (strncmp(url, "sqlite://", 9) != 0) { - errno = EINVAL; - return SQLITE_MISUSE; - } - p = url + 9; - } else { - p = url; - } - + if ((p = strchr(url, ':')) == NULL) { + return SQLITE_MISUSE; + } else { + ++p; + } /* * See if we'll be creating a new database, or opening an existing one |