ancestorid.c 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  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 = LDBM_ANCESTORID_STR;
  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, LDBM_PARENTID_STR, &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, LDBM_ANCESTORID_STR, &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, LDBM_PARENTID_STR, &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, LDBM_ANCESTORID_STR, &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, LDBM_PARENTID_STR, &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. slapi_log_error(SLAPI_LOG_FATAL, sourcefile,
  398. "Error: ldbm_parentid on node index [" ID_FMT "] of [" ID_FMT "]\n",
  399. nids, nodes->b_nids);
  400. idl_free(&children);
  401. goto out;
  402. }
  403. /* A suffix entry does not have a parent */
  404. if (parentid == NOID) {
  405. idl_free(&children);
  406. break;
  407. }
  408. /* Reset the key to the parent id */
  409. key.size = PR_snprintf(key.data, key.ulen, "%c%lu",
  410. EQ_PREFIX, (u_long)parentid);
  411. key.size++;
  412. /* Insert into ancestorid for this node's parent */
  413. ret = idl_store_block(be, db_aid, &key, children, txn, ai_aid);
  414. if (ret != 0) {
  415. idl_free(&children);
  416. goto out;
  417. }
  418. id = parentid;
  419. }
  420. } while (nids > 0);
  421. if (ret != 0) {
  422. goto out;
  423. }
  424. out:
  425. if (ret == 0) {
  426. LDAPDebug(LDAP_DEBUG_TRACE, "Created ancestorid index\n", 0,0,0);
  427. } else {
  428. LDAPDebug(LDAP_DEBUG_ANY, "Failed to create ancestorid index\n", 0,0,0);
  429. }
  430. /* Free any leftover idlists */
  431. idl_free(&nodes);
  432. /* Release the parentid file */
  433. if (db_pid != NULL) {
  434. dblayer_release_index_file( be, ai_pid, db_pid );
  435. }
  436. /* Release the ancestorid file */
  437. if (db_aid != NULL) {
  438. dblayer_release_index_file( be, ai_aid, db_aid );
  439. }
  440. /* Enable the index */
  441. if (ret == 0) {
  442. ai_aid->ai_indexmask &= ~INDEX_OFFLINE;
  443. }
  444. return ret;
  445. }
  446. /*
  447. * Get parentid of an id by reading the operational attr from id2entry.
  448. */
  449. static int ldbm_parentid(backend *be, DB_TXN *txn, ID id, ID *ppid)
  450. {
  451. int ret = 0;
  452. DB *db = NULL;
  453. DBT key = {0};
  454. DBT data = {0};
  455. ID stored_id;
  456. char *p;
  457. /* Open the id2entry file */
  458. ret = dblayer_get_id2entry(be, &db);
  459. if (ret != 0) {
  460. ldbm_nasty(sourcefile,13100,ret);
  461. goto out;
  462. }
  463. /* Initialize key and data DBTs */
  464. id_internal_to_stored(id, (char *)&stored_id);
  465. key.data = (char *)&stored_id;
  466. key.size = sizeof(stored_id);
  467. key.flags = DB_DBT_USERMEM;
  468. data.flags = DB_DBT_MALLOC;
  469. /* Read id2entry */
  470. ret = db->get(db, txn, &key, &data, 0);
  471. if (ret != 0) {
  472. ldbm_nasty(sourcefile,13110,ret);
  473. slapi_log_error(SLAPI_LOG_FATAL, sourcefile,
  474. "Error: unable to find entry id [" ID_FMT "] (original [" ID_FMT "])"
  475. " in id2entry\n", stored_id, id);
  476. goto out;
  477. }
  478. /* Extract the parentid value */
  479. #define PARENTID_STR "\nparentid:"
  480. p = strstr(data.data, PARENTID_STR);
  481. if (p == NULL) {
  482. *ppid = NOID;
  483. goto out;
  484. }
  485. *ppid = strtoul(p + strlen(PARENTID_STR), NULL, 10);
  486. out:
  487. /* Free the entry value */
  488. slapi_ch_free(&(data.data));
  489. /* Release the id2entry file */
  490. if (db != NULL) {
  491. dblayer_release_id2entry(be, db);
  492. }
  493. return ret;
  494. }
  495. static void id2idl_free(id2idl **ididl)
  496. {
  497. idl_free(&((*ididl)->idl));
  498. slapi_ch_free((void**)ididl);
  499. }
  500. static int id2idl_same_key(const void *ididl, const void *k)
  501. {
  502. return (((id2idl *)ididl)->keyid == *(ID *)k);
  503. }
  504. static int check_cache(id2idl_hash *ht)
  505. {
  506. id2idl *e;
  507. u_long i, found = 0;
  508. int ret = 0;
  509. if (ht == NULL) return 0;
  510. for (i = 0; i < ht->size; i++) {
  511. e = (id2idl *)ht->slot[i];
  512. while (e) {
  513. found++;
  514. e = e->next;
  515. }
  516. }
  517. if (found > 0) {
  518. LDAPDebug(LDAP_DEBUG_ANY, "ERROR: parentid index is not complete (%lu extra keys in ancestorid cache)\n", found,0,0);
  519. ret = -1;
  520. }
  521. return ret;
  522. }
  523. static void id2idl_hash_destroy(id2idl_hash *ht)
  524. {
  525. u_long i;
  526. id2idl *e, *next;
  527. if (ht == NULL) return;
  528. for (i = 0; i < ht->size; i++) {
  529. e = (id2idl *)ht->slot[i];
  530. while (e) {
  531. next = e->next;
  532. id2idl_free(&e);
  533. e = next;
  534. }
  535. }
  536. slapi_ch_free((void **)&ht);
  537. }
  538. /*
  539. * idl_union_allids - return a union b
  540. * takes attr index allids setting into account
  541. */
  542. static IDList *idl_union_allids(backend *be, struct attrinfo *ai, IDList *a, IDList *b)
  543. {
  544. if (!idl_get_idl_new()) {
  545. if (a != NULL && b != NULL) {
  546. if (ALLIDS( a ) || ALLIDS( b ) ||
  547. (IDL_NIDS(a) + IDL_NIDS(b) > idl_get_allidslimit(ai, 0))) {
  548. return( idl_allids( be ) );
  549. }
  550. }
  551. }
  552. return idl_union(be, a, b);
  553. }
  554. static int ancestorid_addordel(
  555. backend *be,
  556. DB* db,
  557. ID node_id,
  558. ID id,
  559. DB_TXN *txn,
  560. struct attrinfo *ai,
  561. int flags,
  562. int *allids
  563. )
  564. {
  565. DBT key = {0};
  566. char keybuf[24];
  567. int ret = 0;
  568. /* Initialize key DBT */
  569. key.data = keybuf;
  570. key.ulen = sizeof(keybuf);
  571. key.flags = DB_DBT_USERMEM;
  572. key.size = PR_snprintf(key.data, key.ulen, "%c%lu",
  573. EQ_PREFIX, (u_long)node_id);
  574. key.size++; /* include the null terminator */
  575. if (flags & BE_INDEX_ADD) {
  576. #if 1
  577. LDAPDebug(LDAP_DEBUG_TRACE, "insert ancestorid %lu:%lu\n",
  578. (u_long)node_id, (u_long)id, 0);
  579. #endif
  580. ret = idl_insert_key(be, db, &key, id, txn, ai, allids);
  581. } else {
  582. #if 1
  583. LDAPDebug(LDAP_DEBUG_TRACE, "delete ancestorid %lu:%lu\n",
  584. (u_long)node_id, (u_long)id, 0);
  585. #endif
  586. ret = idl_delete_key(be, db, &key, id, txn, ai);
  587. }
  588. if (ret != 0) {
  589. ldbm_nasty(sourcefile,13120,ret);
  590. }
  591. return ret;
  592. }
  593. /*
  594. * Update ancestorid index inserting or deleting depending on flags.
  595. * The entry ids to be indexed are given by id (a base object)
  596. * and optionally subtree_idl (descendants of the base object).
  597. * The ancestorid keys to be updated are derived from nodes
  598. * in the tree from low up to high. Whether the low and high nodes
  599. * themselves are updated is given by include_low and include_high.
  600. */
  601. static int ldbm_ancestorid_index_update(
  602. backend *be,
  603. const Slapi_DN *low,
  604. const Slapi_DN *high,
  605. int include_low,
  606. int include_high,
  607. ID id,
  608. IDList *subtree_idl,
  609. int flags, /* BE_INDEX_ADD, BE_INDEX_DEL */
  610. back_txn *txn
  611. )
  612. {
  613. DB *db = NULL;
  614. int allids = IDL_INSERT_NORMAL;
  615. Slapi_DN sdn;
  616. Slapi_DN nextsdn;
  617. struct attrinfo *ai = NULL;
  618. ID node_id, sub_id;
  619. idl_iterator iter;
  620. int err = 0, ret = 0;
  621. DB_TXN *db_txn = txn != NULL ? txn->back_txn_txn : NULL;
  622. slapi_sdn_init(&sdn);
  623. slapi_sdn_init(&nextsdn);
  624. /* Open the ancestorid index */
  625. ainfo_get(be, LDBM_ANCESTORID_STR, &ai);
  626. ret = dblayer_get_index_file(be, ai, &db, DBOPEN_CREATE);
  627. if (ret != 0) {
  628. ldbm_nasty(sourcefile,13130,ret);
  629. goto out;
  630. }
  631. slapi_sdn_copy(low, &sdn);
  632. if (include_low == 0) {
  633. if (slapi_sdn_compare(&sdn, high) == 0) {
  634. goto out;
  635. }
  636. /* Get the next highest DN */
  637. slapi_sdn_get_parent(&sdn, &nextsdn);
  638. slapi_sdn_copy(&nextsdn, &sdn);
  639. }
  640. /* Iterate up through the tree */
  641. do {
  642. if (slapi_sdn_isempty(&sdn)) {
  643. break;
  644. }
  645. /* Have we reached the high node? */
  646. if (include_high == 0 && slapi_sdn_compare(&sdn, high) == 0) {
  647. break;
  648. }
  649. /* Get the id for that DN */
  650. if (entryrdn_get_switch()) { /* subtree-rename: on */
  651. node_id = 0;
  652. err = entryrdn_index_read(be, &sdn, &node_id, txn);
  653. if (err) {
  654. if (DB_NOTFOUND != err) {
  655. ldbm_nasty(sourcefile,13141,err);
  656. LDAPDebug1Arg(LDAP_DEBUG_ANY, "entryrdn_index_read(%s)\n", slapi_sdn_get_dn(&sdn));
  657. ret = err;
  658. }
  659. break;
  660. }
  661. } else {
  662. IDList *idl = NULL;
  663. struct berval ndnv;
  664. ndnv.bv_val = (void*)slapi_sdn_get_ndn(&sdn);
  665. ndnv.bv_len = slapi_sdn_get_ndn_len(&sdn);
  666. err = 0;
  667. idl = index_read(be, LDBM_ENTRYDN_STR, indextype_EQUALITY, &ndnv, txn, &err);
  668. if (idl == NULL) {
  669. if (err != 0 && err != DB_NOTFOUND) {
  670. ldbm_nasty(sourcefile,13140,err);
  671. ret = err;
  672. }
  673. break;
  674. }
  675. node_id = idl_firstid(idl);
  676. idl_free(&idl);
  677. }
  678. /* Update ancestorid for the base entry */
  679. ret = ancestorid_addordel(be, db, node_id, id, db_txn, ai, flags, &allids);
  680. if (ret != 0) break;
  681. /*
  682. * If this node was already allids then all higher nodes must already
  683. * be at allids since the higher nodes must have a greater number
  684. * of descendants. Therefore no point continuing.
  685. */
  686. if (allids == IDL_INSERT_ALLIDS) break;
  687. /* Update ancestorid for any subtree entries */
  688. if (subtree_idl != NULL && ((flags & BE_INDEX_ADD) || (!ALLIDS(subtree_idl)))) {
  689. iter = idl_iterator_init(subtree_idl);
  690. while ((sub_id = idl_iterator_dereference_increment(&iter, subtree_idl)) != NOID) {
  691. ret = ancestorid_addordel(be, db, node_id, sub_id, db_txn, ai, flags, &allids);
  692. if (ret != 0) break;
  693. }
  694. if (ret != 0) break;
  695. }
  696. /* Have we reached the high node? */
  697. if (slapi_sdn_compare(&sdn, high) == 0) {
  698. break;
  699. }
  700. /* Get the next highest DN */
  701. slapi_sdn_get_parent(&sdn, &nextsdn);
  702. slapi_sdn_copy(&nextsdn, &sdn);
  703. } while (ret == 0);
  704. out:
  705. slapi_sdn_done(&sdn);
  706. slapi_sdn_done(&nextsdn);
  707. /* Release the ancestorid file */
  708. if (db != NULL) {
  709. dblayer_release_index_file(be, ai, db);
  710. }
  711. return ret;
  712. }
  713. /*
  714. * Update the ancestorid index for a single entry.
  715. * This function depends on the integrity of the entrydn index.
  716. */
  717. int ldbm_ancestorid_index_entry(
  718. backend *be,
  719. struct backentry *e,
  720. int flags, /* BE_INDEX_ADD, BE_INDEX_DEL */
  721. back_txn *txn
  722. )
  723. {
  724. int ret = 0;
  725. ret = ldbm_ancestorid_index_update(be,
  726. slapi_entry_get_sdn_const(e->ep_entry),
  727. slapi_be_getsuffix(be, 0),
  728. 0, 1, e->ep_id, NULL, flags, txn);
  729. return ret;
  730. }
  731. /*
  732. * Returns <0, 0, >0 according to whether right is a suffix of left,
  733. * neither is a suffix of the other, or left is a suffix of right.
  734. * If common is non-null then the common suffix of left and right
  735. * is returned in *common.
  736. */
  737. static int
  738. _sdn_suffix_cmp(
  739. const Slapi_DN *left,
  740. const Slapi_DN *right,
  741. Slapi_DN *common
  742. )
  743. {
  744. char **rdns1, **rdns2;
  745. int count1, count2, i, ret = 0;
  746. size_t len = 0;
  747. char *p, *ndnstr;
  748. rdns1 = slapi_ldap_explode_dn(slapi_sdn_get_ndn(left), 0);
  749. rdns2 = slapi_ldap_explode_dn(slapi_sdn_get_ndn(right), 0);
  750. if (NULL == rdns1) {
  751. if (NULL == rdns2) {
  752. ret = 0;
  753. } else {
  754. ret = 1;
  755. }
  756. goto out;
  757. } else {
  758. if (NULL == rdns2) {
  759. ret = -1;
  760. goto out;
  761. }
  762. }
  763. for(count1 = 0; rdns1[count1]!=NULL; count1++) ;
  764. count1--;
  765. for(count2 = 0; rdns2[count2]!=NULL; count2++) ;
  766. count2--;
  767. while (count1 >= 0 && count2 >= 0) {
  768. if (strcmp(rdns1[count1], rdns2[count2]) != 0) break;
  769. count1--;
  770. count2--;
  771. }
  772. count1++;
  773. count2++;
  774. if (count1 == 0 && count2 == 0) {
  775. /* equal */
  776. ret = 0;
  777. } else if (count1 == 0) {
  778. /* left is suffix of right */
  779. ret = 1;
  780. } else if (count2 == 0) {
  781. /* right is suffix of left */
  782. ret = -1;
  783. } else {
  784. /* common prefix (possibly root), not left nor right */
  785. ret = 0;
  786. }
  787. /* if caller does not want the common prefix then we're done */
  788. if (common == NULL) goto out;
  789. /* figure out how much space we need */
  790. for (i = count1; rdns1[i] != NULL; i++) {
  791. len += strlen(rdns1[i]) + 1;
  792. }
  793. /* write the string */
  794. p = ndnstr = slapi_ch_calloc(len+1,sizeof(char));
  795. for (i = count1; rdns1[i] != NULL; i++) {
  796. sprintf(p, "%s%s", (p != ndnstr) ? "," : "", rdns1[i]);
  797. p += strlen(p);
  798. }
  799. /* return the DN */
  800. slapi_sdn_set_dn_passin(common, ndnstr);
  801. LDAPDebug(LDAP_DEBUG_TRACE, "common suffix <%s>\n",
  802. slapi_sdn_get_dn(common), 0, 0);
  803. out:
  804. slapi_ldap_value_free(rdns1);
  805. slapi_ldap_value_free(rdns2);
  806. LDAPDebug(LDAP_DEBUG_TRACE, "_sdn_suffix_cmp(<%s>, <%s>) => %d\n",
  807. slapi_sdn_get_dn(left), slapi_sdn_get_dn(right), ret);
  808. return ret;
  809. }
  810. int ldbm_ancestorid_move_subtree(
  811. backend *be,
  812. const Slapi_DN *olddn,
  813. const Slapi_DN *newdn,
  814. ID id,
  815. IDList *subtree_idl,
  816. back_txn *txn
  817. )
  818. {
  819. int ret = 0;
  820. Slapi_DN commonsdn;
  821. slapi_sdn_init(&commonsdn);
  822. /* Determine the common ancestor */
  823. (void)_sdn_suffix_cmp(olddn, newdn, &commonsdn);
  824. /* Delete from old ancestors */
  825. ret = ldbm_ancestorid_index_update(be,
  826. olddn,
  827. &commonsdn,
  828. 0,
  829. 0,
  830. id,
  831. subtree_idl,
  832. BE_INDEX_DEL,
  833. txn);
  834. if (ret != 0) goto out;
  835. /* Add to new ancestors */
  836. ret = ldbm_ancestorid_index_update(be,
  837. newdn,
  838. &commonsdn,
  839. 0,
  840. 0,
  841. id,
  842. subtree_idl,
  843. BE_INDEX_ADD,
  844. txn);
  845. out:
  846. slapi_sdn_done(&commonsdn);
  847. return ret;
  848. }
  849. int ldbm_ancestorid_read_ext(
  850. backend *be,
  851. back_txn *txn,
  852. ID id,
  853. IDList **idl,
  854. int allidslimit
  855. )
  856. {
  857. int ret = 0;
  858. struct berval bv;
  859. char keybuf[24];
  860. bv.bv_val = keybuf;
  861. bv.bv_len = PR_snprintf(keybuf, sizeof(keybuf), "%lu", (u_long)id);
  862. *idl = index_read_ext_allids(NULL, be, LDBM_ANCESTORID_STR, indextype_EQUALITY, &bv, txn, &ret, NULL, allidslimit);
  863. return ret;
  864. }
  865. int ldbm_ancestorid_read(
  866. backend *be,
  867. back_txn *txn,
  868. ID id,
  869. IDList **idl
  870. )
  871. {
  872. return ldbm_ancestorid_read_ext(be, txn, id, idl, 0);
  873. }