r6956: added start of ldb_sqlite3 work
authorDerrell Lipman <derrell@samba.org>
Tue, 24 May 2005 13:31:22 +0000 (13:31 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:17:00 +0000 (13:17 -0500)
(This used to be commit ac396a4a53756f40ad5e1d45ca23e002f9c649e7)

source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c [new file with mode: 0644]
source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.h [new file with mode: 0644]
source4/lib/ldb/ldb_sqlite3/schema [new file with mode: 0644]

diff --git a/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c b/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.c
new file mode 100644 (file)
index 0000000..67debd8
--- /dev/null
@@ -0,0 +1,951 @@
+/* 
+   ldb database library
+
+   Copyright (C) Andrew Tridgell  2004
+
+     ** NOTE! The following LGPL license applies to the ldb
+     ** library. This does NOT imply that all of Samba is released
+     ** under the LGPL
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser 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
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb sqlite backend
+ *
+ *  Description: core files for SQLITE3 backend
+ *
+ *  Author: Derrell Lipman (based on Andrew Tridgell's LDAP backend)
+ */
+
+#include "includes.h"
+#include "ldb/include/ldb.h"
+#include "ldb/include/ldb_private.h"
+#include "ldb/ldb_sqlite3/ldb_sqlite3.h"
+
+#if 0
+/*
+  we don't need this right now, but will once we add some backend 
+  options
+*/
+
+/*
+  find an option in an option list (a null terminated list of strings)
+
+  this assumes the list is short. If it ever gets long then we really
+  should do this in some smarter way
+ */
+static const char *lsqlite3_option_find(const struct lsqlite3_private *lsqlite3, const char *name)
+{
+       int i;
+       size_t len = strlen(name);
+
+       if (!lsqlite3->options) return NULL;
+
+       for (i=0;lsqlite3->options[i];i++) {            
+               if (strncmp(lsqlite3->options[i], name, len) == 0 &&
+                   lsqlite3->options[i][len] == '=') {
+                       return &lsqlite3->options[i][len+1];
+               }
+       }
+
+       return NULL;
+}
+#endif
+
+/*
+  rename a record
+*/
+static int lsqlite3_rename(struct ldb_module *module, const char *olddn, const char *newdn)
+{
+        int column;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+
+       /* ignore ltdb specials */
+       if (olddn[0] == '@' ||newdn[0] == '@') {
+               return 0;
+       }
+
+        /* Bind old distinguished names */
+        column = sqlite3_bind_parameter_index(lsqlite3->renameDN, ":oldDN");
+        if (sqlite3_bind_text(lsqlite3->renameDN, column,
+                              olddn, strlen(olddn),
+                              SQLITE_STATIC) != SQLITE_OK) {
+                return -1;
+        }
+
+        /* Bind new distinguished names */
+        column = sqlite3_bind_parameter_index(lsqlite3->renameDN, ":newDN");
+        if (sqlite3_bind_text(lsqlite3->renameDN, column,
+                              newdn, strlen(newdn),
+                              SQLITE_STATIC) != SQLITE_OK) {
+                return -1;
+        }
+
+        do {
+                lsqlite3->last_rc = sqlite3_step(lsqlite3->renameDN);
+                (void) sqlite3_reset(lsqlite3->renameDN);
+        } while lsqlite3->last_rc == SQLITE3_BUSY;
+
+       return lsqlite3->last_rc == 0 ? 0 : -1;
+}
+
+/*
+  delete a record
+*/
+static int lsqlite3_delete(struct ldb_module *module, const char *dn)
+{
+       int ret = 0;
+        int column;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+
+       /* ignore ltdb specials */
+       if (dn[0] == '@') {
+               return 0;
+       }
+       
+        /* Bind new distinguished names */
+        column = sqlite3_bind_parameter_index(lsqlite3->renameDN, ":dn");
+        if (sqlite3_bind_text(lsqlite3->deleteDN, column,
+                              dn, strlen(dn),
+                              SQLITE_STATIC) != SQLITE_OK) {
+                return -1;
+        }
+
+        do {
+                lsqlite3->last_rc = sqlite3_step(lsqlite3->deleteDN);
+                (void) sqlite3_reset(lsqlite3->deleteDN);
+        } while lsqlite3->last_rc == SQLITE3_BUSY;
+
+       return lsqlite3->last_rc == 0 ? 0 : -1;
+}
+
+/*
+  free a search result
+*/
+static int lsqlite3_search_free(struct ldb_module *module, struct ldb_message **res)
+{
+       talloc_free(res);
+       return 0;
+}
+
+
+/*
+  add a single set of ldap message values to a ldb_message
+*/
+static int lsqlite3_add_msg_attr(struct ldb_context *ldb,
+                            struct ldb_message *msg, 
+                            const char *attr, struct berval **bval)
+{
+       int count, i;
+       struct ldb_message_element *el;
+
+       count = ldap_count_values_len(bval);
+
+       if (count <= 0) {
+               return -1;
+       }
+
+       el = talloc_realloc(msg, msg->elements, struct ldb_message_element, 
+                             msg->num_elements + 1);
+       if (!el) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       msg->elements = el;
+
+       el = &msg->elements[msg->num_elements];
+
+       el->name = talloc_strdup(msg->elements, attr);
+       if (!el->name) {
+               errno = ENOMEM;
+               return -1;
+       }
+       el->flags = 0;
+
+       el->num_values = 0;
+       el->values = talloc_array(msg->elements, struct ldb_val, count);
+       if (!el->values) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       for (i=0;i<count;i++) {
+               el->values[i].data = talloc_memdup(el->values, bval[i]->bv_val, bval[i]->bv_len);
+               if (!el->values[i].data) {
+                       return -1;
+               }
+               el->values[i].length = bval[i]->bv_len;
+               el->num_values++;
+       }
+
+       msg->num_elements++;
+
+       return 0;
+}
+
+/*
+  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)
+{
+       struct ldb_context *ldb = module->ldb;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+       int count, msg_count;
+
+       if (base == NULL) {
+               base = "";
+       }
+
+       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;
+       }
+
+       count = ldap_count_entries(lsqlite3->ldap, ldapres);
+       if (count == -1 || count == 0) {
+               ldap_msgfree(ldapres);
+               return count;
+       }
+
+       (*res) = talloc_array(lsqlite3, struct ldb_message *, count+1);
+       if (! *res) {
+               ldap_msgfree(ldapres);
+               errno = ENOMEM;
+               return -1;
+       }
+
+       (*res)[0] = NULL;
+
+       msg_count = 0;
+
+       /* 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;
+
+               if (msg_count == count) {
+                       /* hmm, got too many? */
+                       ldb_debug(ldb, LDB_DEBUG_FATAL, "Fatal: ldap message count inconsistent\n");
+                       break;
+               }
+
+               (*res)[msg_count] = talloc(*res, struct ldb_message);
+               if (!(*res)[msg_count]) {
+                       goto failed;
+               }
+               (*res)[msg_count+1] = NULL;
+
+               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;
+               }
+
+
+               (*res)[msg_count]->num_elements = 0;
+               (*res)[msg_count]->elements = NULL;
+               (*res)[msg_count]->private_data = NULL;
+
+               /* 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);
+
+                       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);
+
+               msg_count++;
+       }
+
+       ldap_msgfree(ldapres);
+
+       return msg_count;
+
+failed:
+       if (*res) lsqlite3_search_free(module, *res);
+       return -1;
+}
+
+
+/*
+  Issue a series of SQL statements to implement the requests in the ldb_message
+*/
+static int lsqlite3_msg_to_sql(struct ldb_context *ldb,
+                               const struct ldb_message *msg,
+                               int modify_existing)
+{
+       unsigned int i, j;
+       struct ldb_context *ldb = module->ldb;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+        sqlite3_stmt *stmt = NULL;
+
+       for (i=0;i<msg->num_elements;i++) {
+               const struct ldb_message_element *el = &msg->elements[i];
+
+               if (! modify_existing) {
+                        /* This is a new DN.  Bind new distinguished name */
+                        column =
+                                sqlite3_bind_parameter_index(
+                                        lsqlite3->queries.newDN,
+                                        ":dn");
+                        if (sqlite3_bind_text(lsqlite3->queries.newDN, column,
+                                              msg->dn, strlen(msg->dn),
+                                              SQLITE_STATIC) != SQLITE_OK) {
+                                return -1;
+                        }
+
+                        /* Add this new DN */
+                        do {
+                                lsqlite3->last_rc =
+                                        sqlite3_step(lsqlite3->queries.newDN);
+                                (void) sqlite3_reset(lsqlite3->queries.newDN);
+                        } while lsqlite3->last_rc == SQLITE_BUSY;
+                        
+                        if (lsqlite3->last_rc != SQLITE_DONE) {
+                                return -1;
+                        }
+
+                        dn_id = last_insert_rowid(lsqlite3->sqlite3);
+
+                        stmt = lsqlite3->queries.newAttribute;
+
+               } else {
+                        /* Get the dn_id for the specified DN */
+                        xxx;
+
+                       switch (el->flags & LDB_FLAG_MOD_MASK) {
+                       case LDB_FLAG_MOD_ADD:
+                                stmt = lsqlite3->queries.addAttrValuePair;
+                               break;
+                       case LDB_FLAG_MOD_DELETE:
+                                stmt = lsqlite3->queries.deleteAttrValuePairs;
+                               break;
+                       case LDB_FLAG_MOD_REPLACE:
+                                stmt = lsqlite3->queries.replaceAttrValuePairs;
+                               break;
+                       }
+                        
+                }
+
+               for (j=0;j<el->num_values;j++) {
+                       mods[num_mods]->mod_vals.modv_bvals[j] = talloc(mods[num_mods]->mod_vals.modv_bvals,
+                                                                         struct berval);
+                       if (!mods[num_mods]->mod_vals.modv_bvals[j]) {
+                               goto failed;
+                       }
+                       mods[num_mods]->mod_vals.modv_bvals[j]->bv_val = el->values[j].data;
+                       mods[num_mods]->mod_vals.modv_bvals[j]->bv_len = el->values[j].length;
+               }
+               mods[num_mods]->mod_vals.modv_bvals[j] = NULL;
+               num_mods++;
+       }
+
+       return mods;
+
+failed:
+       talloc_free(mods);
+       return NULL;
+}
+
+
+/*
+  add a record
+*/
+static int lsqlite3_add(struct ldb_module *module, const struct ldb_message *msg)
+{
+       struct ldb_context *ldb = module->ldb;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+       LDAPMod **mods;
+       int ret = 0;
+
+       /* ignore ltdb specials */
+       if (msg->dn[0] == '@') {
+               return 0;
+       }
+
+       mods = lsqlite3_msg_to_mods(ldb, msg, 0);
+
+       lsqlite3->last_rc = ldap_add_s(lsqlite3->ldap, msg->dn, mods);
+       if (lsqlite3->last_rc != LDAP_SUCCESS) {
+               ret = -1;
+       }
+
+       talloc_free(mods);
+
+       return ret;
+}
+
+
+/*
+  modify a record
+*/
+static int lsqlite3_modify(struct ldb_module *module, const struct ldb_message *msg)
+{
+       struct ldb_context *ldb = module->ldb;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+       LDAPMod **mods;
+       int ret = 0;
+
+       /* ignore ltdb specials */
+       if (msg->dn[0] == '@') {
+               return 0;
+       }
+
+       mods = lsqlite3_msg_to_mods(ldb, msg, 1);
+
+       lsqlite3->last_rc = ldap_modify_s(lsqlite3->ldap, msg->dn, mods);
+       if (lsqlite3->last_rc != LDAP_SUCCESS) {
+               ret = -1;
+       }
+
+       talloc_free(mods);
+
+       return ret;
+}
+
+static int lsqlite3_lock(struct ldb_module *module, const char *lockname)
+{
+       int ret = 0;
+       struct ldb_context *ldb = module->ldb;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+
+       if (lockname == NULL) {
+               return -1;
+       }
+
+        /* If we're already locked, just update lock count */
+        if (++lsqlite3->lock_count > 1) {
+                return -1;
+        }
+            
+        /* Write-lock (but not read-lock) the database */
+        lsqlite3->last_rc = sqlite3_step(lsqlite3->begin);
+
+        /* Ready the compiled statememt for its next use */
+        (void ) sqlite_reset(lsqlite3->begin);
+
+       return lsqlite3->last_rc == 0 ? 0 : -1;
+}
+
+static int lsqlite3_unlock(struct ldb_module *module, const char *lockname)
+{
+       int ret = 0;
+       struct ldb_context *ldb = module->ldb;
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+
+       if (lockname == NULL) {
+               return -1;
+       }
+
+        /* If we're not already locked, there's nothing to do */
+        if (lsqlite3->lock_count == 0) {
+                return 0;
+        }
+
+        /* Decrement lock count */
+        if (--lsqlite3->lock_count == 0) {
+        
+                /* Final unlock.  Unlock the database */
+                lsqlite3->last_rc = sqlite3_step(lsqlite3->commit);
+
+                /* Ready the compiled statememt for its next use */
+                (void ) sqlite_reset(lsqlite3->commit);
+        }
+
+       return lsqlite3->last_rc == 0 ? 0 : -1;
+}
+
+/*
+  return extended error information
+*/
+static const char *lsqlite3_errstring(struct ldb_module *module)
+{
+       struct lsqlite3_private *lsqlite3 = module->private_data;
+       return sqlite3_errmsg(lsqlite3->sqlite3);
+}
+
+
+static const struct ldb_module_ops lsqlite3_ops = {
+       "sqlite",
+       lsqlite3_search,
+       lsqlite3_search_free,
+       lsqlite3_add,
+       lsqlite3_modify,
+       lsqlite3_delete,
+       lsqlite3_rename,
+       lsqlite3_lock,
+       lsqlite3_unlock,
+       lsqlite3_errstring
+};
+
+
+static int lsqlite3_destructor(void *p)
+{
+       struct lsqlite3_private *lsqlite3 = p;
+        (void) sqlite3_close(lsqlite3->sqlite3);
+       return 0;
+}
+
+static int lsqlite3_initialize(lsqlite3_private *lsqlite3,
+                               const char *url)
+{
+        int bNewDatabase = False;
+        char *p;
+        char *pTail;
+        struct stat statbuf;
+        sqlite3_stmt *stmt;
+        const char *schema =
+                "
+                -- ------------------------------------------------------
+
+                PRAGMA auto_vacuum=1;
+
+                -- ------------------------------------------------------
+
+                BEGIN EXCLUSIVE;
+
+                -- ------------------------------------------------------
+
+                CREATE TABLE ldb_info AS 
+                  SELECT 'LDB' AS database_type, 
+                         '1.0' AS version;
+
+                CREATE TABLE ldb_distinguished_names 
+                (
+                  dn_id         INTEGER PRIMARY KEY AUTOINCREMENT, 
+                  dn            TEXT UNIQUE
+                );
+
+                CREATE TABLE ldb_object_classes 
+                (
+                  class_name    TEXT PRIMARY KEY,
+                  tree_key      TEXT,
+                  max_child_num INTEGER
+                );
+
+                CREATE TABLE ldb_dn_object_classes 
+                (
+                  dn_id         INTEGER REFERENCES ldb_distinguished_names, 
+                  class_name    TEXT REFERENCES ldb_object_classes 
+                );
+
+                CREATE TABLE ldb_attributes
+                (
+                  attr_name             TEXT PRIMARY KEY,
+                  case_insensitive_p    BOOLEAN DEFAULT FALSE,
+                  wildcard_p            BOOLEAN DEFAULT FALSE,
+                  hidden_p              BOOLEAN DEFAULT FALSE,
+                  integer_p             BOOLEAN DEFAULT FALSE
+                );
+
+                CREATE TABLE ldb_attr_value_pairs 
+                (
+                  dn_id         INTEGER REFERENCES ldb_distinguished_names, 
+                  attr_name     TEXT REFERENCES ldb_attributes,
+                  attr_value    TEXT 
+                );
+
+                -- ------------------------------------------------------
+
+                CREATE TRIGGER ldb_distinguished_names_delete_tr
+                  AFTER DELETE
+                  ON ldb_distinguished_names
+                  FOR EACH ROW
+                    BEGIN
+                      DELETE FROM ldb_attr_value_pairs
+                        WHERE dn_id = old.dn_id;
+                      DELETE FROM ldb_dn_object_classes
+                        WHERE dn_id = old.dn_id;
+                    END;
+
+                CREATE TRIGGER ldb_attr_value_pairs_insert_tr
+                  BEFORE INSERT
+                  ON ldb_attr_value_pairs
+                  FOR EACH ROW
+                    BEGIN
+                      INSERT OR IGNORE INTO ldb_attributes
+                          (attr_name)
+                        VALUES
+                          (new.attr_name);
+                    END;
+
+                CREATE TRIGGER ldb_attr_value_pairs_delete_tr
+                  AFTER DELETE
+                  ON ldb_attr_value_pairs
+                  FOR EACH ROW
+                    BEGIN
+                      DELETE FROM ldb_attributes
+                        WHERE (SELECT COUNT(*)
+                                 FROM ldb_attr_value_pairs
+                                 WHERE attr_name = old.attr_name) = 0
+                          AND attr_name = old.attr_name;
+                    END;
+
+                -- ------------------------------------------------------
+
+                CREATE INDEX ldb_distinguished_names_dn_idx
+                  ON ldb_distinguished_names (dn);
+
+                CREATE INDEX ldb_object_classes_tree_key_idx
+                  ON ldb_object_classes (tree_key);
+
+
+                CREATE INDEX ldb_dn_object_classes_dn_id_idx
+                  ON ldb_dn_object_classes (dn_id);
+
+                CREATE INDEX ldb_dn_object_classes_class_name_idx
+                  ON ldb_dn_object_classes (class_name);
+
+
+                CREATE INDEX ldb_attr_value_pairs_dn_id_name_case_idx
+                  ON ldb_attr_value_pairs (dn_id, attr_name);
+
+                CREATE INDEX ldb_attr_value_pairs_dn_id_name_nocase_idx
+                  ON ldb_attr_value_pairs (dn_id, attr_name COLLATE NOCASE);
+
+                -- ------------------------------------------------------
+
+                /* all defaults for dn, initially */
+                INSERT INTO ldb_attributes (attr_name)
+                  VALUES ('dn');
+
+                /* We need an implicit "top" level object class */
+                INSERT INTO ldb_object_classes (class_name, tree_key)
+                  SELECT 'top', /* next_tree_key(NULL) */ '0001';
+
+                -- ------------------------------------------------------
+
+                COMMIT;
+
+                -- ------------------------------------------------------
+                ";
+
+        /* Skip protocol indicator of 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
+         */
+        if ((stat(p, &statbuf) < 0 && errno == ENOENT) ||
+            statbuf.st_size == 0) {
+
+                bNewDatabase = True;
+        }
+
+        /* Try to open the (possibly empty/non-existent) database */
+        if ((lsqlite3->last_rc = sqlite3_open(p, &lsqlite3->sqlite3)) != SQLITE_SUCCESS) {
+                return ret;
+        }
+
+        if (bNewDatabase) {
+                /*
+                 * Create the database schema
+                 */
+                for (pTail = schema; pTail != NULL; ) {
+
+                        if ((lsqlite3->last_rc = sqlite3_prepare(
+                                     lsqlite3->sqlite3,
+                                     pTail,
+                                     -1,
+                                     &stmt,
+                                     &pTail)) != SQLITE_SUCCESS ||
+                            (lsqlite3->last_rc = sqlite3_step(stmt)) != SQLITE_DONE ||
+                            (lsqlite3->last_rc = sqlite_finalize(stmt)) != SQLITE_SUCCESS) {
+
+                                (void) sqlite3_close(lsqlite3->sqlite3);
+                                return ret;
+                        }
+                }
+        } else {
+                /*
+                 * Ensure that the database we opened is one of ours
+                 */
+                if ((lsqlite3->last_rc = sqlite3_prepare(
+                             lsqlite3->sqlite3,
+                             "SELECT COUNT(*) "
+                             "  FROM sqlite_master "
+                             "  WHERE type = 'table' "
+                             "    AND name IN "
+                             "      ("
+                             "        'ldb_info', "
+                             "        'ldb_distinguished_names', "
+                             "        'ldb_object_classes', "
+                             "        'ldb_dn_object_classes', "
+                             "        'ldb_attributes', "
+                             "        'ldb_attr_value_pairs' "
+                             "      );",
+                             -1,
+                             &stmt,
+                             &pTail)) != SQLITE_SUCCESS ||
+                    (lsqlite3->last_rc = sqlite3_step(stmt)) != SQLITE_ROW ||
+                    sqlite3_column_int(stmt, 0) != 6 ||
+                    (lsqlite3->last_rc = sqlite_finalize(stmt)) != SQLITE_SUCCESS ||
+
+                    (lsqlite3->last_rc = sqlite3_prepare(
+                             lsqlite3->sqlite3,
+                             "SELECT 1 "
+                             "  FROM ldb_info "
+                             "  WHERE database_type = 'LDB' "
+                             "    AND version = '1.0';",
+                             -1,
+                             &stmt,
+                             &pTail)) != SQLITE_SUCCESS ||
+                    (lsqlite3->last_rc = sqlite3_step(stmt)) != SQLITE_ROW ||
+                    (lsqlite3->last_rc = sqlite_finalize(stmt)) != SQLITE_SUCCESS) {
+                
+                        /* It's not one that we created.  See ya! */
+                        (void) sqlite3_close(lsqlite3->sqlite3);
+                        return SQLITE_MISUSE;
+                }
+        }
+
+        /*
+         * Pre-compile each of the queries we'll be using.
+         */
+
+        if ((lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "BEGIN IMMEDIATE;",
+                     -1,
+                     &lsqlite3->queries.begin,
+                     &pTail)) != SQLITE_SUCCESS ||
+
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "COMMIT;",
+                     -1,
+                     &lsqlite3->queries.commit,
+                     &pTail)) != SQLITE_SUCCESS ||
+
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "ROLLBACK;",
+                     -1,
+                     &lsqlite3->queries.rollback,
+                     &pTail)) != SQLITE_SUCCESS ||
+
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "INSERT INTO ldb_distinguished_names (dn_id, dn) "
+                     "  VALUES (:dn_id, :dn);",
+                     -1,
+                     &lsqlite3->queries.newDN,
+                     &pTail)) != SQLITE_SUCCESS ||
+
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "UPDATE ldb_distinguished_names "
+                     "  SET dn = :newDN "
+                     "  WHERE dn = :oldDN;",
+                     -1,
+                     &lsqlite3->queries.renameDN,
+                     &pTail)) != SQLITE_SUCCESS ||
+
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "DELETE FROM ldb_distinguished_names "
+                     "  WHERE dn = :dn;",
+                     -1,
+                     &lsqlite3->queries.deleteDN,
+                     &pTail)) != SQLITE_SUCCESS ||
+
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "INSERT OR IGNORE INTO ldb_object_classes "
+                     "    (class_name, tree_key)"
+                     "  SELECT :class_name, next_tree_key(NULL);",
+                     -1,
+                     &lsqlite3->queries.newObjectClass,
+                     &pTail)) != SQLITE_SUCCESS ||
+            
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "INSERT OR REPLACE INTO ldb_dn_object_classes "
+                     "    (dn_id, class_name) "
+                     "  VALUES (:dn_id, :class_name);",
+                     -1,
+                     &lsqlite3->queries.assignObjectClass,
+                     &pTail)) != SQLITE_SUCCESS ||
+            
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "INSERT OR IGNORE INTO ldb_attributes (name) "
+                     "  VALUES (:name);",
+                     -1,
+                     &lsqlite3->queries.newAttributeUseDefaults,
+                     &pTail)) != SQLITE_SUCCESS ||
+            
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "INSERT OR REPLACE INTO ldb_attributes "
+                     "    (name, "
+                     "     case_insensitive_p, "
+                     "     wildcard_p, "
+                     "     hidden_p, "
+                     "     integer_p) "
+                     "  VALUES (:name, "
+                     "          :case_insensitive_p, "
+                     "          :wildcard_p, "
+                     "          :hidden_p, "
+                     "          :integer_p);",
+                     -1,
+                     &lsqlite3->queries.newAttribute,
+                     &pTail)) != SQLITE_SUCCESS ||
+            
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "INSERT INTO ldb_attr_value_pairs "
+                     "    (dn_id, attr_name, attr_value) "
+                     "  VALUES (:dn_id, :attr_name, :attr_value);",
+                     -1,
+                     &lsqlite3->queries.addAttrValuePair,
+                     &pTail)) != SQLITE_SUCCESS ||
+            
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "UPDATE ldb_attr_value_pairs "
+                     "  SET attr_value = :attr_value "
+                     "  WHERE dn_id = :dn_id "
+                     "    AND attr_name = :attr_name;",
+                     -1,
+                     &lsqlite3->queries.addAttrValuePair,
+                     &pTail)) != SQLITE_SUCCESS ||
+            
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "DELETE FROM ldb_attr_value_pairs "
+                     "  WHERE dn_id = :dn_id "
+                     "    AND attr_name = :attr_name;"
+                     -1,
+                     &lsqlite3->queries.deleteAttrValuePair,
+                     &pTail)) != SQLITE_SUCCESS ||
+            
+            (lsqlite3->last_rc = sqlite3_prepare(
+                     lsqlite3->sqlite3,
+                     "INSERT OR REPLACE INTO ldb_object_classes "
+                     "    (class_name, tree_key) "
+                     "  SELECT :child_class, next_tree_key(:parent_class);"
+                     -1,
+                     &lsqlite3->queries.insertSubclass,
+                     &pTail)) != SQLITE_SUCCESS) {
+
+                (void) sqlite3_close(lsqlite3->sqlite3);
+                return ret;
+        }
+
+        return SQLITE_SUCCESS;
+}
+
+/*
+  connect to the database
+*/
+struct ldb_context *lsqlite3_connect(const char *url, 
+                                unsigned int flags, 
+                                const char *options[])
+{
+       struct ldb_context *ldb = NULL;
+       struct lsqlite3_private *lsqlite3 = NULL;
+       int i;
+
+       ldb = talloc(NULL, struct ldb_context);
+       if (!ldb) {
+               errno = ENOMEM;
+               goto failed;
+       }
+
+       lsqlite3 = talloc(ldb, struct lsqlite3_private);
+       if (!lsqlite3) {
+               errno = ENOMEM;
+               goto failed;
+       }
+
+       lsqlite3->sqlite3 = NULL;
+       lsqlite3->options = NULL;
+        lsqlite3->lock_count = 0;
+
+       lsqlite3->last_rc = lsqlite3_initialize(&lsqlite3->sqlite3, url);
+       if (lsqlite3->last_rc != LDAP_SUCCESS) {
+               goto failed;
+       }
+
+       talloc_set_destructor(lsqlite3, lsqlite3_destructor);
+
+       ldb->modules = talloc(ldb, struct ldb_module);
+       if (!ldb->modules) {
+               errno = ENOMEM;
+               goto failed;
+       }
+       ldb->modules->ldb = ldb;
+       ldb->modules->prev = ldb->modules->next = NULL;
+       ldb->modules->private_data = lsqlite3;
+       ldb->modules->ops = &lsqlite3_ops;
+
+       if (options) {
+               /* take a copy of the options array, so we don't have to rely
+                  on the caller keeping it around (it might be dynamic) */
+               for (i=0;options[i];i++) ;
+
+               lsqlite3->options = talloc_array(lsqlite3, char *, i+1);
+               if (!lsqlite3->options) {
+                       goto failed;
+               }
+               
+               for (i=0;options[i];i++) {
+                       lsqlite3->options[i+1] = NULL;
+                       lsqlite3->options[i] = talloc_strdup(lsqlite3->options, options[i]);
+                       if (!lsqlite3->options[i]) {
+                               goto failed;
+                       }
+               }
+       }
+
+       return ldb;
+
+failed:
+        if (lsqlite3->sqlite3 != NULL) {
+                (void) sqlite3_close(lsqlite3->sqlite3);
+        }
+       talloc_free(ldb);
+       return NULL;
+}
+
diff --git a/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.h b/source4/lib/ldb/ldb_sqlite3/ldb_sqlite3.h
new file mode 100644 (file)
index 0000000..75105fe
--- /dev/null
@@ -0,0 +1,25 @@
+#include <sqlite3.h>
+
+struct lsqlite3_private {
+       char **options;
+       const char *basedn;
+        sqlite3 * sqlite;
+        int lock_count;
+       int last_rc;
+        struct {
+                sqlite3_stmt *begin;
+                sqlite3_stmt *commit;
+                sqlite3_stmt *rollback;
+                sqlite3_stmt *newDN;
+                sqlite3_stmt *renameDN;
+                sqlite3_stmt *deleteDN;
+                sqlite3_stmt *newObjectClass;
+                sqlite3_stmt *assignObjectClass;
+                sqlite3_stmt *newAttributeUseDefaults;
+                sqlite3_stmt *newAttribute;
+                sqlite3_stmt *addAttrValuePair;
+                sqlite3_stmt *replaceAttrValuePairs;
+                sqlite3_stmt *deleteAttrValuePairs;
+                sqlite3_stmt *insertSubclass;
+        } queries;
+};
diff --git a/source4/lib/ldb/ldb_sqlite3/schema b/source4/lib/ldb/ldb_sqlite3/schema
new file mode 100644 (file)
index 0000000..2ba188c
--- /dev/null
@@ -0,0 +1,240 @@
+                -- ------------------------------------------------------
+
+                PRAGMA auto_vacuum=1;
+
+                -- ------------------------------------------------------
+
+                BEGIN EXCLUSIVE;
+
+                -- ------------------------------------------------------
+
+                CREATE TABLE ldb_info AS 
+                  SELECT 'LDB' AS database_type, 
+                         '1.0' AS version;
+
+                CREATE TABLE ldb_distinguished_names 
+                (
+                  dn_id         INTEGER PRIMARY KEY AUTOINCREMENT, 
+                  dn            TEXT UNIQUE
+                );
+
+                CREATE TABLE ldb_object_classes 
+                (
+                  class_name    TEXT PRIMARY KEY,
+                  tree_key      TEXT,
+                  max_child_num INTEGER
+                );
+
+                CREATE TABLE ldb_dn_object_classes 
+                (
+                  dn_id         INTEGER REFERENCES ldb_distinguished_names, 
+                  class_name    TEXT REFERENCES ldb_object_classes 
+                );
+
+                CREATE TABLE ldb_attributes
+                (
+                  attr_name             TEXT PRIMARY KEY,
+                  case_insensitive_p    BOOLEAN DEFAULT FALSE,
+                  wildcard_p            BOOLEAN DEFAULT FALSE,
+                  hidden_p              BOOLEAN DEFAULT FALSE,
+                  integer_p             BOOLEAN DEFAULT FALSE
+                );
+
+                CREATE TABLE ldb_attr_value_pairs 
+                (
+                  dn_id         INTEGER REFERENCES ldb_distinguished_names, 
+                  attr_name     TEXT REFERENCES ldb_attributes,
+                  attr_value    TEXT 
+                );
+
+                -- ------------------------------------------------------
+
+                CREATE TRIGGER ldb_distinguished_names_delete_tr
+                  AFTER DELETE
+                  ON ldb_distinguished_names
+                  FOR EACH ROW
+                    BEGIN
+                      DELETE FROM ldb_attr_value_pairs
+                        WHERE dn_id = old.dn_id;
+                      DELETE FROM ldb_dn_object_classes
+                        WHERE dn_id = old.dn_id;
+                    END;
+
+                CREATE TRIGGER ldb_attr_value_pairs_insert_tr
+                  BEFORE INSERT
+                  ON ldb_attr_value_pairs
+                  FOR EACH ROW
+                    BEGIN
+                      INSERT OR IGNORE INTO ldb_attributes
+                          (attr_name)
+                        VALUES
+                          (new.attr_name);
+                    END;
+
+                CREATE TRIGGER ldb_attr_value_pairs_delete_tr
+                  AFTER DELETE
+                  ON ldb_attr_value_pairs
+                  FOR EACH ROW
+                    BEGIN
+                      DELETE FROM ldb_attributes
+                        WHERE (SELECT COUNT(*)
+                                 FROM ldb_attr_value_pairs
+                                 WHERE attr_name = old.attr_name) = 0
+                          AND attr_name = old.attr_name;
+                    END;
+
+                -- ------------------------------------------------------
+
+                CREATE INDEX ldb_distinguished_names_dn_idx
+                  ON ldb_distinguished_names (dn);
+
+                CREATE INDEX ldb_object_classes_tree_key_idx
+                  ON ldb_object_classes (tree_key);
+
+
+                CREATE INDEX ldb_dn_object_classes_dn_id_idx
+                  ON ldb_dn_object_classes (dn_id);
+
+                CREATE INDEX ldb_dn_object_classes_class_name_idx
+                  ON ldb_dn_object_classes (class_name);
+
+
+                CREATE INDEX ldb_attr_value_pairs_dn_id_name_case_idx
+                  ON ldb_attr_value_pairs (dn_id, attr_name);
+
+                CREATE INDEX ldb_attr_value_pairs_dn_id_name_nocase_idx
+                  ON ldb_attr_value_pairs (dn_id, attr_name COLLATE NOCASE);
+
+                -- ------------------------------------------------------
+
+                /* all defaults for dn, initially */
+                INSERT INTO ldb_attributes (attr_name)
+                  VALUES ('dn');
+
+                /* We need an implicit "top" level object class */
+                INSERT INTO ldb_object_classes (class_name, tree_key)
+                  SELECT 'top', /* next_tree_key(NULL) */ '0001';
+
+                -- ------------------------------------------------------
+
+                COMMIT;
+
+                -- ------------------------------------------------------
+
+/*
+ * dn: o=University of Michigan,c=US
+ * objectclass: organization
+ * objectclass: domainRelatedObject
+ */
+-- newDN
+INSERT INTO ldb_distinguished_names (dn_id, dn)
+  VALUES (1, 'o=University of Michigan,c=US');
+
+-- newObjectClass
+INSERT OR IGNORE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'organization', /* next_tree_key(NULL) */ '0002';
+
+INSERT OR IGNORE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'domainRelatedObject', /* next_tree_key(NULL) */ '0003';
+
+-- assignObjectClass
+INSERT OR IGNORE INTO ldb_dn_object_classes (dn_id, class_name)
+  VALUES (1, 'organization');
+
+INSERT OR IGNORE INTO ldb_dn_object_classes (dn_id, class_name)
+  VALUES (1, 'domainRelatedObject');
+
+/*
+ * l: Ann Arbor, Michigan
+ * st: Michigan
+ * o: University of Michigan
+ * o: UMICH
+ * o: UM
+ * o: U-M
+ * o: U of M
+ * description: The University of Michigan at Ann Arbor
+ * seeAlso:
+ * postaladdress: University of Michigan $ 535 W. William St. $ Ann Arbor, MI 481
+ *  09 $ US
+ * telephonenumber: +1 313 764-1817
+ * associateddomain: example.com
+ */
+-- addAttrValuePair
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'l', 'Ann Arbor, Michigan');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'st', 'Michigan');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'o', 'University of Michigan');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'o', 'UMICH');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'o', 'UM');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'o', 'U-M');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'o', 'U of M');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'description', 'The University of Michigan at Ann Arbor');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'seeAlso', '');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'postaladdress', 'University of Michigan $ 535 W. William St. $ Ann Arbor, MI 48109 $ US');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'telephonenumber', '+1 313 764-1817');
+INSERT INTO ldb_attr_value_pairs (dn_id, attr_name, attr_value)
+  VALUES (1, 'associateddomain', 'example.com');
+
+-- ----------------------------------------------------------------------
+
+/*
+ * dn: @ATTRIBUTES
+ * uid: CASE_INSENSITIVE WILDCARD
+ * cn: CASE_INSENSITIVE
+ * ou: CASE_INSENSITIVE
+ * dn: CASE_INSENSITIVE
+ */
+-- newAttribute
+INSERT OR REPLACE INTO ldb_attributes
+    (attr_name, case_insensitive_p, wildcard_p, hidden_p, integer_p)
+  VALUES ('uid', 1, 1, 0, 0);
+INSERT OR REPLACE INTO ldb_attributes
+    (attr_name, case_insensitive_p, wildcard_p, hidden_p, integer_p)
+  VALUES ('cn', 1, 0, 0, 0);
+INSERT OR REPLACE INTO ldb_attributes
+    (attr_name, case_insensitive_p, wildcard_p, hidden_p, integer_p)
+  VALUES ('ou', 1, 0, 0, 0);
+INSERT OR REPLACE INTO ldb_attributes
+    (attr_name, case_insensitive_p, wildcard_p, hidden_p, integer_p)
+  VALUES ('dn', 1, 0, 0, 0);
+
+-- ----------------------------------------------------------------------
+
+/*
+ * dn: @SUBCLASSES
+ * top: domain
+ * top: person
+ * domain: domainDNS
+ * person: organizationalPerson
+ * person: fooPerson
+ * organizationalPerson: user
+ * organizationalPerson: OpenLDAPperson
+ * user: computer
+ */
+-- insertSubclass
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'domain', /* next_tree_key('top') */ '00010001';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'person', /* next_tree_key('top') */ '00010002';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'domainDNS', /* next_tree_key('domain') */ '000100010001';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'organizationalPerson', /* next_tree_key('person') */ '000100020001';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'fooPerson', /* next_tree_key('person') */ '000100020002';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'user', /* next_tree_key('organizationalPerson') */ '0001000200010001';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'OpenLDAPperson', /* next_tree_key('organizationPerson') */ '0001000200010002';
+INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
+  SELECT 'computer', /* next_tree_key('user') */ '0001000200010001';