mirror of
https://github.com/samba-team/samba.git
synced 2025-11-11 00: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:
committed by
Gerald (Jerry) Carter
parent
0dda66f0e8
commit
a80bced0b9
@@ -72,7 +72,7 @@
|
|||||||
* @note You are explicitly allowed to pass NULL pointers -- they will
|
* @note You are explicitly allowed to pass NULL pointers -- they will
|
||||||
* always be ignored.
|
* 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
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ struct ldb_context *ldb_connect(const char *url, unsigned int flags,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if HAVE_SQLITE3
|
||||||
|
if (strncmp(url, "sqlite:", 7) == 0) {
|
||||||
|
ldb_ctx = lsqlite3_connect(url, flags, options);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
if (!ldb_ctx) {
|
if (!ldb_ctx) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
|
|||||||
@@ -88,3 +88,80 @@ int ldb_attr_cmp(const char *dn1, const char *dn2)
|
|||||||
{
|
{
|
||||||
return ldb_caseless_cmp(dn1, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ if test x"$with_ldap_support" = x"yes"; then
|
|||||||
SMB_MODULE_DEFAULT(libldb_ldap,STATIC)
|
SMB_MODULE_DEFAULT(libldb_ldap,STATIC)
|
||||||
fi
|
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)
|
SMB_LIBRARY_ENABLE(libldb,NO)
|
||||||
if test x"$experimental" = x"yes"; then
|
if test x"$experimental" = x"yes"; then
|
||||||
SMB_LIBRARY_ENABLE(libldb,YES)
|
SMB_LIBRARY_ENABLE(libldb,YES)
|
||||||
|
|||||||
@@ -28,6 +28,18 @@ NOPROTO = YES
|
|||||||
# End MODULE libldb_tdb
|
# 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
|
# Start MODULE libldb_tdb
|
||||||
[MODULE::libldb_tdb]
|
[MODULE::libldb_tdb]
|
||||||
|
|||||||
@@ -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_dn_cmp(const char *dn1, const char *dn2);
|
||||||
int ldb_attr_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 */
|
/* create an empty message */
|
||||||
struct ldb_message *ldb_msg_new(void *mem_ctx);
|
struct ldb_message *ldb_msg_new(void *mem_ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -37,15 +37,16 @@
|
|||||||
#include "ldb/include/ldb_private.h"
|
#include "ldb/include/ldb_private.h"
|
||||||
#include "ldb/ldb_sqlite3/ldb_sqlite3.h"
|
#include "ldb/ldb_sqlite3/ldb_sqlite3.h"
|
||||||
|
|
||||||
#undef SQL_EXEC /* just in case; not expected to be defined */
|
#define QUERY(lsqlite3, pppValues, pNumRows, bRollbackOnError, sql...) \
|
||||||
#define SQL_EXEC(lsqlite3, query, reset) \
|
do \
|
||||||
do { \
|
{ \
|
||||||
lsqlite3->last_rc = \
|
if (query(lsqlite3, pppValues, pNumRows, sql) != 0) { \
|
||||||
sqlite3_step(lsqlite3->queries.query); \
|
if (bRollbackOnError) { \
|
||||||
if (lsqlite3->last_rc == SQLITE_BUSY || reset) \
|
query(lsqlite3, NULL, NULL, "ROLLBACK;"); \
|
||||||
(void) sqlite3_reset(lsqlite3->queries.query); \
|
} \
|
||||||
} while lsqlite3->last_rc == SQLITE_BUSY;
|
return -1; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@@ -93,30 +94,10 @@ lsqlite3_rename(struct ldb_module *module,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bind old distinguished names */
|
#warning "rename() is not yet supported"
|
||||||
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;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* delete a record
|
* delete a record
|
||||||
*/
|
*/
|
||||||
@@ -133,21 +114,9 @@ lsqlite3_delete(struct ldb_module *module,
|
|||||||
return 0;
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Execute the query. This sets lsqlite3->last_rc */
|
|
||||||
SQL_EXEC(lsqlite3, deleteDN, TRUE);
|
|
||||||
|
|
||||||
return lsqlite3->last_rc == 0 ? 0 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* free a search result
|
* free a search result
|
||||||
*/
|
*/
|
||||||
@@ -332,9 +301,9 @@ failed:
|
|||||||
* requests in the ldb_message
|
* requests in the ldb_message
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
lsqlite3_msg_to_sql(struct ldb_context *ldb,
|
lsqlite3_msg_to_sql(struct ldb_module *module,
|
||||||
const struct ldb_message *msg,
|
const struct ldb_message *msg,
|
||||||
long long dn_id,
|
long long eid,
|
||||||
int use_flags)
|
int use_flags)
|
||||||
{
|
{
|
||||||
int flags;
|
int flags;
|
||||||
@@ -353,88 +322,75 @@ lsqlite3_msg_to_sql(struct ldb_context *ldb,
|
|||||||
flags = el->flags & LDB_FLAG_MOD_MASK;
|
flags = el->flags & LDB_FLAG_MOD_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine which query to use */
|
if (flags == LDB_FLAG_MOD_ADD) {
|
||||||
switch (flags) {
|
/* Create the attribute table if it doesn't exist */
|
||||||
case LDB_FLAG_MOD_ADD:
|
if (create_attr_table(module, el->name) != 0) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
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 each value of the specified attribute name... */
|
||||||
for (j = 0; j < el->num_values; j++) {
|
for (j = 0; j < el->num_values; j++) {
|
||||||
|
|
||||||
/* ... bind the attribute value, if necessary */
|
/* ... bind the attribute value, if necessary */
|
||||||
switch (flags) {
|
switch (flags) {
|
||||||
case LDB_FLAG_MOD_ADD:
|
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:
|
case LDB_FLAG_MOD_REPLACE:
|
||||||
/* Bind attribute value */
|
QUERY(lsqlite3,
|
||||||
column =
|
NULL, NULL,
|
||||||
sqlite3_bind_parameter_index(
|
FALSE,
|
||||||
stmt,
|
"UPDATE ldb_attr_%q "
|
||||||
":attr_value");
|
" SET attr_value = %Q "
|
||||||
if (sqlite3_bind_text(
|
" WHERE eid = %lld;",
|
||||||
stmt, column,
|
el->data, eid);
|
||||||
el->values[j].data,
|
QUERY(lsqlite3,
|
||||||
el->values[j].length,
|
NULL, NULL,
|
||||||
SQLITE_STATIC) != SQLITE_OK) {
|
FALSE,
|
||||||
|
"UPDATE ldb_entry "
|
||||||
return -1;
|
" SET entry_data = "
|
||||||
}
|
" mod_attr(entry_data, %Q, %Q) "
|
||||||
|
" WHERE eid = %lld;",
|
||||||
|
el->name, el->data, eid);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LDB_FLAG_MOD_DELETE:
|
case LDB_FLAG_MOD_DELETE:
|
||||||
/* No additional parameters to this query */
|
/* 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;
|
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
|
* add a record
|
||||||
*/
|
*/
|
||||||
@@ -450,6 +435,7 @@ lsqlite3_add(struct ldb_module *module,
|
|||||||
const struct ldb_message *msg)
|
const struct ldb_message *msg)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
long long eid;
|
||||||
struct ldb_context * ldb = module->ldb;
|
struct ldb_context * ldb = module->ldb;
|
||||||
struct lsqlite3_private * lsqlite3 = module->private_data;
|
struct lsqlite3_private * lsqlite3 = module->private_data;
|
||||||
|
|
||||||
@@ -459,42 +445,28 @@ lsqlite3_add(struct ldb_module *module,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Begin a transaction */
|
/* 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");
|
* Build any portions of the directory tree that don't exist. If the
|
||||||
if (sqlite3_bind_text(lsqlite3->queries.newDN, column,
|
* final component already exists, it's an error.
|
||||||
msg->dn, strlen(msg->dn),
|
*/
|
||||||
SQLITE_STATIC) != SQLITE_OK) {
|
if (lsqlite3_insert_dn_recursive(lsqlite3,
|
||||||
|
lsqlite3_normalize_dn(ldb, msg->dn),
|
||||||
|
&eid) != 0) {
|
||||||
|
QUERY(lsqlite3, NULL, NULL, FALSE, "ROLLBACK;");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add this new DN. This sets lsqlite3->last_rc */
|
/* Add attributes to this new entry */
|
||||||
SQL_EXEC(lsqlite3, newDN, TRUE);
|
if (lsqlite3_msg_to_sql(module, msg, eid, FALSE) != 0) {
|
||||||
|
QUERY(lsqlite3, NULL, NULL, FALSE, "ROLLBACK;");
|
||||||
if (lsqlite3->last_rc != SQLITE_DONE) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the id of the just-added DN */
|
/* Everything worked. Commit it! */
|
||||||
dn_id = sqlite3_last_insert_rowid(lsqlite3->sqlite3);
|
QUERY(lsqlite3, NULL, NULL, TRUE, "COMMIT;");
|
||||||
|
return 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -506,6 +478,8 @@ lsqlite3_modify(struct ldb_module *module,
|
|||||||
const struct ldb_message *msg)
|
const struct ldb_message *msg)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int numRows;
|
||||||
|
char ** ppValues;
|
||||||
struct ldb_context * ldb = module->ldb;
|
struct ldb_context * ldb = module->ldb;
|
||||||
struct lsqlite3_private * lsqlite3 = module->private_data;
|
struct lsqlite3_private * lsqlite3 = module->private_data;
|
||||||
|
|
||||||
@@ -515,47 +489,37 @@ lsqlite3_modify(struct ldb_module *module,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Begin a transaction */
|
/* Begin a transaction */
|
||||||
SQL_EXEC(lsqlite3, begin, TRUE);
|
QUERY(lsqlite3, NULL, NULL, FALSE, "BEGIN EXCLUSIVE;");
|
||||||
|
|
||||||
/* Get the dn_id for the specified DN */
|
/* Get the id of this DN. */
|
||||||
column =
|
QUERY(lsqlite3,
|
||||||
sqlite3_bind_parameter_index(
|
&ppValues,
|
||||||
lsqlite3->queries.getDNID,
|
&numRows,
|
||||||
":dn");
|
TRUE,
|
||||||
if (sqlite3_bind_text(lsqlite3->queries.getDNID,
|
"SELECT eid "
|
||||||
column,
|
" FROM ldb_entry "
|
||||||
msg->dn, strlen(msg->dn),
|
" WHERE dn = %Q;",
|
||||||
SQLITE_STATIC) != SQLITE_OK) {
|
lsqlite3_normalize_dn(ldb, msg->dn));
|
||||||
|
|
||||||
|
/* Did it exist? */
|
||||||
|
if (numRows != 1) {
|
||||||
|
/* Nope. See ya! */
|
||||||
|
sqlite_free_table(ppValues);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the id of this DN. This sets lsqlite3->last_rc */
|
/* Retrieve the eid */
|
||||||
SQL_EXEC(lsqlite3, getDNID, FALSE);
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dn_id = sqlite3_column_int64(lsqlite3->queries.getDNID,
|
/* Everything worked. Commit it! */
|
||||||
column);
|
QUERY(lsqlite3, NULL, NULL, TRUE, "COMMIT;");
|
||||||
(void) sqlite3_reset(lsqlite3->queries.getDNID);
|
return 0 ;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -570,15 +534,9 @@ lsqlite3_lock(struct ldb_module *module,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're already locked, just update lock count */
|
/* TODO implement a local locking mechanism here */
|
||||||
if (++lsqlite3->lock_count > 1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write-lock (but not read-lock) the database */
|
return 0;
|
||||||
SQL_EXEC(lsqlite3, begin, TRUE);
|
|
||||||
|
|
||||||
return lsqlite3->last_rc == 0 ? 0 : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -593,21 +551,11 @@ lsqlite3_unlock(struct ldb_module *module,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're not already locked, there's nothing to do */
|
/* TODO implement a local locking mechanism here */
|
||||||
if (lsqlite3->lock_count == 0) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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 extended error information
|
* return extended error information
|
||||||
*/
|
*/
|
||||||
@@ -647,6 +595,7 @@ static int
|
|||||||
lsqlite3_initialize(lsqlite3_private *lsqlite3,
|
lsqlite3_initialize(lsqlite3_private *lsqlite3,
|
||||||
const char *url)
|
const char *url)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
int bNewDatabase = False;
|
int bNewDatabase = False;
|
||||||
char * p;
|
char * p;
|
||||||
char * pTail;
|
char * pTail;
|
||||||
@@ -668,110 +617,124 @@ lsqlite3_initialize(lsqlite3_private *lsqlite3,
|
|||||||
SELECT 'LDB' AS database_type,
|
SELECT 'LDB' AS database_type,
|
||||||
'1.0' AS version;
|
'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,
|
-- Unique identifier of this LDB entry
|
||||||
dn TEXT UNIQUE
|
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
|
CREATE TABLE ldb_object_classes
|
||||||
(
|
(
|
||||||
|
-- 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,
|
class_name TEXT PRIMARY KEY,
|
||||||
tree_key TEXT,
|
|
||||||
max_child_num INTEGER
|
-- 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,
|
-- The unique identifier of the LDB entry
|
||||||
class_name TEXT REFERENCES ldb_object_classes
|
eid INTEGER REFERENCES ldb_entry,
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE ldb_attributes
|
-- Normalized attribute value
|
||||||
(
|
attr_value TEXT
|
||||||
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, -- 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
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
DELETE FROM ldb_attr_value_pairs
|
UPDATE ldb_entry
|
||||||
WHERE dn_id = old.dn_id;
|
SET create_timestamp = strftime('%s', 'now'),
|
||||||
DELETE FROM ldb_dn_object_classes
|
modify_timestamp = strftime('%s', 'now')
|
||||||
WHERE dn_id = old.dn_id;
|
WHERE eid = new.eid;
|
||||||
END;
|
END;
|
||||||
|
|
||||||
CREATE TRIGGER ldb_attr_value_pairs_insert_tr
|
CREATE TRIGGER ldb_entry_update_tr
|
||||||
BEFORE INSERT
|
AFTER UPDATE
|
||||||
ON ldb_attr_value_pairs
|
ON ldb_entry
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
INSERT OR IGNORE INTO ldb_attributes
|
UPDATE ldb_entry
|
||||||
(attr_name)
|
SET modify_timestamp = strftime('%s', 'now')
|
||||||
VALUES
|
WHERE eid = old.eid;
|
||||||
(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;
|
END;
|
||||||
|
|
||||||
-- ------------------------------------------------------
|
-- ------------------------------------------------------
|
||||||
|
-- Table initialization
|
||||||
|
|
||||||
CREATE INDEX ldb_distinguished_names_dn_idx
|
/* We need an implicit "top" level object class */
|
||||||
ON ldb_distinguished_names (dn);
|
INSERT INTO ldb_attributes (attr_name,
|
||||||
|
parent_tree_key)
|
||||||
CREATE INDEX ldb_object_classes_tree_key_idx
|
SELECT 'top', '';
|
||||||
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';
|
|
||||||
|
|
||||||
-- ------------------------------------------------------
|
-- ------------------------------------------------------
|
||||||
|
|
||||||
@@ -797,7 +760,7 @@ lsqlite3_initialize(lsqlite3_private *lsqlite3,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Try to open the (possibly empty/non-existent) database */
|
/* 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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -831,18 +794,15 @@ lsqlite3_initialize(lsqlite3_private *lsqlite3,
|
|||||||
" WHERE type = 'table' "
|
" WHERE type = 'table' "
|
||||||
" AND name IN "
|
" AND name IN "
|
||||||
" ("
|
" ("
|
||||||
" 'ldb_info', "
|
" 'ldb_entry', "
|
||||||
" 'ldb_distinguished_names', "
|
" 'ldb_descendants', "
|
||||||
" 'ldb_object_classes', "
|
" 'ldb_object_classes' "
|
||||||
" 'ldb_dn_object_classes', "
|
|
||||||
" 'ldb_attributes', "
|
|
||||||
" 'ldb_attr_value_pairs' "
|
|
||||||
" );",
|
" );",
|
||||||
-1,
|
-1,
|
||||||
&stmt,
|
&stmt,
|
||||||
&pTail)) != SQLITE_SUCCESS ||
|
&pTail)) != SQLITE_SUCCESS ||
|
||||||
(lsqlite3->last_rc = sqlite3_step(stmt)) != SQLITE_ROW ||
|
(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 = sqlite_finalize(stmt)) != SQLITE_SUCCESS ||
|
||||||
|
|
||||||
(lsqlite3->last_rc = sqlite3_prepare(
|
(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;
|
return SQLITE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,24 +5,6 @@ struct lsqlite3_private {
|
|||||||
const char *basedn;
|
const char *basedn;
|
||||||
sqlite3 * sqlite;
|
sqlite3 * sqlite;
|
||||||
int lock_count;
|
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
|
void
|
||||||
|
|||||||
@@ -12,158 +12,119 @@
|
|||||||
SELECT 'LDB' AS database_type,
|
SELECT 'LDB' AS database_type,
|
||||||
'1.0' AS version;
|
'1.0' AS version;
|
||||||
|
|
||||||
|
-- ------------------------------------------------------
|
||||||
|
-- Schema
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the next USN value with:
|
* The entry table holds the information about an entry. This
|
||||||
* BEGIN EXCLUSIVE;
|
* table is used to obtain the EID of the entry and to support
|
||||||
* UPDATE usn SET value = value + 1;
|
* scope="one" and scope="base". The parent and child table
|
||||||
* SELECT value FROM usn;
|
* is included in the entry table since all the other
|
||||||
* COMMIT;
|
* attributes on EID.
|
||||||
*/
|
*/
|
||||||
CREATE TABLE usn
|
CREATE TABLE ldb_entry
|
||||||
(
|
(
|
||||||
value INTEGER
|
-- Unique identifier of this LDB entry
|
||||||
);
|
eid INTEGER PRIMARY KEY,
|
||||||
|
|
||||||
CREATE TABLE ldb_object
|
-- Unique identifier of the parent LDB entry
|
||||||
(
|
peid INTEGER REFERENCES ldb_entry,
|
||||||
/* tree_key is auto-generated by the insert trigger */
|
|
||||||
tree_key TEXT PRIMARY KEY,
|
|
||||||
|
|
||||||
parent_tree_key TEXT,
|
-- Distinguished name of this entry
|
||||||
dn TEXT,
|
dn TEXT,
|
||||||
|
|
||||||
attr_name TEXT REFERENCES ldb_attributes,
|
-- Time when the entry was created
|
||||||
attr_value TEXT,
|
create_timestamp INTEGER,
|
||||||
|
|
||||||
/*
|
-- Time when the entry was last modified
|
||||||
* object_type can take on these values (to date):
|
modify_timestamp INTEGER,
|
||||||
* 1: object is a node of a DN
|
|
||||||
* 2: object is an attribute/value pair of its parent DN
|
|
||||||
*/
|
|
||||||
object_type INTEGER,
|
|
||||||
|
|
||||||
/*
|
-- Attributes of this entry, in the form
|
||||||
* if object_type is 1, the node can have children.
|
-- attr\1value\0[attr\1value\0]*\0
|
||||||
* this tracks the maximum previously assigned child
|
entry_data TEXT
|
||||||
* 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)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE ldb_attributes
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
(
|
(
|
||||||
attr_name TEXT PRIMARY KEY,
|
-- The unique identifier of the ancestor LDB entry
|
||||||
parent_tree_key TEXT,
|
aeid INTEGER REFERENCES ldb_entry,
|
||||||
|
|
||||||
objectclass_p BOOLEAN DEFAULT 0,
|
-- The unique identifier of the descendant LDB entry
|
||||||
|
deid INTEGER REFERENCES ldb_entry
|
||||||
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
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ------------------------------------------------------
|
|
||||||
|
|
||||||
CREATE INDEX ldb_object_dn_idx
|
CREATE TABLE ldb_object_classes
|
||||||
ON ldb_object (dn);
|
(
|
||||||
|
-- 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
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
CREATE INDEX ldb_attributes_tree_key_ids
|
|
||||||
ON ldb_attributes (tree_key);
|
|
||||||
|
|
||||||
-- ------------------------------------------------------
|
-- ------------------------------------------------------
|
||||||
|
-- Indexes
|
||||||
|
|
||||||
/* Gifts for metze. Automatically updated meta-data */
|
|
||||||
CREATE TRIGGER ldb_object_insert_tr
|
-- ------------------------------------------------------
|
||||||
|
-- Triggers
|
||||||
|
|
||||||
|
CREATE TRIGGER ldb_entry_insert_tr
|
||||||
AFTER INSERT
|
AFTER INSERT
|
||||||
ON ldb_object
|
ON ldb_entry
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE ldb_object
|
UPDATE ldb_entry
|
||||||
SET max_child_num = max_child_num + 1
|
SET create_timestamp = strftime('%s', 'now'),
|
||||||
WHERE tree_key = new.parent_tree_key;
|
modify_timestamp = strftime('%s', 'now')
|
||||||
UPDATE usn SET value = value + 1;
|
WHERE eid = new.eid;
|
||||||
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;
|
|
||||||
END;
|
END;
|
||||||
|
|
||||||
CREATE TRIGGER ldb_object_update_tr
|
CREATE TRIGGER ldb_entry_update_tr
|
||||||
AFTER UPDATE
|
AFTER UPDATE
|
||||||
ON ldb_object
|
ON ldb_entry
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE usn SET value = value + 1;
|
UPDATE ldb_entry
|
||||||
UPDATE ldb_object
|
SET modify_timestamp = strftime('%s', 'now')
|
||||||
SET timestamp = strftime('%s', 'now'),
|
WHERE eid = old.eid;
|
||||||
usn = (SELECT value FROM usn);
|
|
||||||
WHERE tree_key = new.tree_key;
|
|
||||||
END;
|
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;
|
|
||||||
|
|
||||||
|
|
||||||
-- ------------------------------------------------------
|
-- ------------------------------------------------------
|
||||||
|
-- Table initialization
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* We need an implicit "top" level object class */
|
/* We need an implicit "top" level object class */
|
||||||
INSERT INTO ldb_attributes (attr_name,
|
INSERT INTO ldb_attributes (attr_name,
|
||||||
|
|||||||
@@ -44,78 +44,12 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
casefold a dn. We need to uppercase the attribute names, and the
|
callback function used in call to ldb_dn_fold() for determining whether an
|
||||||
attribute values of case insensitive attributes. We also need to remove
|
attribute type requires case folding.
|
||||||
extraneous spaces between elements
|
|
||||||
*/
|
*/
|
||||||
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;
|
return ltdb_attribute_flags(module, attr) & LTDB_FLAG_CASE_INSENSITIVE;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -172,7 +106,7 @@ struct TDB_DATA ltdb_key(struct ldb_module *module, const char *dn)
|
|||||||
}
|
}
|
||||||
talloc_free(attr_name);
|
talloc_free(attr_name);
|
||||||
} else {
|
} else {
|
||||||
dn_folded = ltdb_dn_fold(module, dn);
|
dn_folded = ldb_dn_fold(module, dn, ltdb_case_fold_attr_required);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dn_folded) {
|
if (!dn_folded) {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ static void usage(void)
|
|||||||
printf(" -s base|sub|one choose search scope\n");
|
printf(" -s base|sub|one choose search scope\n");
|
||||||
printf(" -b basedn choose baseDN\n");
|
printf(" -b basedn choose baseDN\n");
|
||||||
printf(" -i read search expressions from stdin\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(" -o options pass options like modules to activate\n");
|
||||||
printf(" e.g: -o modules:timestamps\n");
|
printf(" e.g: -o modules:timestamps\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -56,6 +57,7 @@ static void usage(void)
|
|||||||
static int do_search(struct ldb_context *ldb,
|
static int do_search(struct ldb_context *ldb,
|
||||||
const char *basedn,
|
const char *basedn,
|
||||||
int scope,
|
int scope,
|
||||||
|
int sort_attribs,
|
||||||
const char *expression,
|
const char *expression,
|
||||||
const char * const *attrs)
|
const char * const *attrs)
|
||||||
{
|
{
|
||||||
@@ -77,6 +79,15 @@ static int do_search(struct ldb_context *ldb,
|
|||||||
ldif.changetype = LDB_CHANGETYPE_NONE;
|
ldif.changetype = LDB_CHANGETYPE_NONE;
|
||||||
ldif.msg = msgs[i];
|
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);
|
ldb_ldif_write_file(ldb, stdout, &ldif);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +111,7 @@ static int do_search(struct ldb_context *ldb,
|
|||||||
const char **options = NULL;
|
const char **options = NULL;
|
||||||
int opt, ldbopts;
|
int opt, ldbopts;
|
||||||
enum ldb_scope scope = LDB_SCOPE_SUBTREE;
|
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");
|
ldb_url = getenv("LDB_URL");
|
||||||
|
|
||||||
@@ -129,6 +140,10 @@ static int do_search(struct ldb_context *ldb,
|
|||||||
interactive = 1;
|
interactive = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
sort_attribs = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
options = ldb_options_parse(options, &ldbopts, optarg);
|
options = ldb_options_parse(options, &ldbopts, optarg);
|
||||||
break;
|
break;
|
||||||
@@ -168,12 +183,12 @@ static int do_search(struct ldb_context *ldb,
|
|||||||
if (interactive) {
|
if (interactive) {
|
||||||
char line[1024];
|
char line[1024];
|
||||||
while (fgets(line, sizeof(line), stdin)) {
|
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;
|
ret = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = do_search(ldb, basedn, scope, argv[0], attrs);
|
ret = do_search(ldb, basedn, scope, sort_attribs, argv[0], attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
talloc_free(ldb);
|
talloc_free(ldb);
|
||||||
|
|||||||
@@ -105,9 +105,18 @@
|
|||||||
#define MAP_FAILED ((void *)-1)
|
#define MAP_FAILED ((void *)-1)
|
||||||
#endif
|
#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 */
|
/* free memory if the pointer is valid and zero the pointer */
|
||||||
#ifndef SAFE_FREE
|
#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
|
#endif
|
||||||
|
|
||||||
#define BUCKET(hash) ((hash) % tdb->header.hash_size)
|
#define BUCKET(hash) ((hash) % tdb->header.hash_size)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SAFE_FREE
|
#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
|
#endif
|
||||||
|
|
||||||
#ifndef _WINBINDD_NTDOM_H
|
#ifndef _WINBINDD_NTDOM_H
|
||||||
|
|||||||
Reference in New Issue
Block a user