seq.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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. /* seq.c - ldbm backend sequential access function */
  42. #include "back-ldbm.h"
  43. #define SEQ_LITTLE_BUFFER_SIZE 100
  44. /*
  45. * Access the database sequentially.
  46. * There are 4 ways to call this routine. In each case, the equality index
  47. * for "attrname" is consulted:
  48. * 1) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_FIRST, then this routine
  49. * will find the smallest key greater than or equal to the SLAPI_SEQ_VAL
  50. * parameter, and return all entries that key's IDList. If SLAPI_SEQ_VAL
  51. * is NULL, then the smallest key is retrieved and the associaated
  52. * entries are returned.
  53. * 2) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_NEXT, then this routine
  54. * will find the smallest key strictly greater than the SLAPI_SEQ_VAL
  55. * parameter, and return all entries that key's IDList.
  56. * 3) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_PREV, then this routine
  57. * will find the greatest key strictly less than the SLAPI_SEQ_VAL
  58. * parameter, and return all entries that key's IDList.
  59. * 4) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_LAST, then this routine
  60. * will find the largest equality key in the index and return all entries
  61. * which match that key. The SLAPI_SEQ_VAL parameter is ignored.
  62. */
  63. int
  64. ldbm_back_seq( Slapi_PBlock *pb )
  65. {
  66. backend *be;
  67. ldbm_instance *inst;
  68. struct ldbminfo *li;
  69. IDList *idl = NULL;
  70. int err = LDAP_SUCCESS;
  71. DB *db;
  72. DBC *dbc = NULL;
  73. int type;
  74. char *attrname, *val;
  75. int isroot;
  76. struct attrinfo *ai = NULL;
  77. int return_value = -1;
  78. int nentries = 0;
  79. int retry_count=0;
  80. /* Decode arguments */
  81. slapi_pblock_get( pb, SLAPI_BACKEND, &be);
  82. slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
  83. slapi_pblock_get( pb, SLAPI_SEQ_TYPE, &type );
  84. slapi_pblock_get( pb, SLAPI_SEQ_ATTRNAME, &attrname );
  85. slapi_pblock_get( pb, SLAPI_SEQ_VAL, &val );
  86. slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
  87. inst = (ldbm_instance *) be->be_instance_info;
  88. /* Validate arguments */
  89. if ( type != SLAPI_SEQ_FIRST &&
  90. type != SLAPI_SEQ_LAST &&
  91. type != SLAPI_SEQ_NEXT &&
  92. type != SLAPI_SEQ_PREV )
  93. {
  94. slapi_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
  95. "Bad seq access type", 0, NULL );
  96. return( -1 );
  97. }
  98. /* get a database */
  99. ainfo_get( be, attrname, &ai );
  100. LDAPDebug( LDAP_DEBUG_ARGS,
  101. " seq: indextype: %s indexmask: 0x%x seek type: %d\n",
  102. ai->ai_type, ai->ai_indexmask, type );
  103. if ( ! (INDEX_EQUALITY & ai->ai_indexmask) ) {
  104. LDAPDebug( LDAP_DEBUG_TRACE,
  105. "seq: caller specified un-indexed attribute %s\n",
  106. attrname ? attrname : "", 0, 0 );
  107. slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
  108. "Unindexed seq access type", 0, NULL );
  109. return -1;
  110. }
  111. if ( (return_value = dblayer_get_index_file( be, ai, &db, DBOPEN_CREATE )) != 0 ) {
  112. LDAPDebug( LDAP_DEBUG_ANY,
  113. "<= ldbm_back_seq NULL (could not open index file for attribute %s)\n",
  114. attrname, 0, 0 );
  115. slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
  116. return -1;
  117. }
  118. /* First, get a database cursor */
  119. return_value = db->cursor(db,NULL,&dbc,0);
  120. if (0 == return_value)
  121. {
  122. DBT data = {0};
  123. DBT key = {0};
  124. char little_buffer[SEQ_LITTLE_BUFFER_SIZE];
  125. char *big_buffer = NULL;
  126. char keystring = EQ_PREFIX;
  127. /* Set data */
  128. data.flags = DB_DBT_MALLOC;
  129. /* Set up key */
  130. key.flags = DB_DBT_MALLOC;
  131. if (NULL == val)
  132. {
  133. /* this means, goto the first equality key */
  134. /* seek to key >= "=" */
  135. key.data = &keystring;
  136. key.size = 1;
  137. }
  138. else
  139. {
  140. size_t key_length = strlen(val) + 2;
  141. if (key_length <= SEQ_LITTLE_BUFFER_SIZE) {
  142. key.data = &little_buffer;
  143. } else {
  144. big_buffer = slapi_ch_malloc(key_length);
  145. if (NULL == big_buffer) {
  146. /* memory allocation failure */
  147. dblayer_release_index_file( be, ai, db );
  148. return -1;
  149. }
  150. key.data = big_buffer;
  151. }
  152. key.size = sprintf(key.data,"%c%s",EQ_PREFIX,val);
  153. }
  154. /* decide which type of operation we're being asked to do and do the db bit */
  155. /* The c_get call always mallocs memory for data.data */
  156. /* The c_get call mallocs memory for key.data, except for DB_SET */
  157. /* after this, we leave data containing the retrieved IDL, or NULL if we didn't get it */
  158. switch (type) {
  159. case SLAPI_SEQ_FIRST:
  160. /* if (NULL == val) goto the first equality key ( seek to key >= "=" ) */
  161. /* else goto the first equality key >= val ( seek to key >= "=val" )*/
  162. return_value = dbc->c_get(dbc,&key,&data,DB_SET_RANGE);
  163. break;
  164. case SLAPI_SEQ_NEXT:
  165. /* seek to the indicated =value, then seek to the next entry, */
  166. return_value = dbc->c_get(dbc,&key,&data,DB_SET);
  167. if (0 == return_value)
  168. {
  169. slapi_ch_free(&(data.data));
  170. return_value = dbc->c_get(dbc,&key,&data,DB_NEXT);
  171. }
  172. else
  173. {
  174. /* DB_SET doesn't allocate key data. Make sure we don't try to free it... */
  175. key.data= NULL;
  176. }
  177. break;
  178. case SLAPI_SEQ_PREV:
  179. /* seek to the indicated =value, then seek to the previous entry, */
  180. return_value = dbc->c_get(dbc,&key,&data,DB_SET);
  181. if (0 == return_value )
  182. {
  183. slapi_ch_free(&(data.data));
  184. return_value = dbc->c_get(dbc,&key,&data,DB_PREV);
  185. }
  186. else
  187. {
  188. /* DB_SET doesn't allocate key data. Make sure we don't try to free it... */
  189. key.data= NULL;
  190. }
  191. break;
  192. case SLAPI_SEQ_LAST:
  193. /* seek to the first possible key after all the equality keys (">"), then seek back one */
  194. {
  195. keystring = EQ_PREFIX + 1;
  196. key.data = &keystring;
  197. key.size = 1;
  198. return_value = dbc->c_get(dbc,&key,&data,DB_SET_RANGE);
  199. if (0 == return_value || DB_NOTFOUND == return_value)
  200. {
  201. slapi_ch_free(&(data.data));
  202. return_value = dbc->c_get(dbc,&key,&data,DB_PREV);
  203. }
  204. }
  205. break;
  206. default:
  207. PR_ASSERT(0);
  208. }
  209. dbc->c_close(dbc);
  210. if (0 == return_value && key.data!=NULL)
  211. {
  212. /* Now check that the key we eventually settled on was an equality key ! */
  213. if (*((char*)key.data) == EQ_PREFIX)
  214. {
  215. /* Retrieve the idlist for this key */
  216. key.flags = 0;
  217. for (retry_count = 0; retry_count < IDL_FETCH_RETRY_COUNT; retry_count++) {
  218. err = NEW_IDL_DEFAULT;
  219. idl = idl_fetch( be, db, &key, NULL, ai, &err );
  220. if(err == DB_LOCK_DEADLOCK) {
  221. ldbm_nasty("ldbm_back_seq deadlock retry", 1600, err);
  222. continue;
  223. } else {
  224. break;
  225. }
  226. }
  227. }
  228. }
  229. if(retry_count == IDL_FETCH_RETRY_COUNT) {
  230. ldbm_nasty("ldbm_back_seq retry count exceeded",1645,err);
  231. } else if ( err != 0 && err != DB_NOTFOUND ) {
  232. ldbm_nasty("ldbm_back_seq database error", 1650, err);
  233. }
  234. slapi_ch_free( &(data.data) );
  235. if ( key.data != little_buffer && key.data != &keystring ) {
  236. slapi_ch_free( &(key.data) );
  237. }
  238. slapi_ch_free_string( &big_buffer );
  239. }
  240. /* null idlist means there were no matching keys */
  241. if ( idl != NULL )
  242. {
  243. /*
  244. * Step through the IDlist. For each ID, get the entry
  245. * and send it.
  246. */
  247. ID id;
  248. struct backentry *e;
  249. for ( id = idl_firstid( idl ); id != NOID;
  250. id = idl_nextid( idl, id ))
  251. {
  252. if (( e = id2entry( be, id, NULL, &err )) == NULL )
  253. {
  254. if ( err != LDAP_SUCCESS )
  255. {
  256. LDAPDebug( LDAP_DEBUG_ANY, "seq id2entry err %d\n", err, 0, 0 );
  257. }
  258. LDAPDebug( LDAP_DEBUG_ARGS,
  259. "ldbm_back_seq: candidate %lu not found\n",
  260. (u_long)id, 0, 0 );
  261. continue;
  262. }
  263. if ( slapi_send_ldap_search_entry( pb, e->ep_entry, NULL, NULL, 0 ) == 0 )
  264. {
  265. nentries++;
  266. }
  267. cache_return( &inst->inst_cache, &e );
  268. }
  269. idl_free( idl );
  270. }
  271. dblayer_release_index_file( be, ai, db );
  272. slapi_send_ldap_result( pb, LDAP_SUCCESS == err ? LDAP_SUCCESS : LDAP_OPERATIONS_ERROR, NULL, NULL, nentries, NULL );
  273. return 0;
  274. }