1
0
mirror of https://github.com/samba-team/samba.git synced 2025-11-09 20:23:51 +03:00

r7276: - moved static tdb function ltdb_dn_fold() into common/ so that it can be

called from multiple backends.  (ldb_sqlite3 needs it too.)  Added parameter
  for a callback function that determines whether an attribute needs case
  folding.
- begin to prepare for sqlite3 in build process
- work-in-progress updates, on ldb_sqlite3
This commit is contained in:
Derrell Lipman
2005-06-04 17:13:43 +00:00
committed by Gerald (Jerry) Carter
parent 0dda66f0e8
commit a80bced0b9
13 changed files with 466 additions and 645 deletions

View File

@@ -72,7 +72,7 @@
* @note You are explicitly allowed to pass NULL pointers -- they will
* always be ignored.
**/
#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
#define SAFE_FREE(x) do { if ((x) != NULL) {free(discard_const_p(void *, (x))); (x)=NULL;} } while(0)
#endif

View File

@@ -62,6 +62,12 @@ struct ldb_context *ldb_connect(const char *url, unsigned int flags,
}
#endif
#if HAVE_SQLITE3
if (strncmp(url, "sqlite:", 7) == 0) {
ldb_ctx = lsqlite3_connect(url, flags, options);
}
#endif
if (!ldb_ctx) {
errno = EINVAL;

View File

@@ -88,3 +88,80 @@ int ldb_attr_cmp(const char *dn1, const char *dn2)
{
return ldb_caseless_cmp(dn1, dn2);
}
/*
casefold a dn. We need to uppercase the attribute names, and the
attribute values of case insensitive attributes. We also need to remove
extraneous spaces between elements
*/
char *ldb_dn_fold(struct ldb_module *module, const char *dn, int (*case_fold_attr_fn)(struct ldb_module * module, char * attr))
{
const char *dn_orig = dn;
struct ldb_context *ldb = module->ldb;
TALLOC_CTX *tmp_ctx = talloc_new(ldb);
char *ret;
size_t len;
ret = talloc_strdup(tmp_ctx, "");
if (ret == NULL) goto failed;
while ((len = strcspn(dn, ",")) > 0) {
char *p = strchr(dn, '=');
char *attr, *value;
int case_fold_required;
if (p == NULL || (p-dn) > len) goto failed;
attr = talloc_strndup(tmp_ctx, dn, p-dn);
if (attr == NULL) goto failed;
/* trim spaces from the attribute name */
while (' ' == *attr) attr++;
while (' ' == attr[strlen(attr)-1]) {
attr[strlen(attr)-1] = 0;
}
if (*attr == 0) goto failed;
value = talloc_strndup(tmp_ctx, p+1, len-(p+1-dn));
if (value == NULL) goto failed;
/* trim spaces from the value */
while (' ' == *value) value++;
while (' ' == value[strlen(value)-1]) {
value[strlen(value)-1] = 0;
}
if (*value == 0) goto failed;
case_fold_required = (* case_fold_attr_fn)(module, attr);
attr = ldb_casefold(ldb, attr);
if (attr == NULL) goto failed;
talloc_steal(tmp_ctx, attr);
if (case_fold_required) {
value = ldb_casefold(ldb, value);
if (value == NULL) goto failed;
talloc_steal(tmp_ctx, value);
}
if (dn[len] == ',') {
ret = talloc_asprintf_append(ret, "%s=%s,", attr, value);
} else {
ret = talloc_asprintf_append(ret, "%s=%s", attr, value);
}
if (ret == NULL) goto failed;
dn += len;
if (*dn == ',') dn++;
}
talloc_steal(ldb, ret);
talloc_free(tmp_ctx);
return ret;
failed:
talloc_free(tmp_ctx);
return ldb_casefold(ldb, dn_orig);
}

View File

@@ -3,6 +3,11 @@ if test x"$with_ldap_support" = x"yes"; then
SMB_MODULE_DEFAULT(libldb_ldap,STATIC)
fi
SMB_MODULE_DEFAULT(libldb_sqlite3,NOT)
if test x"$with_sqlite3_support" = x"yes"; then
SMB_MODULE_DEFAULT(libldb_sqlite3,STATIC)
fi
SMB_LIBRARY_ENABLE(libldb,NO)
if test x"$experimental" = x"yes"; then
SMB_LIBRARY_ENABLE(libldb,YES)

View File

@@ -28,6 +28,18 @@ NOPROTO = YES
# End MODULE libldb_tdb
################################################
################################################
# Start MODULE libldb_sqlite3
[MODULE::libldb_sqlite3]
SUBSYSTEM = LIBLDB
INIT_OBJ_FILES = \
lib/ldb/ldb_sqlite3/ldb_sqlite3.o
REQUIRED_SUBSYSTEMS = \
EXT_LIB_SQLITE3
NOPROTO = YES
# End MODULE libldb_tdb
################################################
################################################
# Start MODULE libldb_tdb
[MODULE::libldb_tdb]

View File

@@ -225,6 +225,9 @@ int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif
int ldb_dn_cmp(const char *dn1, const char *dn2);
int ldb_attr_cmp(const char *dn1, const char *dn2);
/* case-fold a DN */
char *ldb_dn_fold(struct ldb_module *module, const char *dn, int (*case_fold_attr_fn)(struct ldb_module * module, char * attr));
/* create an empty message */
struct ldb_message *ldb_msg_new(void *mem_ctx);

View File

@@ -37,15 +37,16 @@
#include "ldb/include/ldb_private.h"
#include "ldb/ldb_sqlite3/ldb_sqlite3.h"
#undef SQL_EXEC /* just in case; not expected to be defined */
#define SQL_EXEC(lsqlite3, query, reset) \
do { \
lsqlite3->last_rc = \
sqlite3_step(lsqlite3->queries.query); \
if (lsqlite3->last_rc == SQLITE_BUSY || reset) \
(void) sqlite3_reset(lsqlite3->queries.query); \
} while lsqlite3->last_rc == SQLITE_BUSY;
#define QUERY(lsqlite3, pppValues, pNumRows, bRollbackOnError, sql...) \
do \
{ \
if (query(lsqlite3, pppValues, pNumRows, sql) != 0) { \
if (bRollbackOnError) { \
query(lsqlite3, NULL, NULL, "ROLLBACK;"); \
} \
return -1; \
} \
} while (0)
#if 0
@@ -93,28 +94,8 @@ lsqlite3_rename(struct ldb_module *module,
return 0;
}
/* Bind old distinguished names */
column = sqlite3_bind_parameter_index(lsqlite3->queries.renameDN,
":oldDN");
if (sqlite3_bind_text(lsqlite3->queries.renameDN, column,
olddn, strlen(olddn),
SQLITE_STATIC) != SQLITE_OK) {
return -1;
}
/* Bind new distinguished names */
column = sqlite3_bind_parameter_index(lsqlite3->queries.renameDN,
":newDN");
if (sqlite3_bind_text(lsqlite3->queries.renameDN, column,
newdn, strlen(newdn),
SQLITE_STATIC) != SQLITE_OK) {
return -1;
}
/* Execute the query. This sets lsqlite3->last_rc */
SQL_EXEC(lsqlite3, renameDN, TRUE);
return lsqlite3->last_rc == 0 ? 0 : -1;
#warning "rename() is not yet supported"
return -1;
}
/*
@@ -133,19 +114,7 @@ lsqlite3_delete(struct ldb_module *module,
return 0;
}
/* Bind distinguished names */
column = sqlite3_bind_parameter_index(lsqlite3->queries.deleteDN,
":dn");
if (sqlite3_bind_text(lsqlite3->queries.deleteDN, column,
dn, strlen(dn),
SQLITE_STATIC) != SQLITE_OK) {
return -1;
}
/* Execute the query. This sets lsqlite3->last_rc */
SQL_EXEC(lsqlite3, deleteDN, TRUE);
return lsqlite3->last_rc == 0 ? 0 : -1;
return -1;
}
/*
@@ -332,9 +301,9 @@ failed:
* requests in the ldb_message
*/
static int
lsqlite3_msg_to_sql(struct ldb_context *ldb,
lsqlite3_msg_to_sql(struct ldb_module *module,
const struct ldb_message *msg,
long long dn_id,
long long eid,
int use_flags)
{
int flags;
@@ -353,88 +322,75 @@ lsqlite3_msg_to_sql(struct ldb_context *ldb,
flags = el->flags & LDB_FLAG_MOD_MASK;
}
/* Determine which query to use */
switch (flags) {
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;
if (flags == LDB_FLAG_MOD_ADD) {
/* Create the attribute table if it doesn't exist */
if (create_attr_table(module, el->name) != 0) {
return -1;
}
}
/*
* All queries use dn id and attribute name. Bind them now.
*/
/* Bind distinguished name id */
column =
sqlite3_bind_parameter_index(
stmt,
":dn_id");
if (sqlite3_bind_int64(stmt,
column,
dn_id) != SQLITE_OK) {
return -1;
}
/* Bind attribute name */
column =
sqlite3_bind_parameter_index(
stmt,
":attr_name");
if (sqlite3_bind_text(lsqlite3->queries.deleteDN, column,
el->name, strlen(el->name),
SQLITE_STATIC) != SQLITE_OK) {
return -1;
}
/* For each value of the specified attribute name... */
for (j = 0; j < el->num_values; j++) {
/* ... bind the attribute value, if necessary */
switch (flags) {
case LDB_FLAG_MOD_ADD:
QUERY(lsqlite3,
NULL, NULL,
FALSE,
"INSERT INTO ldb_attr_%q "
" (eid, attr_value) "
" VALUES "
" (%lld, %Q);",
eid, el->data);
QUERY(lsqlite3,
NULL, NULL,
FALSE,
"UPDATE ldb_entry "
" SET entry_data = "
" add_attr(entry_data, %Q, %Q) "
" WHERE eid = %lld;",
el->name, el->data, eid);
break;
case LDB_FLAG_MOD_REPLACE:
/* Bind attribute value */
column =
sqlite3_bind_parameter_index(
stmt,
":attr_value");
if (sqlite3_bind_text(
stmt, column,
el->values[j].data,
el->values[j].length,
SQLITE_STATIC) != SQLITE_OK) {
return -1;
}
QUERY(lsqlite3,
NULL, NULL,
FALSE,
"UPDATE ldb_attr_%q "
" SET attr_value = %Q "
" WHERE eid = %lld;",
el->data, eid);
QUERY(lsqlite3,
NULL, NULL,
FALSE,
"UPDATE ldb_entry "
" SET entry_data = "
" mod_attr(entry_data, %Q, %Q) "
" WHERE eid = %lld;",
el->name, el->data, eid);
break;
case LDB_FLAG_MOD_DELETE:
/* No additional parameters to this query */
QUERY(lsqlite3,
NULL, NULL,
FALSE,
"DELETE FROM ldb_attr_%q "
" WHERE eid = %lld "
" AND attr_value = %Q;",
eid, el->data);
QUERY(lsqlite3,
NULL, NULL,
FALSE,
"UPDATE ldb_entry "
" SET entry_data = "
" del_attr(entry_data, %Q, %Q) "
" WHERE eid = %lld;",
el->name, el->data, eid);
break;
}
/* Execute the query */
do {
lsqlite3->last_rc = sqlite3_step(stmt);
(void) sqlite3_reset(stmt);
} while lsqlite3->last_rc == SQLITE_BUSY;
/* Make sure we succeeded */
if (lsqlite3->last_rc != SQLITE_OK) {
return -1;
}
}
}
@@ -442,6 +398,35 @@ lsqlite3_msg_to_sql(struct ldb_context *ldb,
}
static char *
lsqlite3_normalize_dn(struct ldb_context * ldb,
char * pDN)
{
char * pSrc;
char * pDest;
char * pNormalized;
pNormalized = talloc_size(ldb, strlen(pDN) + 1);
if (pNormalized == NULL) {
errno = ENOMEM;
return -1;
}
for (pSrc = pDN, pDest = pNormalized; *pSrc != '\0'; ) {
}
}
static int
lsqlite3_insert_dn_recursive(struct lsqlite3_private * lsqlite3,
char * pDN,
long long * pEID)
{
}
/*
* add a record
*/
@@ -450,6 +435,7 @@ lsqlite3_add(struct ldb_module *module,
const struct ldb_message *msg)
{
int ret;
long long eid;
struct ldb_context * ldb = module->ldb;
struct lsqlite3_private * lsqlite3 = module->private_data;
@@ -459,42 +445,28 @@ lsqlite3_add(struct ldb_module *module,
}
/* Begin a transaction */
SQL_EXEC(lsqlite3, begin, TRUE);
QUERY(lsqlite3, NULL, NULL< FALSE, "BEGIN EXCLUSIVE;");
/* 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) {
/*
* Build any portions of the directory tree that don't exist. If the
* final component already exists, it's an error.
*/
if (lsqlite3_insert_dn_recursive(lsqlite3,
lsqlite3_normalize_dn(ldb, msg->dn),
&eid) != 0) {
QUERY(lsqlite3, NULL, NULL, FALSE, "ROLLBACK;");
return -1;
}
/* Add this new DN. This sets lsqlite3->last_rc */
SQL_EXEC(lsqlite3, newDN, TRUE);
if (lsqlite3->last_rc != SQLITE_DONE) {
/* Add attributes to this new entry */
if (lsqlite3_msg_to_sql(module, msg, eid, FALSE) != 0) {
QUERY(lsqlite3, NULL, NULL, FALSE, "ROLLBACK;");
return -1;
}
/* Get the id of the just-added DN */
dn_id = sqlite3_last_insert_rowid(lsqlite3->sqlite3);
ret = lsqlite3_msg_to_sql(ldb, msg, dn_id, FALSE);
/* Did the attribute additions (if any) succeeded? */
if (ret == 0)
{
/* Yup. Commit the transaction */
SQL_EXEC(lsqlite3, commit, TRUE);
}
else
{
/* Attribute addition failed. Rollback the transaction */
SQL_EXEC(lsqlite3, rollback, TRUE);
}
/* If everything succeeded, return success */
return lsqlite3->last_rc == SQLITE_DONE && ret == 0 ? 0 : -1;
/* Everything worked. Commit it! */
QUERY(lsqlite3, NULL, NULL, TRUE, "COMMIT;");
return 0;
}
@@ -506,6 +478,8 @@ lsqlite3_modify(struct ldb_module *module,
const struct ldb_message *msg)
{
int ret = 0;
int numRows;
char ** ppValues;
struct ldb_context * ldb = module->ldb;
struct lsqlite3_private * lsqlite3 = module->private_data;
@@ -515,47 +489,37 @@ lsqlite3_modify(struct ldb_module *module,
}
/* Begin a transaction */
SQL_EXEC(lsqlite3, begin, TRUE);
QUERY(lsqlite3, NULL, NULL, FALSE, "BEGIN EXCLUSIVE;");
/* Get the dn_id for the specified DN */
column =
sqlite3_bind_parameter_index(
lsqlite3->queries.getDNID,
":dn");
if (sqlite3_bind_text(lsqlite3->queries.getDNID,
column,
msg->dn, strlen(msg->dn),
SQLITE_STATIC) != SQLITE_OK) {
/* Get the id of this DN. */
QUERY(lsqlite3,
&ppValues,
&numRows,
TRUE,
"SELECT eid "
" FROM ldb_entry "
" WHERE dn = %Q;",
lsqlite3_normalize_dn(ldb, msg->dn));
/* Did it exist? */
if (numRows != 1) {
/* Nope. See ya! */
sqlite_free_table(ppValues);
return -1;
}
/* Get the id of this DN. This sets lsqlite3->last_rc */
SQL_EXEC(lsqlite3, getDNID, FALSE);
/* Retrieve the eid */
eid = strtoll(ppValues[1], NULL, 10);
if (lsqlite3->last_rc != SQLITE_ROW) {
/* Modify attributes as specified */
if (lsqlite3_msg_to_sql(module, msg, eid, FALSE) != 0) {
QUERY(lsqlite3, NULL, NULL, FALSE, "ROLLBACK;");
return -1;
}
dn_id = sqlite3_column_int64(lsqlite3->queries.getDNID,
column);
(void) sqlite3_reset(lsqlite3->queries.getDNID);
ret = lsqlite3_msg_to_sql(ldb, msg, dn_id, FALSE);
/* Did the attribute additions (if any) succeeded? */
if (ret == 0)
{
/* Yup. Commit the transaction */
SQL_EXEC(lsqlite3, commit, TRUE);
}
else
{
/* Attribute addition failed. Rollback the transaction */
SQL_EXEC(lsqlite3, rollback, TRUE);
}
/* If everything succeeded, return success */
return lsqlite3->last_rc == SQLITE_DONE && ret == 0 ? 0 : -1;
/* Everything worked. Commit it! */
QUERY(lsqlite3, NULL, NULL, TRUE, "COMMIT;");
return 0 ;
}
static int
@@ -570,15 +534,9 @@ lsqlite3_lock(struct ldb_module *module,
return -1;
}
/* If we're already locked, just update lock count */
if (++lsqlite3->lock_count > 1) {
return -1;
}
/* TODO implement a local locking mechanism here */
/* Write-lock (but not read-lock) the database */
SQL_EXEC(lsqlite3, begin, TRUE);
return lsqlite3->last_rc == 0 ? 0 : -1;
return 0;
}
static int
@@ -593,19 +551,9 @@ lsqlite3_unlock(struct ldb_module *module,
return -1;
}
/* If we're not already locked, there's nothing to do */
if (lsqlite3->lock_count == 0) {
return 0;
}
/* TODO implement a local locking mechanism here */
/* Decrement lock count */
if (--lsqlite3->lock_count == 0) {
/* Final unlock. Unlock the database */
SQL_EXEC(lsqlite3, commit, TRUE);
}
return lsqlite3->last_rc == 0 ? 0 : -1;
return 0;
}
/*
@@ -647,6 +595,7 @@ static int
lsqlite3_initialize(lsqlite3_private *lsqlite3,
const char *url)
{
int ret;
int bNewDatabase = False;
char * p;
char * pTail;
@@ -668,110 +617,124 @@ lsqlite3_initialize(lsqlite3_private *lsqlite3,
SELECT 'LDB' AS database_type,
'1.0' AS version;
CREATE TABLE ldb_distinguished_names
-- ------------------------------------------------------
-- Schema
/*
* The entry table holds the information about an entry. This
* table is used to obtain the EID of the entry and to support
* scope=one and scope=base. The parent and child table
* is included in the entry table since all the other
* attributes on EID.
*/
CREATE TABLE ldb_entry
(
dn_id INTEGER PRIMARY KEY AUTOINCREMENT,
dn TEXT UNIQUE
-- Unique identifier of this LDB entry
eid INTEGER PRIMARY KEY,
-- Unique identifier of the parent LDB entry
peid INTEGER REFERENCES ldb_entry,
-- Distinguished name of this entry
dn TEXT,
-- Time when the entry was created
create_timestamp INTEGER,
-- Time when the entry was last modified
modify_timestamp INTEGER,
-- Attributes of this entry, in the form
-- attr\1value\0[attr\1value\0]*\0
entry_data TEXT
);
/*
* The purpose of the descendant table is to support the
* subtree search feature. For each LDB entry with a unique
* ID (AEID), this table contains the unique identifiers
* (DEID) of the descendant entries.
*
* For evern entry in the directory, a row exists in this
* table for each of its ancestors including itself. The size
* of the table depends on the depth of each entry. In the
* worst case, if all the entries were at the same depth, the
* number of rows in the table is O(nm) where n is the number
* of nodes in the directory and m is the depth of the tree.
*/
CREATE TABLE ldb_descendants
(
-- The unique identifier of the ancestor LDB entry
aeid INTEGER REFERENCES ldb_entry,
-- The unique identifier of the descendant LDB entry
deid INTEGER REFERENCES ldb_entry
);
CREATE TABLE ldb_object_classes
(
class_name TEXT PRIMARY KEY,
tree_key TEXT,
max_child_num INTEGER
-- Object classes are inserted into this table to track
-- their class hierarchy. 'top' is the top-level class
-- of which all other classes are subclasses.
class_name TEXT PRIMARY KEY,
-- tree_key tracks the position of the class in
-- the hierarchy
tree_key TEXT UNIQUE
);
CREATE TABLE ldb_dn_object_classes
/*
* There is one attribute table per searchable attribute.
*/
/*
CREATE TABLE ldb_attr_ATTRIBUTE_NAME
(
dn_id INTEGER REFERENCES ldb_distinguished_names,
class_name TEXT REFERENCES ldb_object_classes
);
-- The unique identifier of the LDB entry
eid INTEGER REFERENCES ldb_entry,
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
-- Normalized attribute value
attr_value TEXT
);
*/
CREATE TABLE ldb_attr_value_pairs
(
dn_id INTEGER REFERENCES ldb_distinguished_names,
attr_name TEXT, -- optionally REFERENCES ldb_attributes
attr_value TEXT,
UNIQUE (dn_id, attr_name, attr_value)
);
-- ------------------------------------------------------
-- Indexes
CREATE TRIGGER ldb_distinguished_names_delete_tr
AFTER DELETE
ON ldb_distinguished_names
-- ------------------------------------------------------
-- Triggers
CREATE TRIGGER ldb_entry_insert_tr
AFTER INSERT
ON ldb_entry
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;
UPDATE ldb_entry
SET create_timestamp = strftime('%s', 'now'),
modify_timestamp = strftime('%s', 'now')
WHERE eid = new.eid;
END;
CREATE TRIGGER ldb_attr_value_pairs_insert_tr
BEFORE INSERT
ON ldb_attr_value_pairs
CREATE TRIGGER ldb_entry_update_tr
AFTER UPDATE
ON ldb_entry
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;
UPDATE ldb_entry
SET modify_timestamp = strftime('%s', 'now')
WHERE eid = old.eid;
END;
-- ------------------------------------------------------
-- Table initialization
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';
/* We need an implicit "top" level object class */
INSERT INTO ldb_attributes (attr_name,
parent_tree_key)
SELECT 'top', '';
-- ------------------------------------------------------
@@ -797,7 +760,7 @@ lsqlite3_initialize(lsqlite3_private *lsqlite3,
}
/* Try to open the (possibly empty/non-existent) database */
if ((lsqlite3->last_rc = sqlite3_open(p, &lsqlite3->sqlite3)) != SQLITE_SUCCESS) {
if ((ret = sqlite3_open(p, &lsqlite3->sqlite3)) != SQLITE_SUCCESS) {
return ret;
}
@@ -831,18 +794,15 @@ lsqlite3_initialize(lsqlite3_private *lsqlite3,
" WHERE type = 'table' "
" AND name IN "
" ("
" 'ldb_info', "
" 'ldb_distinguished_names', "
" 'ldb_object_classes', "
" 'ldb_dn_object_classes', "
" 'ldb_attributes', "
" 'ldb_attr_value_pairs' "
" 'ldb_entry', "
" 'ldb_descendants', "
" 'ldb_object_classes' "
" );",
-1,
&stmt,
&pTail)) != SQLITE_SUCCESS ||
(lsqlite3->last_rc = sqlite3_step(stmt)) != SQLITE_ROW ||
sqlite3_column_int(stmt, 0) != 6 ||
sqlite3_column_int(stmt, 0) != 3 ||
(lsqlite3->last_rc = sqlite_finalize(stmt)) != SQLITE_SUCCESS ||
(lsqlite3->last_rc = sqlite3_prepare(
@@ -863,149 +823,6 @@ lsqlite3_initialize(lsqlite3_private *lsqlite3,
}
}
/*
* 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 ||
(lsqlite3->last_rc = sqlite3_prepare(
lsqlite3->sqlite3,
"SELECT dn_id "
" FROM ldb_distinguished_names "
" WHERE dn = :dn;"
-1,
&lsqlite3->queries.getDNID,
&pTail)) != SQLITE_SUCCESS) {
(void) sqlite3_close(lsqlite3->sqlite3);
return ret;
}
return SQLITE_SUCCESS;
}

View File

@@ -5,24 +5,6 @@ struct lsqlite3_private {
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;
sqlite3_stmt *getDNID;
} queries;
};
void

View File

@@ -12,158 +12,119 @@
SELECT 'LDB' AS database_type,
'1.0' AS version;
-- ------------------------------------------------------
-- Schema
/*
* Get the next USN value with:
* BEGIN EXCLUSIVE;
* UPDATE usn SET value = value + 1;
* SELECT value FROM usn;
* COMMIT;
* The entry table holds the information about an entry. This
* table is used to obtain the EID of the entry and to support
* scope="one" and scope="base". The parent and child table
* is included in the entry table since all the other
* attributes on EID.
*/
CREATE TABLE usn
CREATE TABLE ldb_entry
(
value INTEGER
-- Unique identifier of this LDB entry
eid INTEGER PRIMARY KEY,
-- Unique identifier of the parent LDB entry
peid INTEGER REFERENCES ldb_entry,
-- Distinguished name of this entry
dn TEXT,
-- Time when the entry was created
create_timestamp INTEGER,
-- Time when the entry was last modified
modify_timestamp INTEGER,
-- Attributes of this entry, in the form
-- attr\1value\0[attr\1value\0]*\0
entry_data TEXT
);
CREATE TABLE ldb_object
/*
* The purpose of the descendant table is to support the
* subtree search feature. For each LDB entry with a unique
* ID (AEID), this table contains the unique identifiers
* (DEID) of the descendant entries.
*
* For evern entry in the directory, a row exists in this
* table for each of its ancestors including itself. The size
* of the table depends on the depth of each entry. In the
* worst case, if all the entries were at the same depth, the
* number of rows in the table is O(nm) where n is the number
* of nodes in the directory and m is the depth of the tree.
*/
CREATE TABLE ldb_descendants
(
/* tree_key is auto-generated by the insert trigger */
tree_key TEXT PRIMARY KEY,
-- The unique identifier of the ancestor LDB entry
aeid INTEGER REFERENCES ldb_entry,
parent_tree_key TEXT,
dn TEXT,
attr_name TEXT REFERENCES ldb_attributes,
attr_value TEXT,
/*
* object_type can take on these values (to date):
* 1: object is a node of a DN
* 2: object is an attribute/value pair of its parent DN
*/
object_type INTEGER,
/*
* if object_type is 1, the node can have children.
* this tracks the maximum previously assigned child
* number so we can generate a new unique tree key for
* a new child object. note that this is always incremented,
* so if children are deleted, this will not represent
* the _number_ of children.
*/
max_child_num INTEGER,
/*
* Automatically maintained meta-data (a gift for metze)
*/
object_guid TEXT UNIQUE,
timestamp INTEGER, -- originating_time
invoke_id TEXT, -- GUID: originating_invocation_id
usn INTEGER, -- hyper: originating_usn
/* do not allow duplicate name/value pairs */
UNIQUE (parent_tree_key, attr_name, attr_value, object_type)
-- The unique identifier of the descendant LDB entry
deid INTEGER REFERENCES ldb_entry
);
CREATE TABLE ldb_attributes
CREATE TABLE ldb_object_classes
(
attr_name TEXT PRIMARY KEY,
parent_tree_key TEXT,
-- Object classes are inserted into this table to track
-- their class hierarchy. 'top' is the top-level class
-- of which all other classes are subclasses.
class_name TEXT PRIMARY KEY,
objectclass_p BOOLEAN DEFAULT 0,
case_insensitive_p BOOLEAN DEFAULT 0,
wildcard_p BOOLEAN DEFAULT 0,
hidden_p BOOLEAN DEFAULT 0,
integer_p BOOLEAN DEFAULT 0,
/* tree_key is auto-generated by the insert trigger */
tree_key TEXT, -- null if not a object/sub class
-- level 1 if an objectclass
-- level 1-n if a subclass
max_child_num INTEGER
-- tree_key tracks the position of the class in
-- the hierarchy
tree_key TEXT UNIQUE
);
/*
* There is one attribute table per searchable attribute.
*/
/*
CREATE TABLE ldb_attr_ATTRIBUTE_NAME
(
-- The unique identifier of the LDB entry
eid INTEGER REFERENCES ldb_entry,
-- Normalized attribute value
attr_value TEXT
);
*/
-- ------------------------------------------------------
-- Indexes
CREATE INDEX ldb_object_dn_idx
ON ldb_object (dn);
CREATE INDEX ldb_attributes_tree_key_ids
ON ldb_attributes (tree_key);
-- ------------------------------------------------------
-- Triggers
/* Gifts for metze. Automatically updated meta-data */
CREATE TRIGGER ldb_object_insert_tr
CREATE TRIGGER ldb_entry_insert_tr
AFTER INSERT
ON ldb_object
ON ldb_entry
FOR EACH ROW
BEGIN
UPDATE ldb_object
SET max_child_num = max_child_num + 1
WHERE tree_key = new.parent_tree_key;
UPDATE usn SET value = value + 1;
UPDATE ldb_object
SET tree_key =
(SELECT
new.tree_key ||
base160(SELECT max_child_num
FROM ldb_object
WHERE tree_key =
new.parent_tree_key));
max_child_num = 0,
object_guid = random_guid(),
timestamp = strftime('%s', 'now'),
usn = (SELECT value FROM usn);
WHERE tree_key = new.tree_key;
UPDATE ldb_entry
SET create_timestamp = strftime('%s', 'now'),
modify_timestamp = strftime('%s', 'now')
WHERE eid = new.eid;
END;
CREATE TRIGGER ldb_object_update_tr
CREATE TRIGGER ldb_entry_update_tr
AFTER UPDATE
ON ldb_object
ON ldb_entry
FOR EACH ROW
BEGIN
UPDATE usn SET value = value + 1;
UPDATE ldb_object
SET timestamp = strftime('%s', 'now'),
usn = (SELECT value FROM usn);
WHERE tree_key = new.tree_key;
UPDATE ldb_entry
SET modify_timestamp = strftime('%s', 'now')
WHERE eid = old.eid;
END;
CREATE TRIGGER ldb_attributes_insert_tr
AFTER INSERT
ON ldb_attributes
FOR EACH ROW
BEGIN
UPDATE ldb_attributes
SET max_child_num = max_child_num + 1
WHERE tree_key = new.parent_tree_key;
UPDATE ldb_attributes
SET tree_key =
(SELECT
new.tree_key ||
base160(SELECT max_child_num
FROM ldb_attributes
WHERE tree_key =
new.parent_tree_key));
max_child_num = 0
WHERE tree_key = new.tree_key;
END;
-- ------------------------------------------------------
/* Initialize usn */
INSERT INTO usn (value) VALUES (0);
/* Create root object */
INSERT INTO ldb_object
(tree_key, parent_tree_key,
dn,
object_type, max_child_num)
VALUES ('', NULL,
'',
1, 0);
-- Table initialization
/* We need an implicit "top" level object class */
INSERT INTO ldb_attributes (attr_name,

View File

@@ -44,78 +44,12 @@
/*
casefold a dn. We need to uppercase the attribute names, and the
attribute values of case insensitive attributes. We also need to remove
extraneous spaces between elements
callback function used in call to ldb_dn_fold() for determining whether an
attribute type requires case folding.
*/
static char *ltdb_dn_fold(struct ldb_module *module, const char *dn)
static int ltdb_case_fold_attr_required(struct ldb_module *module, char *attr)
{
const char *dn_orig = dn;
struct ldb_context *ldb = module->ldb;
TALLOC_CTX *tmp_ctx = talloc_new(ldb);
char *ret;
size_t len;
ret = talloc_strdup(tmp_ctx, "");
if (ret == NULL) goto failed;
while ((len = strcspn(dn, ",")) > 0) {
char *p = strchr(dn, '=');
char *attr, *value;
int flags;
if (p == NULL || (p-dn) > len) goto failed;
attr = talloc_strndup(tmp_ctx, dn, p-dn);
if (attr == NULL) goto failed;
/* trim spaces from the attribute name */
while (' ' == *attr) attr++;
while (' ' == attr[strlen(attr)-1]) {
attr[strlen(attr)-1] = 0;
}
if (*attr == 0) goto failed;
value = talloc_strndup(tmp_ctx, p+1, len-(p+1-dn));
if (value == NULL) goto failed;
/* trim spaces from the value */
while (' ' == *value) value++;
while (' ' == value[strlen(value)-1]) {
value[strlen(value)-1] = 0;
}
if (*value == 0) goto failed;
flags = ltdb_attribute_flags(module, attr);
attr = ldb_casefold(ldb, attr);
if (attr == NULL) goto failed;
talloc_steal(tmp_ctx, attr);
if (flags & LTDB_FLAG_CASE_INSENSITIVE) {
value = ldb_casefold(ldb, value);
if (value == NULL) goto failed;
talloc_steal(tmp_ctx, value);
}
if (dn[len] == ',') {
ret = talloc_asprintf_append(ret, "%s=%s,", attr, value);
} else {
ret = talloc_asprintf_append(ret, "%s=%s", attr, value);
}
if (ret == NULL) goto failed;
dn += len;
if (*dn == ',') dn++;
}
talloc_steal(ldb, ret);
talloc_free(tmp_ctx);
return ret;
failed:
talloc_free(tmp_ctx);
return ldb_casefold(ldb, dn_orig);
return ltdb_attribute_flags(module, attr) & LTDB_FLAG_CASE_INSENSITIVE;
}
/*
@@ -172,7 +106,7 @@ struct TDB_DATA ltdb_key(struct ldb_module *module, const char *dn)
}
talloc_free(attr_name);
} else {
dn_folded = ltdb_dn_fold(module, dn);
dn_folded = ldb_dn_fold(module, dn, ltdb_case_fold_attr_required);
}
if (!dn_folded) {

View File

@@ -48,6 +48,7 @@ static void usage(void)
printf(" -s base|sub|one choose search scope\n");
printf(" -b basedn choose baseDN\n");
printf(" -i read search expressions from stdin\n");
printf(" -S sort returned attributes\n");
printf(" -o options pass options like modules to activate\n");
printf(" e.g: -o modules:timestamps\n");
exit(1);
@@ -56,6 +57,7 @@ static void usage(void)
static int do_search(struct ldb_context *ldb,
const char *basedn,
int scope,
int sort_attribs,
const char *expression,
const char * const *attrs)
{
@@ -77,6 +79,15 @@ static int do_search(struct ldb_context *ldb,
ldif.changetype = LDB_CHANGETYPE_NONE;
ldif.msg = msgs[i];
if (sort_attribs) {
/*
* Ensure attributes are always returned in the same
* order. For testing, this makes comparison of old
* vs. new much easier.
*/
ldb_msg_sort_elements(ldif.msg);
}
ldb_ldif_write_file(ldb, stdout, &ldif);
}
@@ -100,7 +111,7 @@ static int do_search(struct ldb_context *ldb,
const char **options = NULL;
int opt, ldbopts;
enum ldb_scope scope = LDB_SCOPE_SUBTREE;
int interactive = 0, ret=0;
int interactive = 0, sort_attribs=0, ret=0;
ldb_url = getenv("LDB_URL");
@@ -129,6 +140,10 @@ static int do_search(struct ldb_context *ldb,
interactive = 1;
break;
case 'S':
sort_attribs = 1;
break;
case 'o':
options = ldb_options_parse(options, &ldbopts, optarg);
break;
@@ -168,12 +183,12 @@ static int do_search(struct ldb_context *ldb,
if (interactive) {
char line[1024];
while (fgets(line, sizeof(line), stdin)) {
if (do_search(ldb, basedn, scope, line, attrs) == -1) {
if (do_search(ldb, basedn, scope, sort_attribs, line, attrs) == -1) {
ret = -1;
}
}
} else {
ret = do_search(ldb, basedn, scope, argv[0], attrs);
ret = do_search(ldb, basedn, scope, sort_attribs, argv[0], attrs);
}
talloc_free(ldb);

View File

@@ -105,9 +105,18 @@
#define MAP_FAILED ((void *)-1)
#endif
#ifndef discard_const_p
# if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
# define discard_const(ptr) ((void *)((intptr_t)(ptr)))
# else
# define discard_const(ptr) ((void *)(ptr))
# endif
# define discard_const_p(type, ptr) ((type *)discard_const(ptr))
#endif
/* free memory if the pointer is valid and zero the pointer */
#ifndef SAFE_FREE
#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0)
#define SAFE_FREE(x) do { if ((x) != NULL) {free(discard_const_p(void *, (x))); (x)=NULL;} } while(0)
#endif
#define BUCKET(hash) ((hash) % tdb->header.hash_size)

View File

@@ -22,7 +22,7 @@
*/
#ifndef SAFE_FREE
#define SAFE_FREE(x) do { if(x) {free(x); x=NULL;} } while(0)
#define SAFE_FREE(x) do { if(x) {free(discard_const_p(void *, (x)); x=NULL;} } while(0)
#endif
#ifndef _WINBINDD_NTDOM_H