static int _VmDirInternalPagedSearch( VDIR_OPERATION * pOperation ) { int retVal = LDAP_SUCCESS; PSTR pszLocalErrMsg = NULL; // should never happen - there are asserts in calling function if (pOperation == NULL) { BAIL_WITH_VMDIR_ERROR(retVal, VMDIR_ERROR_INVALID_PARAMETER); } /* * New page search * TODO: Iterator logic and ranking algorithm will be added in next tasks */ if (pOperation->showPagedResultsCtrl != NULL && IsNullOrEmptyString(pOperation->showPagedResultsCtrl->value.pagedResultCtrlVal.cookie)) { retVal = BuildCandidateList(pOperation, pOperation->request.searchReq.filter, 0); BAIL_ON_VMDIR_ERROR_WITH_MSG(retVal, pszLocalErrMsg, "BuildCandidateList failed."); if (pOperation->request.searchReq.filter->computeResult == FILTER_RES_TRUE) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Full scan of Entry DB is required. Refine your search."); } } retVal = VmDirProcessPagedSearch(pOperation); BAIL_ON_VMDIR_ERROR(retVal); cleanup: VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s failed, error(%d)", __FUNCTION__, retVal); goto cleanup; }
static int _VmDirInternalNormalSearch( VDIR_OPERATION * pOperation ) { int retVal = LDAP_SUCCESS; PSTR pszLocalErrMsg = NULL; ENTRYID eStartingId = 0; // should never happen - there are asserts in calling function if (pOperation == NULL) { BAIL_WITH_VMDIR_ERROR(retVal, VMDIR_ERROR_INVALID_PARAMETER); } retVal = BuildCandidateList(pOperation, pOperation->request.searchReq.filter, eStartingId); BAIL_ON_VMDIR_ERROR_WITH_MSG(retVal, pszLocalErrMsg, "BuildCandidateList failed."); if (pOperation->request.searchReq.filter->computeResult == FILTER_RES_TRUE) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Full scan of Entry DB is required. Refine your search."); } retVal = VmDirProcessCandidateList(pOperation); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "VmDirProcessCandidateList failed. (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->ldapResult.pszErrMsg)); cleanup: VMDIR_SAFE_FREE_MEMORY(pszLocalErrMsg); return retVal; error: VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s failed, error(%d)", __FUNCTION__, retVal); goto cleanup; }
static int BuildCandidateList( PVDIR_OPERATION pOperation, VDIR_FILTER * f, ENTRYID eStartingId ) { int retVal = LDAP_SUCCESS; VDIR_FILTER * nextFilter = NULL; if (f->computeResult != FILTER_RES_NORMAL) { goto cleanup; } switch ( f->choice ) { case LDAP_FILTER_AND: { VDIR_FILTER * specialFilter = NULL; int iMaxIndexScan = gVmdirGlobals.dwMaxIndexScan; int iSmallCandidateSet = gVmdirGlobals.dwSmallCandidateSet; BOOLEAN bGotPositiveCandidateSet = f->bAncestorGotPositiveCandidateSet; SearchReq * sr = &pOperation->request.searchReq; VMDIR_LOG_VERBOSE( LDAP_DEBUG_FILTER, "Filter choice: AND" ); pOperation->pBECtx->iMaxScanForSizeLimit = 0; if (pOperation->showPagedResultsCtrl == 0 && sr->sizeLimit > 0) { //pfnBEGetCandidates will try to avoid excessive index lookup when there is a sizeLimit hint // - only if there is no page control pOperation->pBECtx->iMaxScanForSizeLimit = gVmdirGlobals.dwMaxSizelimitScan; } // Look for the "special" filters that ONLY need to be processed. Currently, special filters are: // - equality match filter on a unique attribute (Priority 1) // - filter on usnChanged attribute (Priority 2) // replication issue query -b "" -s sub "usnChanged>=xxx" for (nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next) { if (nextFilter->choice == LDAP_FILTER_GE && VmDirStringCompareA( nextFilter->filtComp.ava.type.lberbv.bv_val, ATTR_USN_CHANGED, FALSE ) == 0) { specialFilter = nextFilter; continue; //keep looking for equality match filter on a unique attribute } if (nextFilter->choice == LDAP_FILTER_EQUALITY) { //look for equality match filter on a unique indexed attribute PVDIR_INDEX_CFG pIndexCfg = NULL; BOOLEAN bFoundGlobalUniqIdx = FALSE; retVal = VmDirIndexCfgAcquire( nextFilter->filtComp.ava.type.lberbv.bv_val, VDIR_INDEX_READ, &pIndexCfg); BAIL_ON_VMDIR_ERROR( retVal ); bFoundGlobalUniqIdx = pIndexCfg && pIndexCfg->bGlobalUniq; VmDirIndexCfgRelease(pIndexCfg); if (bFoundGlobalUniqIdx) { specialFilter = nextFilter; break; } } } if (specialFilter == NULL) { goto first_pass; } // We found a special filter. Just build the candidate list for this filter. specialFilter->iMaxIndexScan = 0; //special entry should return all matched entries. retVal = BuildCandidateList(pOperation, specialFilter, eStartingId); if (retVal == VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND) { retVal = LDAP_SUCCESS; } BAIL_ON_VMDIR_ERROR( retVal ); if ( specialFilter->candidates != NULL ) { if ( specialFilter->candidates->size <= iSmallCandidateSet ) { goto candidate_build_done; } bGotPositiveCandidateSet = TRUE; } first_pass: // First pass - evalute non-composite filters in the AND component // For each of the filters, we limit the number of index scan allowed. // If it exceeds this limit, we abandon it and move on to next one. // The loop stop once we have a small good positive filter/candidate. // Will not attempt second pass if we got any good positive candidate set. ////////////////////////////////////////////////////////////////////// for ( nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next ) { if ( nextFilter == specialFilter || nextFilter->choice == LDAP_FILTER_AND || nextFilter->choice == LDAP_FILTER_OR ) { continue; } nextFilter->iMaxIndexScan = iMaxIndexScan; retVal = BuildCandidateList(pOperation, nextFilter, eStartingId); BAIL_ON_VMDIR_ERROR( retVal ); if ( nextFilter->candidates != NULL && nextFilter->candidates->size >= 0 && nextFilter->candidates->positive == TRUE ) { if (nextFilter->candidates->size <= iSmallCandidateSet ) { // We have a small, positive, candidate set, stop evaluating remaining filters in the AND component goto candidate_build_done; } bGotPositiveCandidateSet = TRUE; //Continue loop looking for a better candidate set. } } // First pass - evaluate composite filters in the AND component // The loop stop once we have a small good positive filter/candidate, // otherwise, do not attempt the second pass if got any good positive candidate set. // It passes in current bGotPositiveCandidateSet to the composit filters, so that // their evaluations will not go through second pass when bGotPositiveCandidateSet is true. for ( nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next ) { if ( nextFilter->choice != LDAP_FILTER_AND && nextFilter->choice != LDAP_FILTER_OR ) { continue; } nextFilter->bAncestorGotPositiveCandidateSet = bGotPositiveCandidateSet; retVal = BuildCandidateList(pOperation, nextFilter, eStartingId); BAIL_ON_VMDIR_ERROR( retVal ); if ( nextFilter->candidates != NULL && nextFilter->candidates->size >= 0 && nextFilter->candidates->positive == TRUE) { if (nextFilter->candidates->size <= iSmallCandidateSet ) { // We have a small, positive, candidate set, stop evaluating remaining filters in the AND component goto candidate_build_done; } bGotPositiveCandidateSet = TRUE; //Continue loop looking for a better candidate set. } } if ( bGotPositiveCandidateSet ) { goto candidate_build_done; } ////////////////////////////////////////////////////////////////////// // Second pass - get here when failing to get any positive candidate set // Run through all filters w/o index scan limit. ////////////////////////////////////////////////////////////////////// for ( nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next ) { nextFilter->iMaxIndexScan = 0; retVal = BuildCandidateList(pOperation, nextFilter, eStartingId); BAIL_ON_VMDIR_ERROR( retVal ); } candidate_build_done: // First, "AND" no candidates lists or "positive" candidates lists for (nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next) { if (nextFilter->candidates == NULL || nextFilter->candidates->positive == TRUE) { AndFilterResults( nextFilter, f); } } // Second, "AND" "negative" candidates lists. for (nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next) { if (nextFilter->candidates != NULL && nextFilter->candidates->positive == FALSE) { AndFilterResults( nextFilter, f); } } break; } case LDAP_FILTER_OR: VMDIR_LOG_VERBOSE( LDAP_DEBUG_FILTER, "Filter choice: OR" ); for (nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next) { //nextFilter inherents the bAncestorGotPositiveCandidateSet nextFilter->bAncestorGotPositiveCandidateSet = f->bAncestorGotPositiveCandidateSet; retVal = BuildCandidateList(pOperation, nextFilter, eStartingId); BAIL_ON_VMDIR_ERROR( retVal ); } // First, "OR" no candidates lists or "positive" candidates lists for (nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next) { if (nextFilter->candidates == NULL || nextFilter->candidates->positive == TRUE) { OrFilterResults( nextFilter, f); } } /// Second, "OR" "negative" candidates lists. for (nextFilter = f->filtComp.complex; nextFilter != NULL; nextFilter = nextFilter->next) { if (nextFilter->candidates != NULL && nextFilter->candidates->positive == FALSE) { OrFilterResults( nextFilter, f); } } break; case LDAP_FILTER_NOT: VMDIR_LOG_VERBOSE( LDAP_DEBUG_FILTER, "Filter choice: NOT" ); #if 0 nextFilter = f->filtComp.complex; retVal = BuildCandidateList( pOperation, nextFilter); BAIL_ON_VMDIR_ERROR( retVal ); NotFilterResults( nextFilter, f ); #endif //The nextFilter above may contain a super set of valid candidates //substracting it from filter f may result in an incorrect result set. //For now, simply set the destination filter to FILTER_RES_TRUE. // Ignore candidate building on filtComp.complex for NOT filter. // We rely on final pass to qualify NOT filter. f->computeResult = FILTER_RES_TRUE; break; case LDAP_FILTER_EQUALITY: case LDAP_FILTER_SUBSTRINGS: case FILTER_ONE_LEVEL_SEARCH: case LDAP_FILTER_GE: case LDAP_FILTER_LE: retVal = pOperation->pBEIF->pfnBEGetCandidates(pOperation->pBECtx, f, eStartingId); if (retVal != 0) { if ( retVal == VMDIR_ERROR_BACKEND_ENTRY_NOTFOUND ) // SJ-TBD: What about DB_DEADLOCK error ?? { retVal = LDAP_SUCCESS; } } else if (f->candidates) { PSTR pszCand = NULL; PSTR pszCandList = NULL; retVal = _GetFilterCandidateLabel(f, &pszCand); BAIL_ON_VMDIR_ERROR( retVal ); retVal = VmDirAllocateStringPrintf(&pszCandList, "%s%s%s:%d", pOperation->pszFilters ? pOperation->pszFilters : "", pOperation->pszFilters ? ", " : "", VDIR_SAFE_STRING(pszCand), f->candidates->size); BAIL_ON_VMDIR_ERROR( retVal ); VMDIR_SAFE_FREE_STRINGA(pOperation->pszFilters); pOperation->pszFilters = pszCandList; } BAIL_ON_VMDIR_ERROR( retVal ); break; case LDAP_FILTER_PRESENT: VMDIR_LOG_VERBOSE( LDAP_DEBUG_FILTER, "Filter choice: PRESENT" ); f->computeResult = FILTER_RES_TRUE; // SJ-TBD: Just for now, not always, needs to be looked into f->candidates = NULL; break; default: break; } cleanup: return retVal; error: VMDIR_LOG_ERROR( VMDIR_LOG_MASK_ALL, "%s,%d failed, error(%d)", __FUNCTION__, __LINE__, retVal ); goto cleanup; }
int VmDirInternalSearch( PVDIR_OPERATION pOperation ) { int retVal = LDAP_SUCCESS; ENTRYID eId = 0; int deadLockRetries = 0; BOOLEAN bHasTxn = FALSE; PSTR pszLocalErrMsg = NULL; PVDIR_LDAP_RESULT pResult = &(pOperation->ldapResult); assert(pOperation && pOperation->pBEIF); // Normalize (base) DN retVal = VmDirNormalizeDN( &(pOperation->reqDn), pOperation->pSchemaCtx ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DN normalization failed - (%u)(%s)", retVal, VDIR_SAFE_STRING(VmDirSchemaCtxGetErrorMsg(pOperation->pSchemaCtx)) ); if (VmDirHandleSpecialSearch( pOperation, pResult )) // TODO, add &pszLocalErrMsg { retVal = pResult->errCode; BAIL_ON_VMDIR_ERROR_WITH_MSG(retVal, pszLocalErrMsg, "Special search failed - (%u)", retVal); goto cleanup; // done special search } if (pOperation->syncReqCtrl != NULL) // Replication { pOperation->lowestPendingUncommittedUsn = pOperation->pBEIF->pfnBEGetLeastOutstandingUSN( pOperation->pBECtx, FALSE ); VmDirLog( LDAP_DEBUG_REPL, "Replication request USN (%u)", pOperation->lowestPendingUncommittedUsn ); } // If base is not ROOT, read lock the base object (DnToEntryId index entry) to make sure it exists, and it does // not get deleted during this search processing. if (pOperation->reqDn.lberbv.bv_len != 0) { // ************************************************************************************ // transaction retry loop begin. make sure all function within are retry agnostic. // ************************************************************************************ txnretry: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort( pOperation->pBECtx ); bHasTxn = FALSE; } deadLockRetries++; if (deadLockRetries > MAX_DEADLOCK_RETRIES) { retVal = VMDIR_ERROR_LOCK_DEADLOCK; BAIL_ON_VMDIR_ERROR( retVal ); } else { retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_READ ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; // Lookup in the DN index. retVal = pOperation->pBEIF->pfnBEDNToEntryId( pOperation->pBECtx, &(pOperation->reqDn), &eId ); if (retVal != 0) { switch (retVal) { case VMDIR_ERROR_BACKEND_DEADLOCK: goto txnretry; // Possible retry. default: BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "DNToEID (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); } } } // ************************************************************************************ // transaction retry loop end. // ************************************************************************************ } // start txn if not has one already. if (! pOperation->pBECtx->pBEPrivate) { retVal = pOperation->pBEIF->pfnBETxnBegin( pOperation->pBECtx, VDIR_BACKEND_TXN_READ ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn begin (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = TRUE; } retVal = AppendDNFilter( pOperation ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Appending DN filter failed."); retVal = BuildCandidateList( pOperation, pOperation->request.searchReq.filter); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "BuildCandidateList failed."); if (pOperation->request.searchReq.filter->computeResult == FILTER_RES_TRUE) { retVal = VMDIR_ERROR_UNWILLING_TO_PERFORM; BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "Full scan of Entry DB is required. Refine your search."); } retVal = ProcessCandidateList( pOperation ); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "ProcessCandidateList failed. (%u)(%s)", retVal, VDIR_SAFE_STRING( pOperation->ldapResult.pszErrMsg) ); retVal = pOperation->pBEIF->pfnBETxnCommit( pOperation->pBECtx); BAIL_ON_VMDIR_ERROR_WITH_MSG( retVal, pszLocalErrMsg, "txn commit (%u)(%s)", retVal, VDIR_SAFE_STRING(pOperation->pBEErrorMsg)); bHasTxn = FALSE; cleanup: VMDIR_SAFE_FREE_MEMORY( pszLocalErrMsg ); return retVal; error: if (bHasTxn) { pOperation->pBEIF->pfnBETxnAbort( pOperation->pBECtx ); } VMDIR_SET_LDAP_RESULT_ERROR( &(pOperation->ldapResult), retVal, pszLocalErrMsg); goto cleanup; }