Example #1
0
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;
}
Example #2
0
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;
}
Example #3
0
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;
}
Example #4
0
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;
}