ancestorid.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #ifdef HAVE_CONFIG_H
  39. # include <config.h>
  40. #endif
  41. #include "back-ldbm.h"
  42. static char *sourcefile = "ancestorid";
  43. /* Start of definitions for a simple cache using a hash table */
  44. typedef struct id2idl {
  45. ID keyid;
  46. IDList *idl;
  47. struct id2idl *next;
  48. } id2idl;
  49. static void id2idl_free(id2idl **ididl);
  50. static int id2idl_same_key(const void *ididl, const void *k);
  51. typedef Hashtable id2idl_hash;
  52. #define id2idl_new_hash(size) new_hash(size,HASHLOC(id2idl,next),NULL,id2idl_same_key)
  53. #define id2idl_hash_lookup(ht,key,he) find_hash(ht,key,sizeof(ID),(void**)(he))
  54. #define id2idl_hash_add(ht,key,he,alt) add_hash(ht,key,sizeof(ID),he,(void**)(alt))
  55. #define id2idl_hash_remove(ht,key) remove_hash(ht,key,sizeof(ID))
  56. static void id2idl_hash_destroy(id2idl_hash *ht);
  57. /* End of definitions for a simple cache using a hash table */
  58. static int ldbm_parentid(backend *be, DB_TXN *txn, ID id, ID *ppid);
  59. static int check_cache(id2idl_hash *ht);
  60. static IDList *idl_union_allids(backend *be, struct attrinfo *ai, IDList *a, IDList *b);
  61. static int ldbm_ancestorid_default_create_index(backend *be);
  62. static int ldbm_ancestorid_new_idl_create_index(backend *be);
  63. static int ldbm_get_nonleaf_ids(backend *be, DB_TXN *txn, IDList **idl)
  64. {
  65. int ret = 0;
  66. DB *db = NULL;
  67. DBC *dbc = NULL;
  68. DBT key = {0};
  69. DBT data = {0};
  70. struct attrinfo *ai = NULL;
  71. IDList *nodes = NULL;
  72. ID id;
  73. /* Open the parentid index */
  74. ainfo_get( be, "parentid", &ai );
  75. /* Open the parentid index file */
  76. ret = dblayer_get_index_file(be, ai, &db, DBOPEN_CREATE);
  77. if (ret != 0) {
  78. ldbm_nasty(sourcefile,13010,ret);
  79. goto out;
  80. }
  81. /* Get a cursor so we can walk through the parentid */
  82. ret = db->cursor(db,txn,&dbc,0);
  83. if (ret != 0 ) {
  84. ldbm_nasty(sourcefile,13020,ret);
  85. goto out;
  86. }
  87. /* For each key which is an equality key */
  88. do {
  89. ret = dbc->c_get(dbc,&key,&data,DB_NEXT_NODUP);
  90. if ((ret == 0) && (*(char*)key.data == EQ_PREFIX)) {
  91. id = (ID) strtoul((char*)key.data+1, NULL, 10);
  92. idl_insert(&nodes, id);
  93. }
  94. } while (ret == 0);
  95. /* Check for success */
  96. if (ret == DB_NOTFOUND) ret = 0;
  97. if (ret != 0) ldbm_nasty(sourcefile,13030,ret);
  98. out:
  99. /* Close the cursor */
  100. if (dbc != NULL) {
  101. if (ret == 0) {
  102. ret = dbc->c_close(dbc);
  103. if (ret != 0) ldbm_nasty(sourcefile,13040,ret);
  104. } else {
  105. (void)dbc->c_close(dbc);
  106. }
  107. }
  108. /* Release the parentid file */
  109. if (db != NULL) {
  110. dblayer_release_index_file( be, ai, db );
  111. }
  112. /* Return the idlist */
  113. if (ret == 0) {
  114. *idl = nodes;
  115. LDAPDebug(LDAP_DEBUG_TRACE, "found %lu nodes for ancestorid\n",
  116. (u_long)IDL_NIDS(nodes), 0, 0);
  117. } else {
  118. idl_free(nodes);
  119. *idl = NULL;
  120. }
  121. return ret;
  122. }
  123. /*
  124. * XXX: This function creates ancestorid index, which is a sort of hack.
  125. * This function handles idl directly,
  126. * which should have been implemented in the idl file(s).
  127. * When the idl code would be updated in the future,
  128. * this function may also get affected.
  129. * (see also bug#: 605535)
  130. *
  131. * Construct the ancestorid index. Requirements:
  132. * - The backend is read only.
  133. * - The parentid index is accurate.
  134. * - Non-leaf entries have IDs less than their descendants
  135. * (guaranteed after a database import but not after a subtree move)
  136. *
  137. */
  138. int ldbm_ancestorid_create_index(backend *be)
  139. {
  140. return (idl_get_idl_new()) ?
  141. ldbm_ancestorid_new_idl_create_index(be) :
  142. ldbm_ancestorid_default_create_index(be);
  143. }
  144. /*
  145. * Create the ancestorid index. This version is safe to
  146. * use whichever IDL mode is active. However, it may be
  147. * quite a bit slower than ldbm_ancestorid_new_idl_create_index()
  148. * when the new mode is used, particularly with large databases.
  149. */
  150. static int ldbm_ancestorid_default_create_index(backend *be)
  151. {
  152. int ret = 0;
  153. DB *db_pid = NULL;
  154. DB *db_aid = NULL;
  155. DBT key = {0};
  156. DB_TXN *txn = NULL;
  157. struct attrinfo *ai_pid = NULL;
  158. struct attrinfo *ai_aid = NULL;
  159. char keybuf[24];
  160. IDList *nodes = NULL;
  161. IDList *children = NULL, *descendants = NULL;
  162. NIDS nids;
  163. ID id, parentid;
  164. id2idl_hash *ht = NULL;
  165. id2idl *ididl;
  166. /*
  167. * We need to iterate depth-first through the non-leaf nodes
  168. * in the tree amassing an idlist of descendant ids for each node.
  169. * We would prefer to go through the parentid keys just once from
  170. * highest id to lowest id but the btree ordering is by string
  171. * rather than number. So we go through the parentid keys in btree
  172. * order first of all to create an idlist of all the non-leaf nodes.
  173. * Then we can use the idlist to iterate through parentid in the
  174. * correct order.
  175. */
  176. LDAPDebug(LDAP_DEBUG_TRACE, "Creating ancestorid index\n", 0,0,0);
  177. /* Get the non-leaf node IDs */
  178. ret = ldbm_get_nonleaf_ids(be, txn, &nodes);
  179. if (ret != 0) return ret;
  180. /* Get the ancestorid index */
  181. ainfo_get(be, "ancestorid", &ai_aid);
  182. /* Prevent any other use of the index */
  183. ai_aid->ai_indexmask |= INDEX_OFFLINE;
  184. /* Open the ancestorid index file */
  185. ret = dblayer_get_index_file(be, ai_aid, &db_aid, DBOPEN_CREATE);
  186. if (ret != 0) {
  187. ldbm_nasty(sourcefile,13050,ret);
  188. goto out;
  189. }
  190. /* Maybe nothing to do */
  191. if (nodes == NULL || nodes->b_nids == 0) {
  192. LDAPDebug(LDAP_DEBUG_ANY, "Nothing to do to build ancestorid index\n",
  193. 0, 0, 0);
  194. goto out;
  195. }
  196. /* Create an ancestorid cache */
  197. ht = id2idl_new_hash(nodes->b_nids);
  198. /* Get the parentid index */
  199. ainfo_get( be, "parentid", &ai_pid );
  200. /* Open the parentid index file */
  201. ret = dblayer_get_index_file(be, ai_pid, &db_pid, DBOPEN_CREATE);
  202. if (ret != 0) {
  203. ldbm_nasty(sourcefile,13060,ret);
  204. goto out;
  205. }
  206. /* Initialize key DBT */
  207. key.data = keybuf;
  208. key.ulen = sizeof(keybuf);
  209. key.flags = DB_DBT_USERMEM;
  210. /* Iterate from highest to lowest ID */
  211. nids = nodes->b_nids;
  212. do {
  213. nids--;
  214. id = nodes->b_ids[nids];
  215. /* Get immediate children from parentid index */
  216. key.size = PR_snprintf(key.data, key.ulen, "%c%lu",
  217. EQ_PREFIX, (u_long)id);
  218. key.size++; /* include the null terminator */
  219. ret = NEW_IDL_NO_ALLID;
  220. children = idl_fetch(be, db_pid, &key, txn, ai_pid, &ret);
  221. if (ret != 0) {
  222. ldbm_nasty(sourcefile,13070,ret);
  223. break;
  224. }
  225. /* Insert into ancestorid for this node */
  226. if (id2idl_hash_lookup(ht, &id, &ididl)) {
  227. descendants = idl_union_allids(be, ai_aid, ididl->idl, children);
  228. idl_free(children);
  229. if (id2idl_hash_remove(ht, &id) == 0) {
  230. LDAPDebug(LDAP_DEBUG_ANY, "ancestorid hash_remove failed\n", 0,0,0);
  231. } else {
  232. id2idl_free(&ididl);
  233. }
  234. } else {
  235. descendants = children;
  236. }
  237. ret = idl_store_block(be, db_aid, &key, descendants, txn, ai_aid);
  238. if (ret != 0) break;
  239. /* Get parentid for this entry */
  240. ret = ldbm_parentid(be, txn, id, &parentid);
  241. if (ret != 0) {
  242. idl_free(descendants);
  243. break;
  244. }
  245. /* A suffix entry does not have a parent */
  246. if (parentid == NOID) {
  247. idl_free(descendants);
  248. continue;
  249. }
  250. /* Insert into ancestorid for this node's parent */
  251. if (id2idl_hash_lookup(ht, &parentid, &ididl)) {
  252. IDList *idl = idl_union_allids(be, ai_aid, ididl->idl, descendants);
  253. idl_free(descendants);
  254. idl_free(ididl->idl);
  255. ididl->idl = idl;
  256. } else {
  257. ididl = (id2idl*)slapi_ch_calloc(1,sizeof(id2idl));
  258. ididl->keyid = parentid;
  259. ididl->idl = descendants;
  260. if (id2idl_hash_add(ht, &parentid, ididl, NULL) == 0) {
  261. LDAPDebug(LDAP_DEBUG_ANY, "ancestorid hash_add failed\n", 0,0,0);
  262. }
  263. }
  264. } while (nids > 0);
  265. if (ret != 0) {
  266. goto out;
  267. }
  268. /* We're expecting the cache to be empty */
  269. ret = check_cache(ht);
  270. out:
  271. if (ret == 0) {
  272. LDAPDebug(LDAP_DEBUG_TRACE, "Created ancestorid index\n", 0,0,0);
  273. } else {
  274. LDAPDebug(LDAP_DEBUG_ANY, "Failed to create ancestorid index\n", 0,0,0);
  275. }
  276. /* Destroy the cache */
  277. id2idl_hash_destroy(ht);
  278. /* Free any leftover idlists */
  279. idl_free(nodes);
  280. /* Release the parentid file */
  281. if (db_pid != NULL) {
  282. dblayer_release_index_file( be, ai_pid, db_pid );
  283. }
  284. /* Release the ancestorid file */
  285. if (db_aid != NULL) {
  286. dblayer_release_index_file( be, ai_aid, db_aid );
  287. }
  288. /* Enable the index */
  289. if (ret == 0) {
  290. ai_aid->ai_indexmask &= ~INDEX_OFFLINE;
  291. }
  292. return ret;
  293. }
  294. /*
  295. * Create the ancestorid index. This version expects to use
  296. * idl_new_store_block() and should be used when idl_new != 0.
  297. * It has lower overhead and can be faster than
  298. * ldbm_ancestorid_default_create_index(), particularly on
  299. * large databases. Cf. bug 469800.
  300. */
  301. static int ldbm_ancestorid_new_idl_create_index(backend *be)
  302. {
  303. int ret = 0;
  304. DB *db_pid = NULL;
  305. DB *db_aid = NULL;
  306. DBT key = {0};
  307. DB_TXN *txn = NULL;
  308. struct attrinfo *ai_pid = NULL;
  309. struct attrinfo *ai_aid = NULL;
  310. char keybuf[24];
  311. IDList *nodes = NULL;
  312. IDList *children = NULL;
  313. NIDS nids;
  314. ID id, parentid;
  315. /*
  316. * We need to iterate depth-first through the non-leaf nodes
  317. * in the tree amassing an idlist of descendant ids for each node.
  318. * We would prefer to go through the parentid keys just once from
  319. * highest id to lowest id but the btree ordering is by string
  320. * rather than number. So we go through the parentid keys in btree
  321. * order first of all to create an idlist of all the non-leaf nodes.
  322. * Then we can use the idlist to iterate through parentid in the
  323. * correct order.
  324. */
  325. LDAPDebug(LDAP_DEBUG_TRACE, "Creating ancestorid index\n", 0,0,0);
  326. /* Bail now if we did not get here honestly. */
  327. if (!idl_get_idl_new()) {
  328. LDAPDebug(LDAP_DEBUG_ANY, "Cannot create ancestorid index. "
  329. "New IDL version called but idl_new is false!\n", 0,0,0);
  330. return 1;
  331. }
  332. /* Get the non-leaf node IDs */
  333. ret = ldbm_get_nonleaf_ids(be, txn, &nodes);
  334. if (ret != 0) return ret;
  335. /* Get the ancestorid index */
  336. ainfo_get(be, "ancestorid", &ai_aid);
  337. /* Prevent any other use of the index */
  338. ai_aid->ai_indexmask |= INDEX_OFFLINE;
  339. /* Open the ancestorid index file */
  340. ret = dblayer_get_index_file(be, ai_aid, &db_aid, DBOPEN_CREATE);
  341. if (ret != 0) {
  342. ldbm_nasty(sourcefile,13050,ret);
  343. goto out;
  344. }
  345. /* Maybe nothing to do */
  346. if (nodes == NULL || nodes->b_nids == 0) {
  347. LDAPDebug(LDAP_DEBUG_ANY, "Nothing to do to build ancestorid index\n",
  348. 0, 0, 0);
  349. goto out;
  350. }
  351. /* Get the parentid index */
  352. ainfo_get( be, "parentid", &ai_pid );
  353. /* Open the parentid index file */
  354. ret = dblayer_get_index_file(be, ai_pid, &db_pid, DBOPEN_CREATE);
  355. if (ret != 0) {
  356. ldbm_nasty(sourcefile,13060,ret);
  357. goto out;
  358. }
  359. /* Initialize key DBT */
  360. key.data = keybuf;
  361. key.ulen = sizeof(keybuf);
  362. key.flags = DB_DBT_USERMEM;
  363. /* Iterate from highest to lowest ID */
  364. nids = nodes->b_nids;
  365. do {
  366. nids--;
  367. id = nodes->b_ids[nids];
  368. /* Get immediate children from parentid index */
  369. key.size = PR_snprintf(key.data, key.ulen, "%c%lu",
  370. EQ_PREFIX, (u_long)id);
  371. key.size++; /* include the null terminator */
  372. ret = NEW_IDL_NO_ALLID;
  373. children = idl_fetch(be, db_pid, &key, txn, ai_pid, &ret);
  374. if (ret != 0) {
  375. ldbm_nasty(sourcefile,13070,ret);
  376. break;
  377. }
  378. /* Instead of maintaining a full accounting of IDs in a hashtable
  379. * as is done with ldbm_ancestorid_default_create_index(), perform
  380. * incremental updates straight to the DB with idl_new_store_block()
  381. * (used by idl_store_block() when idl_get_idl_new() is true). This
  382. * can be a significant performance improvement with large databases,
  383. * where the overhead of maintaining and copying the lists is very
  384. * expensive, particularly when the allids threshold is not being
  385. * used to provide any cut off. Cf. bug 469800.
  386. * TEL 20081029 */
  387. /* Insert into ancestorid for this node */
  388. ret = idl_store_block(be, db_aid, &key, children, txn, ai_aid);
  389. if (ret != 0) {
  390. idl_free(children);
  391. break;
  392. }
  393. /* Get parentid(s) for this entry */
  394. while (1) {
  395. ret = ldbm_parentid(be, txn, id, &parentid);
  396. if (ret != 0) {
  397. idl_free(children);
  398. goto out;
  399. }
  400. /* A suffix entry does not have a parent */
  401. if (parentid == NOID) {
  402. idl_free(children);
  403. break;
  404. }
  405. /* Reset the key to the parent id */
  406. key.size = PR_snprintf(key.data, key.ulen, "%c%lu",
  407. EQ_PREFIX, (u_long)parentid);
  408. key.size++;
  409. /* Insert into ancestorid for this node's parent */
  410. ret = idl_store_block(be, db_aid, &key, children, txn, ai_aid);
  411. if (ret != 0) {
  412. idl_free(children);
  413. goto out;
  414. }
  415. id = parentid;
  416. }
  417. } while (nids > 0);
  418. if (ret != 0) {
  419. goto out;
  420. }
  421. out:
  422. if (ret == 0) {
  423. LDAPDebug(LDAP_DEBUG_TRACE, "Created ancestorid index\n", 0,0,0);
  424. } else {
  425. LDAPDebug(LDAP_DEBUG_ANY, "Failed to create ancestorid index\n", 0,0,0);
  426. }
  427. /* Free any leftover idlists */
  428. idl_free(nodes);
  429. /* Release the parentid file */
  430. if (db_pid != NULL) {
  431. dblayer_release_index_file( be, ai_pid, db_pid );
  432. }
  433. /* Release the ancestorid file */
  434. if (db_aid != NULL) {
  435. dblayer_release_index_file( be, ai_aid, db_aid );
  436. }
  437. /* Enable the index */
  438. if (ret == 0) {
  439. ai_aid->ai_indexmask &= ~INDEX_OFFLINE;
  440. }
  441. return ret;
  442. }
  443. /*
  444. * Get parentid of an id by reading the operational attr from id2entry.
  445. */
  446. static int ldbm_parentid(backend *be, DB_TXN *txn, ID id, ID *ppid)
  447. {
  448. int ret = 0;
  449. DB *db = NULL;
  450. DBT key = {0};
  451. DBT data = {0};
  452. ID stored_id;
  453. char *p;
  454. /* Open the id2entry file */
  455. ret = dblayer_get_id2entry(be, &db);
  456. if (ret != 0) {
  457. ldbm_nasty(sourcefile,13100,ret);
  458. goto out;
  459. }
  460. /* Initialize key and data DBTs */
  461. id_internal_to_stored(id, (char *)&stored_id);
  462. key.data = (char *)&stored_id;
  463. key.size = sizeof(stored_id);
  464. key.flags = DB_DBT_USERMEM;
  465. data.flags = DB_DBT_MALLOC;
  466. /* Read id2entry */
  467. ret = db->get(db, txn, &key, &data, 0);
  468. if (ret != 0) {
  469. ldbm_nasty(sourcefile,13110,ret);
  470. goto out;
  471. }
  472. /* Extract the parentid value */
  473. #define PARENTID_STR "\nparentid:"
  474. p = strstr(data.data, PARENTID_STR);
  475. if (p == NULL) {
  476. *ppid = NOID;
  477. goto out;
  478. }
  479. *ppid = strtoul(p + strlen(PARENTID_STR), NULL, 10);
  480. out:
  481. /* Free the entry value */
  482. slapi_ch_free(&(data.data));
  483. /* Release the id2entry file */
  484. if (db != NULL) {
  485. dblayer_release_id2entry(be, db);
  486. }
  487. return ret;
  488. }
  489. static void id2idl_free(id2idl **ididl)
  490. {
  491. idl_free((*ididl)->idl);
  492. slapi_ch_free((void**)ididl);
  493. }
  494. static int id2idl_same_key(const void *ididl, const void *k)
  495. {
  496. return (((id2idl *)ididl)->keyid == *(ID *)k);
  497. }
  498. static int check_cache(id2idl_hash *ht)
  499. {
  500. id2idl *e;
  501. u_long i, found = 0;
  502. int ret = 0;
  503. if (ht == NULL) return 0;
  504. for (i = 0; i < ht->size; i++) {
  505. e = (id2idl *)ht->slot[i];
  506. while (e) {
  507. found++;
  508. e = e->next;
  509. }
  510. }
  511. if (found > 0) {
  512. LDAPDebug(LDAP_DEBUG_ANY, "ERROR: parentid index is not complete (%lu extra keys in ancestorid cache)\n", found,0,0);
  513. ret = -1;
  514. }
  515. return ret;
  516. }
  517. static void id2idl_hash_destroy(id2idl_hash *ht)
  518. {
  519. u_long i;
  520. id2idl *e, *next;
  521. if (ht == NULL) return;
  522. for (i = 0; i < ht->size; i++) {
  523. e = (id2idl *)ht->slot[i];
  524. while (e) {
  525. next = e->next;
  526. id2idl_free(&e);
  527. e = next;
  528. }
  529. }
  530. slapi_ch_free((void **)&ht);
  531. }
  532. /*
  533. * idl_union_allids - return a union b
  534. * takes attr index allids setting into account
  535. */
  536. static IDList *idl_union_allids(backend *be, struct attrinfo *ai, IDList *a, IDList *b)
  537. {
  538. if (!idl_get_idl_new()) {
  539. if (a != NULL && b != NULL) {
  540. if (ALLIDS( a ) || ALLIDS( b ) ||
  541. (IDL_NIDS(a) + IDL_NIDS(b) > idl_get_allidslimit(ai))) {
  542. return( idl_allids( be ) );
  543. }
  544. }
  545. }
  546. return idl_union(be, a, b);
  547. }
  548. static int ancestorid_addordel(
  549. backend *be,
  550. DB* db,
  551. ID node_id,
  552. ID id,
  553. DB_TXN *txn,
  554. struct attrinfo *ai,
  555. int flags,
  556. int *allids
  557. )
  558. {
  559. DBT key = {0};
  560. char keybuf[24];
  561. int ret = 0;
  562. /* Initialize key DBT */
  563. key.data = keybuf;
  564. key.ulen = sizeof(keybuf);
  565. key.flags = DB_DBT_USERMEM;
  566. key.size = PR_snprintf(key.data, key.ulen, "%c%lu",
  567. EQ_PREFIX, (u_long)node_id);
  568. key.size++; /* include the null terminator */
  569. if (flags & BE_INDEX_ADD) {
  570. #if 1
  571. LDAPDebug(LDAP_DEBUG_TRACE, "insert ancestorid %lu:%lu\n",
  572. (u_long)node_id, (u_long)id, 0);
  573. #endif
  574. ret = idl_insert_key(be, db, &key, id, txn, ai, allids);
  575. } else {
  576. #if 1
  577. LDAPDebug(LDAP_DEBUG_TRACE, "delete ancestorid %lu:%lu\n",
  578. (u_long)node_id, (u_long)id, 0);
  579. #endif
  580. ret = idl_delete_key(be, db, &key, id, txn, ai);
  581. }
  582. if (ret != 0) {
  583. ldbm_nasty(sourcefile,13120,ret);
  584. }
  585. return ret;
  586. }
  587. /*
  588. * Update ancestorid index inserting or deleting depending on flags.
  589. * The entry ids to be indexed are given by id (a base object)
  590. * and optionally subtree_idl (descendants of the base object).
  591. * The ancestorid keys to be updated are derived from nodes
  592. * in the tree from low up to high. Whether the low and high nodes
  593. * themselves are updated is given by include_low and include_high.
  594. */
  595. static int ldbm_ancestorid_index_update(
  596. backend *be,
  597. const Slapi_DN *low,
  598. const Slapi_DN *high,
  599. int include_low,
  600. int include_high,
  601. ID id,
  602. IDList *subtree_idl,
  603. int flags, /* BE_INDEX_ADD, BE_INDEX_DEL */
  604. back_txn *txn
  605. )
  606. {
  607. DB *db = NULL;
  608. int allids = IDL_INSERT_NORMAL;
  609. Slapi_DN dn = {0};
  610. Slapi_DN nextdn = {0};
  611. struct attrinfo *ai = NULL;
  612. struct berval ndnv;
  613. ID node_id, sub_id;
  614. IDList *idl;
  615. idl_iterator iter;
  616. int err = 0, ret = 0;
  617. DB_TXN *db_txn = txn != NULL ? txn->back_txn_txn : NULL;
  618. /* Open the ancestorid index */
  619. ainfo_get(be, "ancestorid", &ai);
  620. ret = dblayer_get_index_file(be, ai, &db, DBOPEN_CREATE);
  621. if (ret != 0) {
  622. ldbm_nasty(sourcefile,13130,ret);
  623. goto out;
  624. }
  625. slapi_sdn_copy(low, &dn);
  626. if (include_low == 0) {
  627. if (slapi_sdn_compare(&dn, high) == 0) {
  628. goto out;
  629. }
  630. /* Get the next highest DN */
  631. slapi_sdn_get_parent(&dn, &nextdn);
  632. slapi_sdn_copy(&nextdn, &dn);
  633. }
  634. /* Iterate up through the tree */
  635. do {
  636. if (slapi_sdn_isempty(&dn)) {
  637. break;
  638. }
  639. /* Have we reached the high node? */
  640. if (include_high == 0 && slapi_sdn_compare(&dn, high) == 0) {
  641. break;
  642. }
  643. /* Get the id for that DN */
  644. ndnv.bv_val = (void*)slapi_sdn_get_ndn(&dn);
  645. ndnv.bv_len = slapi_sdn_get_ndn_len(&dn);
  646. err = 0;
  647. idl = index_read(be, "entrydn", indextype_EQUALITY, &ndnv, txn, &err);
  648. if (idl == NULL) {
  649. if (err != 0 && err != DB_NOTFOUND) {
  650. ldbm_nasty(sourcefile,13140,ret);
  651. ret = err;
  652. }
  653. break;
  654. }
  655. node_id = idl_firstid(idl);
  656. idl_free(idl);
  657. /* Update ancestorid for the base entry */
  658. ret = ancestorid_addordel(be, db, node_id, id, db_txn, ai, flags, &allids);
  659. if (ret != 0) break;
  660. /*
  661. * If this node was already allids then all higher nodes must already
  662. * be at allids since the higher nodes must have a greater number
  663. * of descendants. Therefore no point continuing.
  664. */
  665. if (allids == IDL_INSERT_ALLIDS) break;
  666. /* Update ancestorid for any subtree entries */
  667. if (subtree_idl != NULL && ((flags & BE_INDEX_ADD) || (!ALLIDS(subtree_idl)))) {
  668. iter = idl_iterator_init(subtree_idl);
  669. while ((sub_id = idl_iterator_dereference_increment(&iter, subtree_idl)) != NOID) {
  670. ret = ancestorid_addordel(be, db, node_id, sub_id, db_txn, ai, flags, &allids);
  671. if (ret != 0) break;
  672. }
  673. if (ret != 0) break;
  674. }
  675. /* Have we reached the high node? */
  676. if (slapi_sdn_compare(&dn, high) == 0) {
  677. break;
  678. }
  679. /* Get the next highest DN */
  680. slapi_sdn_get_parent(&dn, &nextdn);
  681. slapi_sdn_copy(&nextdn, &dn);
  682. } while (ret == 0);
  683. out:
  684. slapi_sdn_done(&dn);
  685. slapi_sdn_done(&nextdn);
  686. /* Release the ancestorid file */
  687. if (db != NULL) {
  688. dblayer_release_index_file(be, ai, db);
  689. }
  690. return ret;
  691. }
  692. /*
  693. * Update the ancestorid index for a single entry.
  694. * This function depends on the integrity of the entrydn index.
  695. */
  696. int ldbm_ancestorid_index_entry(
  697. backend *be,
  698. struct backentry *e,
  699. int flags, /* BE_INDEX_ADD, BE_INDEX_DEL */
  700. back_txn *txn
  701. )
  702. {
  703. int ret = 0;
  704. ret = ldbm_ancestorid_index_update(be,
  705. slapi_entry_get_sdn_const(e->ep_entry),
  706. slapi_be_getsuffix(be, 0),
  707. 0, 1, e->ep_id, NULL, flags, txn);
  708. return ret;
  709. }
  710. /*
  711. * Returns <0, 0, >0 according to whether right is a suffix of left,
  712. * neither is a suffix of the other, or left is a suffix of right.
  713. * If common is non-null then the common suffix of left and right
  714. * is returned in *common.
  715. */
  716. int slapi_sdn_suffix_cmp(
  717. const Slapi_DN *left,
  718. const Slapi_DN *right,
  719. Slapi_DN *common
  720. )
  721. {
  722. char **rdns1, **rdns2;
  723. int count1, count2, i, ret = 0;
  724. size_t len = 0;
  725. char *p, *ndnstr;
  726. rdns1 = ldap_explode_dn(slapi_sdn_get_ndn(left), 0);
  727. rdns2 = ldap_explode_dn(slapi_sdn_get_ndn(right), 0);
  728. for(count1 = 0; rdns1[count1]!=NULL; count1++){
  729. }
  730. count1--;
  731. for(count2 = 0; rdns2[count2]!=NULL; count2++){
  732. }
  733. count2--;
  734. while (count1 >= 0 && count2 >= 0) {
  735. if (strcmp(rdns1[count1], rdns2[count2]) != 0) break;
  736. count1--;
  737. count2--;
  738. }
  739. count1++;
  740. count2++;
  741. if (count1 == 0 && count2 == 0) {
  742. /* equal */
  743. ret = 0;
  744. } else if (count1 == 0) {
  745. /* left is suffix of right */
  746. ret = 1;
  747. } else if (count2 == 0) {
  748. /* right is suffix of left */
  749. ret = -1;
  750. } else {
  751. /* common prefix (possibly root), not left nor right */
  752. ret = 0;
  753. }
  754. /* if caller does not want the common prefix then we're done */
  755. if (common == NULL) goto out;
  756. /* figure out how much space we need */
  757. for (i = count1; rdns1[i] != NULL; i++) {
  758. len += strlen(rdns1[i]) + 1;
  759. }
  760. /* write the string */
  761. p = ndnstr = slapi_ch_calloc(len+1,sizeof(char));
  762. for (i = count1; rdns1[i] != NULL; i++) {
  763. sprintf(p, "%s%s", (p != ndnstr) ? "," : "", rdns1[i]);
  764. p += strlen(p);
  765. }
  766. /* return the DN */
  767. slapi_sdn_set_dn_passin(common, ndnstr);
  768. LDAPDebug(LDAP_DEBUG_TRACE, "common suffix <%s>\n",
  769. slapi_sdn_get_dn(common), 0, 0);
  770. out:
  771. slapi_ldap_value_free(rdns1);
  772. slapi_ldap_value_free(rdns2);
  773. LDAPDebug(LDAP_DEBUG_TRACE, "slapi_sdn_suffix_cmp(<%s>, <%s>) => %d\n",
  774. slapi_sdn_get_dn(left), slapi_sdn_get_dn(right), ret);
  775. return ret;
  776. }
  777. int ldbm_ancestorid_move_subtree(
  778. backend *be,
  779. const Slapi_DN *olddn,
  780. const Slapi_DN *newdn,
  781. ID id,
  782. IDList *subtree_idl,
  783. back_txn *txn
  784. )
  785. {
  786. int ret = 0;
  787. Slapi_DN commondn = {0};
  788. /* Determine the common ancestor */
  789. (void)slapi_sdn_suffix_cmp(olddn, newdn, &commondn);
  790. /* Delete from old ancestors */
  791. ret = ldbm_ancestorid_index_update(be,
  792. olddn,
  793. &commondn,
  794. 0,
  795. 0,
  796. id,
  797. subtree_idl,
  798. BE_INDEX_DEL,
  799. txn);
  800. if (ret != 0) goto out;
  801. /* Add to new ancestors */
  802. ret = ldbm_ancestorid_index_update(be,
  803. newdn,
  804. &commondn,
  805. 0,
  806. 0,
  807. id,
  808. subtree_idl,
  809. BE_INDEX_ADD,
  810. txn);
  811. out:
  812. slapi_sdn_done(&commondn);
  813. return ret;
  814. }
  815. int ldbm_ancestorid_read(
  816. backend *be,
  817. back_txn *txn,
  818. ID id,
  819. IDList **idl
  820. )
  821. {
  822. int ret = 0;
  823. struct berval bv;
  824. char keybuf[24];
  825. bv.bv_val = keybuf;
  826. bv.bv_len = PR_snprintf(keybuf, sizeof(keybuf), "%lu", (u_long)id);
  827. *idl = index_read(be, "ancestorid", indextype_EQUALITY, &bv, txn, &ret);
  828. return ret;
  829. }