Skip Headers
Oracle® Fusion Middleware Developer's Guide for Oracle Directory Server Enterprise Edition
11g Release 1 (11.1.1.7.0)

Part Number E28970-01
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

10 Writing Matching Rule Plug-Ins

This chapter covers plug-ins that enable Directory Server to handle custom matching rules. The server requires such matching rule plug-ins to support LDAP v3 extensible matching, such as "sounds like" searches.

Code excerpts in this chapter demonstrate a conceptually simple case exact matching scheme. Here, exact matching is a byte comparison between DirectoryString attribute values. Despite the straightforward concept, the plug-in code runs to many lines. The plug-in must provide several functions as well as wrapper functions to enable indexing, searching, and sorting.

Note:

You must understand how Directory Server uses matching rule plug-ins before trying to implement your own plug-in. Read this chapter, then read the sample plug-in code. Avoid creating a new matching rule plug-in from scratch.

This chapter covers the following topics:

10.1 How Matching Rule Plug-Ins Work

This section summarizes what matching rule plug-ins are and how Directory Server handles them.

10.1.1 What a Matching Rule Is

A matching rule defines a specific way to compare attribute values that have a given syntax. In other words, a matching rule defines how potentially matching attributes are compared.

Every matching rule is identified by a unique object identifier (OID) string. A client application that requests a search can specify the matching rule OID in the search filter. The OID indicates to Directory Server how to check for a match of two attribute values.

In practice, a client application might want only entries with attribute values that match the value provided exactly. The sample plug-in demonstrates how you might implement a solution for that case. Another client might want to sort entries according to the rules for a given locale. Directory Server actually uses a matching rule plug-in to handle locale—specific matching.

Chapter 14, Directory Server Internationalization Support, in Reference for Oracle Directory Server Enterprise Edition includes a list of matching rules. Directory Server supports the rules for internationalized searches. You can also view the list by searching the default schema.

$ ldapsearch -h localhost -p 1389 -b cn=schema cn=schema matchingRules

10.1.2 Requesting a Matching Rule

To request a custom matching rule on a specific attribute, a client application includes the matching rule OID in the search filter. LDAP v3 calls these search filters extensible match filters. An extensible match filter looks like the following:

(cn:2.5.13.5:=Quentin)

This filter tells the server to search for Quentin in the common name (CN) of the entries by using matching rule 2.5.13.5. The matching rule happens to be a case exact match.

The case exact matching rule plug-in OID 2.5.13.5 enables Directory Server to perform the search correctly. Directory Server calls code in this matching rule plug-in to check for matches during a search. The server also uses the code to generate indexes that accelerate case exact searches. The indexes also help to sort entries found during such searches.

10.1.3 What a Matching Rule Plug-In Does

A matching rule plug-in can provide code to do the following:

  • Check for matches during an extensible match search

  • Sort results for an extensible match search by using a sort control

  • Maintain an index to speed an extensible match search (optional)

To enable these capabilities, the plug-in implements matching and indexing routines that Directory Server calls to handle requests involving the particular matching rule.

The plug-in also implements factory functions that specify which routine to call when handling a particular matching rule. As a result, a plug-in can support multiple matching rules. Yet, plug-ins implementing only one matching rule also require factory function code to wrap indexing and matching routines. A plug-in therefore requires many lines of code and several functions to handle even a minimal matching rule.

The following table shows all the functions that a matching rule plug-in can implement.

Table 10-1 Functions Defined in Matching Rule Plug-Ins

Type Parameter Block Identifier Required?

Filter factory

SLAPI_PLUGIN_MR_FILTER_CREATE_FN

Required

Filter index, used to check an index for matches

SLAPI_PLUGIN_MR_FILTER_INDEX_FN

Not required

Filter match

SLAPI_PLUGIN_MR_FILTER_MATCH_FN

Required

Filter match reset

SLAPI_PLUGIN_MR_FILTER_RESET_FN

If filter must be reset for reuse

Filter object destructor

SLAPI_PLUGIN_DESTROY_FN

If needed to free memory

Indexer

SLAPI_PLUGIN_MR_INDEX_FN

Not required

Indexer factory

SLAPI_PLUGIN_MR_INDEXER_CREATE_FN

Not required

Indexer object destructor

SLAPI_PLUGIN_DESTROY_FN

If needed to free memory

Plug-in initialization function

Not applicable, but instead specified in configuration settings

Required

Server shutdown (cleanup) function

SLAPI_CLOSE_FN

Not required

Server startup function

SLAPI_START_FN

Not required


Refer to Plug-In API Reference for details about parameter block identifiers that you can use with matching rule plug-ins.

10.2 Example Matching Rule Plug-In

The example code cited in this chapter, from install-path/examples/matchingrule.c, mimics functionality installed by default with Directory Server. For this reason, you do not need to build and load the plug-in unless you modify the plug-in to implement your own matching rule. The example uses a different OID from the plug-in that is provided with Directory Server. Therefore, the sample plug-in does not interfere with the existing plug-in if you do choose to load the sample.

10.2.1 Configuring Matching Rule Plug-Ins

Matching rule plug-ins have type matchingrule. Directory Server plug-ins can depend on matching rule plug-ins by type. Directory Server cannot load plug-ins unless plug-ins of the type the plug-ins depend on load without error.

Refer to Plugging Libraries Into Directory Server for details on loading plug-in configuration entries into Directory Server.

10.2.2 Registering Matching Rule Plug-Ins

Matching rule plug-ins include factory functions. Factory functions provide function pointers to the indexing or filter match routines dynamically. You therefore register only the factory functions as part of the plug-in initialization function.

A plug-in that handles both indexing and matching registers both factory functions and a description, as shown in the following example.

Example 10-1 Registering Matching Rule Factory Functions (matchingrule.c)

#include "slapi-plugin.h"

static Slapi_PluginDesc plg_desc = {
    "caseExactMatchingRule",           /* Plug-in identifier           */
    "Oracle Corporation (test)",       /* Vendor name                  */
    "11.1.1.3.0",                      /* Plug-in revision number      */
    "Case Exact Matching Rule plug-in" /* Plug-in description          */
};

#ifdef _WIN32
__declspec(dllexport)
#endif
int
plg_init(Slapi_PBlock * pb)
{
    int                       rc;
        
    /* Matching rule factory functions are registered using
     * the parameter block structure. Other functions are then
     * registered dynamically by the factory functions.
     *
     * This means that a single matching rule plug-in may handle
     * a number of different matching rules.                           */
    rc  = slapi_pblock_set(
        pb,
        SLAPI_PLUGIN_MR_INDEXER_CREATE_FN,
        (void *)plg_indexer_create
    );
    rc |= slapi_pblock_set(
        pb,
        SLAPI_PLUGIN_MR_FILTER_CREATE_FN,
        (void *)plg_filter_create
    );
    rc |= slapi_pblock_set(
        pb,
        SLAPI_PLUGIN_DESCRIPTION,
        (void *)&plg_desc
    );

    /* Register the matching rules themselves. */

    return rc;
}

Here, plg_init() is the plug-in initialization function, plg_indexer_create() is the indexer factory, plg_filter_create () is the filter factory, and plg_desc is the plug-in description structure.

If your plug-in uses private data, set SLAPI_PLUGIN_PRIVATE in the parameter block as a pointer to the private data structure.

Example 10-2 Registering a Matching Rule (matchingrule.c)

Register matching rules by using slapi_matchingrule_register() as shown here rather than using slapi_pblock_set() in the initialization function.

#include "slapi-plugin.h"

#define PLG_DESC      "Case Exact Matching on Directory String" \
                      " [defined in X.520]"
#define PLG_NAME      "caseExactMatch"
/* This OID is for examples only and is not intended for reuse. The
 * official OID for this matching rule is 2.5.13.5.                    */
#define PLG_OID       "1.3.6.1.4.1.42.2.27.999.4.1"

#ifdef _WIN32
__declspec(dllexport)
#endif
int
plg_init(Slapi_PBlock * pb)
{
    Slapi_MatchingRuleEntry * mrentry = slapi_matchingrule_new();
    int                       rc;
        
    /* Register matching rule factory functions.*/

    /* Matching rules themselves are registered using a
     * Slapi_MatchingRuleEntry structure, not the parameter
     * block structure used when registering plug-in functions.
     *
     * This plug-in registers only one matching rule. Yours
     * may register many matching rules.                               */
    rc |= slapi_matchingrule_set(
        mrentry,
        SLAPI_MATCHINGRULE_DESC,
        (void *)slapi_ch_strdup(PLG_DESC)
    );
    rc |= slapi_matchingrule_set(
        mrentry,
        SLAPI_MATCHINGRULE_NAME,
        (void *)slapi_ch_strdup(PLG_NAME)
    );
    rc |= slapi_matchingrule_set(
        mrentry,
        SLAPI_MATCHINGRULE_OID,
        (void *)slapi_ch_strdup(PLG_OID)
    );
    rc |= slapi_matchingrule_set(
        mrentry,
        SLAPI_MATCHINGRULE_SYNTAX,     /* Here you use DirectoryString.*/
        (void *)slapi_ch_strdup("1.3.6.1.4.1.1466.115.121.1.15")
    );
    rc |= slapi_matchingrule_register(mrentry);
    slapi_matchingrule_free(&mrentry, 1);

    return rc;
}
  • PLG_DESC is a string that describes the matching rule.

  • PLG_NAME is a string identifier for the matching rule.

  • PLG_OID is the object identifier for the matching rule.

    The sample plug-in #defines every item. If the plug-in implements several matching rules, the initialization function must register each item.

    Notice that plug-ins must register factory functions separately, using slapi_pblock_set().

10.3 Handling Extensible Match Filters

This section explains and demonstrates how to handle extensible match filters corresponding to a matching rule. All matching rule plug-ins must enable filter matching.

10.3.1 How Directory Server Handles Extensible Match Searches

The following process describes how Directory Server handles an extensible match search.

  1. When Directory Server receives a search request containing an extensible match filter, the server calls the filter factory function in the plug-in handling the corresponding matching rule. The server passes the filter factory a parameter block containing the extensible match filter information.

  2. The filter factory function builds a filter object. The function then sets pointers to the object as well as the appropriate filter matching and filter index functions.

  3. The server attempts to look up a set of results in an index rather than searching the entire directory.

    In order to read an index, the server calls the filter index function, which helps the server locate the appropriate indexer function.

    Directory Server only considers all entries in the directory, which can slow the search considerably if one of the following is the case:

    • No existing index applies to the search

    • The plug-in specifies no indexer function

    • The plug-in generates no keys for the values specified

    Directory Server calls the indexer function to generate the keys. Refer to Indexer Function for details about that function.

  4. Directory Server builds a list of candidate entries.

  5. Directory Server verifies that the entry is in the scope of the search before returning the entry to the client as a search result.

  6. Directory Server frees memory that was allocated for the operation.

The following figure shows how Directory Server processes the search request.

Figure 10-1 Directory Server Performing an Extensible Match Search

Description of Figure 10-1 follows
Description of "Figure 10-1 Directory Server Performing an Extensible Match Search"

The main filter functions are as follows:

10.3.2 Filter Matching Function

The filter matching function takes pointers to the filter object, the entry, and the first attribute to check for a match.

The following figure shows how Directory Server uses the filter matching function.

Figure 10-2 Filter Match Function Context

Description of Figure 10-2 follows
Description of "Figure 10-2 Filter Match Function Context"

Notice that the filter matching function returns 0 (match) -1 (no match) or an LDAP error code for each entry processed.

Example 10-3 Filter Match Function (matchingrule.c)

This example shows the filter match function for the case exact matching rule where a match occurs when the two berval structures match.

#include "slapi-plugin.h"

typedef struct plg_filter_t            /* For manipulating filter obj. */
{   char          *  f_type;           /* Attribute type to match      */
    int              f_op;             /* Type of comparison
                                        * (<, <=, ==,>=,>, substr)
                                        * for the filter.              */
    struct berval ** f_values;         /* Array of values to match     */
} plg_filter_t;

/* Check for a match against the filter.
 * Returns:  0 filter matched
 *          -1 filter did not match
 *        > 0 an LDAP Error code                                      */
static int
plg_filter_match(
    void        * obj,                 /* Matching rule object         */
    Slapi_Entry * entry,               /* Entry to match               */
    Slapi_Attr  * attr                 /* Attributes to match          */
)
{
    plg_filter_t  * fobj  = (plg_filter_t *)obj;
    struct berval * mrVal = NULL;      /* Values handled as bervals... */
    Slapi_Value   * sval;              /* ...and as Slapi_Value structs*/
    int             rc    = -1;        /* No match                     */

    if (fobj && fobj->f_type && fobj->f_values && fobj->f_values[0]) {

        mrVal = fobj->f_values[0];

        /* Iterate through the attributes, matching subtypes.          */
        for (; attr != NULL; slapi_entry_next_attr(entry, attr, &attr)) {

            char * type = NULL;        /* Attribute type to check      */

            if ((slapi_attr_get_type(attr, &type) == 0) &&
                (type != NULL)                          &&
                (slapi_attr_type_cmp(fobj->f_type, type, 2) == 0)
            ) { /* slapi_attr_type_cmp(type1, type2, ==>2<==)
                 * matches subtypes, too. Refer to the reference
                 * documentation for details.                          */

                /* Type and subtype match, so iterate through the
                 * values of the attribute.                            */
                int hint = slapi_attr_first_value(attr, &sval);
                while (hint != -1) {
                    const struct berval * val =
                        slapi_value_get_berval(sval);

                    /* The case exact matching rule
                     * compares the two bervals.
                     *
                     * Your matching rule may do
                     * lots of different checks here.                  */
                    rc = slapi_berval_cmp(val, mrVal);
                    if (rc == 0)  {    /* Successful match             */
                    /* If you have allocated memory for a custom
                     * matching rule, do not forget to release it.     */
                        return 0;
                    }

                    hint = slapi_attr_next_value(attr, hint, &sval);
                }
            }
        }
    }
    return rc;
}

Notice that checking for a case exact match involves only slapi_berval_cmp (), which performs a byte by byte comparison. Your plug-in might do something more complex for the comparison.

10.3.2.1 Subtype Matches

Notice in the code for iterating through the attributes that slapi_attr_type_cmp () takes 2, the value assigned to SLAPI_TYPE_CMP_SUBTYPE, as its third argument. The argument forces a comparison of attribute subtypes, such as the locale of an attribute, in addition to attribute types. Refer to Plug-In API Reference for details on plug-in API functions and their arguments.

10.3.2.2 Thread Safety and Filter Matching Functions

Directory Server never calls a filter matching function for the same filter object concurrently. Directory Server can, however, call the function concurrently for different filter objects. If you use global variables, ensure that your filter matching function handles such variables safely.

10.3.3 Filter Index Function

The filter index function takes a parameter block from Directory Server. The function also sets pointers in the parameter block, enabling the server to read the index.

Example 10-4 Filter Index Function (matchingrule.c)

This example shows the filter index function for the case exact matching rule where the keys are the same as the values.

#include "slapi-plugin.h"

#define PLG_OID       "1.3.6.1.4.1.42.2.27.999.4.1"
#define SUBSYS        "CaseExactMatching Plugin"

typedef struct plg_filter_t            /* For manipulating filter obj. */
{   char          *  f_type;           /* Attribute type to match      */
    int              f_op;             /* Type of comparison
                                        * (<, <=, ==,>=,>, substr)
                                        * for the filter.              */
    struct berval ** f_values;         /* Array of values to match     */
} plg_filter_t;

static int
plg_filter_index(Slapi_PBlock * pb)
{
    int            rc       = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
    void         * obj      = NULL;    /* Server lets the plug-in      */
    plg_filter_t * fobj;               /* handle the object type.      */
    int            query_op = SLAPI_OP_EQUAL; /* Only exact matches    */
        
    if (!slapi_pblock_get(pb, SLAPI_PLUGIN_OBJECT, &obj)) {

        fobj = (plg_filter_t *)obj;

        /* Case exact match requires no modifications to
         * the at this point. Your plug-in may however
         * modify the object, then set it again in the
         * parameter block.                                            */
        rc  = slapi_pblock_set(        /* This is for completeness.    */
            pb,                        /* Your plug-in may modify      */
            SLAPI_PLUGIN_OBJECT,       /* the object if necessary.     */
            fobj
        );
        rc |= slapi_pblock_set(        /* Set attr type to match.      */
            pb,
            SLAPI_PLUGIN_MR_TYPE,
            fobj->f_type
        );
        rc |= slapi_pblock_set(        /* Set fcn to obtain keys.      */
            pb,
            SLAPI_PLUGIN_MR_INDEX_FN,
            (void*)plg_index_entry
        );
        rc |= slapi_pblock_set(        /* Set values to obtain keys.   */
            pb,
            SLAPI_PLUGIN_MR_VALUES,
            fobj->f_values
        );
        rc |= slapi_pblock_set(        /* This is for completeness.    */
            pb,                        /* Your plug-in may set a       */
            SLAPI_PLUGIN_MR_OID,       /* different MR OID than the    */
            PLG_OID                    /* one in the parameter block   */
        );
        rc |= slapi_pblock_set(        /* <, <=, ==,>=,>, substr     */
            pb,                        /* In this case, ==             */
            SLAPI_PLUGIN_MR_QUERY_OPERATOR,
            &query_op
        );
    }

    return rc;
}

This code uses the variables and macros that follow:

  • fobj->ftype indicates the attribute type that is specified in the filter.

  • plg_index_entry() indicates the indexer function for generating keys from values.

  • fobj->values points to the berval array of attribute values.

  • PLG_OID is the object identifier of the matching rule.

  • query_op indicates the query operator from the filter.

10.3.3.1 Input Parameters for Filter Index Functions

A filter index function can get a pointer to the filter object from SLAPI_PLUGIN_OBJECT in the parameter block.

10.3.3.2 Output Parameters for Filter Index Functions

A filter index function should set values for at least the following in the parameter block before returning control to Directory Server:

  • SLAPI_PLUGIN_MR_INDEX_FN, the pointer to the indexer for generating the keys

  • SLAPI_PLUGIN_MR_OID

  • SLAPI_PLUGIN_MR_QUERY_OPERATOR

  • SLAPI_PLUGIN_MR_TYPE

  • SLAPI_PLUGIN_MR_VALUES

  • SLAPI_PLUGIN_OBJECT, if modified by your function

Refer to Chapter 17, "Parameter Block Reference" for details.

10.3.3.3 Thread Safety and Filter Index Functions

Directory Server never calls a filter index function for the same filter object concurrently. Directory Server can, however, call the function concurrently for different filter objects. If you use global variables, ensure that your function handles such variables safely.

10.3.4 Filter Factory Function

The filter factory function takes a parameter block from Directory Server. The function then sets parameters such that Directory Server can build a list of candidate entries, candidates the server checks for matches.

The following figure shows how the filter factory function operates.

Figure 10-3 Filter Factory Function Context

Description of Figure 10-3 follows
Description of "Figure 10-3 Filter Factory Function Context"

The following example shows the filter factory function for the case exact matching rule.

Example 10-5 Filter Factory Function (matchingrule.c)

#include "slapi-plugin.h"

#define PLG_OID       "1.3.6.1.4.1.42.2.27.999.4.1"
#define SUBSYS        "CaseExactMatching Plugin"

/* Functions to obtain connection information for logging.             */
static long mypblock_get_msgid( Slapi_PBlock * pb);
static int  mypblock_get_connid(Slapi_PBlock * pb);
static int  mypblock_get_opid(  Slapi_PBlock * pb);

typedef struct plg_filter_t            /* For manipulating filter obj. */
{   char          *  f_type;           /* Attribute type to match      */
    int              f_op;             /* Type of comparison
                                        * (<, <=, ==,>=,>, substr)
                                        * for the filter.              */
    struct berval ** f_values;         /* Array of values to match     */
} plg_filter_t;

static int
plg_filter_create(Slapi_PBlock * pb)
{
    int             rc       = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
    char          * mr_oid   = NULL;   /* MR OID from the server       */
    char          * mr_type  = NULL;   /* Attr type to match           */
    struct berval * mr_value = NULL;   /* Attr value to match          */
    plg_filter_t  * fobj     = NULL;   /* Object to create             */
        
    if (
        slapi_pblock_get(pb, SLAPI_PLUGIN_MR_OID, &mr_oid) ||
        (mr_oid == NULL)
    ) {
        slapi_log_error_ex(
            -1, /* errorId */
            mypblock_get_msgid(pb),
            mypblock_get_connid(pb),
            mypblock_get_opid(pb),
            SUBSYS,
            SLAPI_INTERNAL_ERROR,
            "plg_filter_create failed: NULL OID values are invalid.\n"
        );
        }
    else if (strcmp(mr_oid, PLG_OID) == 0) {
        /* The MR OID from the server is handled by this plug-in.      */
        if (
            (slapi_pblock_get(pb, SLAPI_PLUGIN_MR_TYPE,  &mr_type) == 0)  &&
            (mr_type  != NULL)                                            &&
            (slapi_pblock_get(pb, SLAPI_PLUGIN_MR_VALUE, &mr_value) == 0) &&
            (mr_value != NULL)
        ) { /* ...provide a pointer to a filter match function.        */
            int op            = SLAPI_OP_EQUAL;
            fobj              = (plg_filter_t *)slapi_ch_calloc(
                                    1,
                                    sizeof (plg_filter_t)
                                );
            fobj->f_type      = slapi_ch_strdup(mr_type);
            fobj->f_op        = op;
            fobj->f_values    = (struct berval **)slapi_ch_malloc(
                                    2 * sizeof(struct berval *)
                                );
            fobj->f_values[0] = slapi_ch_bvdup(mr_value);
            fobj->f_values[1] = NULL;

            rc  = slapi_pblock_set(        /* Set object destructor.   */
                pb,
                SLAPI_PLUGIN_DESTROY_FN,
                (void *)plg_filter_destroy
            );
            rc |= slapi_pblock_set(        /* Set object itself.       */
                pb,
                SLAPI_PLUGIN_OBJECT,
                (void  *)fobj
            );
            rc |= slapi_pblock_set(        /* Set filter match fcn.    */
                pb,
                SLAPI_PLUGIN_MR_FILTER_MATCH_FN,
                (void *)plg_filter_match
            );
            rc |= slapi_pblock_set(        /* Set sorting function.    */
                pb,
                SLAPI_PLUGIN_MR_FILTER_INDEX_FN,
                (void *)plg_filter_index
            );

            if (rc == 0) {
                slapi_log_info_ex(
                    SLAPI_LOG_INFO_AREA_PLUGIN,
                    SLAPI_LOG_INFO_LEVEL_DEFAULT,
                    mypblock_get_msgid(pb),
                    mypblock_get_connid(pb),
                    mypblock_get_opid(pb),
                    SUBSYS,
                    "plg_filter_create (oid %s; type %s) OK\n",
                    mr_oid, mr_type
                );
            } else {
                slapi_log_error_ex(
                    -1, /* errorId */
                    mypblock_get_msgid(pb),
                    mypblock_get_connid(pb),
                    mypblock_get_opid(pb),
                    SUBSYS,
                    SLAPI_INTERNAL_ERROR,
                    "plg_filter_create (oid %s; type %s) - pblock error \n",
                    mr_oid, mr_type
                );
            }
        } else { /* Missing parameters in the pblock                   */
            slapi_log_error_ex(
                -1,
                mypblock_get_msgid(pb),
                mypblock_get_connid(pb),
                mypblock_get_opid(pb),
                SUBSYS,
                "Parameter errors ",
                "plg_filter_create: invalid input pblock.\n"
            );
        }
    }

    return rc;
}

Notice that this function returns LDAP_UNAVAILABLE_CRITICAL_EXTENSION if the function does not handle the matching rule OID in the parameter block. Refer to Handling an Unknown Matching Rule.

10.3.4.1 Input Parameters for Filter Factory Functions

A filter factory function can get values for the following from the parameter block:

  • SLAPI_PLUGIN_MR_OID

  • SLAPI_PLUGIN_MR_TYPE

  • SLAPI_PLUGIN_MR_VALUE

  • SLAPI_PLUGIN_PRIVATE, if your plug-in uses private data that is specified in the plug-in initialization function

10.3.4.2 Output Parameters for Filter Factory Functions

An indexer factory function should set values for the following in the parameter block before returning control to Directory Server:

  • SLAPI_PLUGIN_DESTROY_FN, a pointer to the destructor for the filter object

  • SLAPI_PLUGIN_MR_FILTER_INDEX_FN, a pointer to the filter index function

  • SLAPI_PLUGIN_MR_FILTER_MATCH_FN, a pointer to the filter match function

  • SLAPI_PLUGIN_MR_FILTER_RESET_FN, if required, a pointer to the filter reset function

  • SLAPI_PLUGIN_MR_FILTER_REUSABLE, if required to specify that the filter can be reused

  • SLAPI_PLUGIN_OBJECT, a pointer to the filter object

Refer to Chapter 17, "Parameter Block Reference" for details.

10.3.4.3 Thread Safety and Filter Factory Functions

This function must be thread safe. Directory Server can call this function concurrently.

10.3.5 Filter Object Destructor Function

A filter object destructor function frees memory that was allocated for a filter object set up by the filter factory function. Directory Server calls the destructor after the operation completes, passing the parameter block indicating the filter object as the value of SLAPI_PLUGIN_OBJECT.

Directory Server never calls a destructor for the same object concurrently.

Example 10-6 Filter Object and Destructor (matchingrule.c)

This code shows an example object and destructor.

#include "slapi-plugin.h"

typedef struct plg_filter_t            /* For manipulating filter obj. */
{   char          *  f_type;           /* Attribute type to match      */
    int              f_op;             /* Type of comparison
                                        * (<, <=, ==,>=,>, substr)
                                        * for the filter.              */
    struct berval ** f_values;         /* Array of values to match     */
} plg_filter_t;


/* Free memory allocated for the filter object.                        */
static int
plg_filter_destroy(Slapi_PBlock * pb)
{
    void         * obj  = NULL;        /* Server lets the plug-in      */
    plg_filter_t * fobj = NULL;        /* handle the object type.      */

    if (!slapi_pblock_get(pb, SLAPI_PLUGIN_OBJECT, &obj))
    {
        fobj = (plg_filter_t *)obj;
        if (fobj){
            slapi_ch_free((void **)&fobj->f_type);
                if (fobj->f_values){
                    ber_bvecfree(fobj->f_values);
                }
            slapi_ch_free((void **)&fobj);
        }
    }
    return 0;
}

10.4 Indexing Entries According to a Matching Rule

This section covers how to provide plug-in support for a directory index that is based on a matching rule. Matching rule plug-ins are not required to enable indexing for the matching rules the plug-ins support.

Note:

Directory Server can use your plug-in for extensible match searches even if you provide no indexing capability.

Without support for indexing, however, Directory Server must generate search results from the entire directory, which severely impacts search performance.

Directory indexes speed up client searches. Directory Server can build a list of search result entries by looking through the index rather than the entire directory. When directories contain many entries, indexes offer a considerable search performance boost. Directory administrators therefore configure the directory to maintain indexes for attributes that are searched regularly.

10.4.1 How Directory Server Handles the Index

To maintain an index for a custom matching rule supported by your plug-in, the server requires an indexer function. The indexer function is capable of translating attribute values to index keys. Directory Server calls the indexer function to create the index, and subsequently when adding, modifying, or deleting attribute values, because such changes can affect the index.

To read an index for a custom matching rule, Directory Server requires a filter index function. The filter factory function provides a pointer to this function. Refer to Handling Extensible Match Filters and Filter Index Function for details.

Directory Server relies on the indexer factory function in your plug-in to set up an indexing object and provide a pointer to the appropriate indexer function.

The following figure shows how Directory Server performs indexing based on a matching rule.

Figure 10-4 Directory Server Maintaining an Index Using a Matching Rule

Description of Figure 10-4 follows
Description of "Figure 10-4 Directory Server Maintaining an Index Using a Matching Rule"

The following summarizes the process shown in Figure 10-4.

  1. Directory Server creates a parameter block, setting parameters to indicate the matching rule OID and attribute type to match. Directory Server then calls the indexer factory function.

  2. The indexer factory function examines the parameter block and allocates an indexer object if necessary. The function also sets pointers to the indexer and filter index functions. If necessary, the function sets a pointer to a destructor to free the indexer object before returning control to Directory Server.

  3. Directory Server sets the parameter block to indicate attribute values to translate to keys and calls the indexer function.

  4. The indexer function translates values to keys. After the indexer returns, Directory Server uses the keys to update the index.

  5. Directory Server frees the parameter block. If necessary, the server frees the indexer object by using the destructor provided.

Be aware that Directory Server can call indexer factory functions concurrently. Indexer factory functions must therefore be thread safe.

The main filter functions are as follows:

10.4.2 Indexer Function

An indexer function takes a parameter block from Directory Server that contains a pointer to a berval array of attribute values. The function then generates a berval array of corresponding keys from the values. Finally, the function sets a pointer in the parameter block to the keys.

The following figure shows how Directory Server uses the indexer function.

Figure 10-5 Indexer Function Context

Description of Figure 10-5 follows
Description of "Figure 10-5 Indexer Function Context"

The following example shows the indexer function for the case exact matching rule where the keys are the same as the values.

Example 10-7 Indexer Function (matchingrule.c)

#include "slapi-plugin.h"

typedef struct plg_filter_t            /* For manipulating filter obj. */
{   char          *  f_type;           /* Attribute type to match      */
    int              f_op;             /* Type of comparison
                                        * (<, <=, ==,>=,>, substr)
                                        * for the filter.              */
    struct berval ** f_values;         /* Array of values to match     */
} plg_filter_t;

static int
plg_index_entry(Slapi_PBlock * pb)
{
    int              rc     = LDAP_OPERATIONS_ERROR;
    void          *  obj    = NULL;    /* Server lets the plug-in      */
    plg_filter_t  *  fobj;             /* handle the object type.      */
    struct berval ** values;           /* Values from server           */
        
    if (slapi_pblock_get(pb, SLAPI_PLUGIN_OBJECT, &obj) == 0) {
        fobj = (plg_filter_t *)obj;
        if (
            (slapi_pblock_get(pb, SLAPI_PLUGIN_MR_VALUES, &values) == 0) &&
            (values != NULL)
        ) {
                
            /* The case exact match builds the index keys
             * from the values by copying the values because
             * the keys and values are the same.
             *
             * Your matching rule may do something quite
             * different before setting the keys associated
             * with the values in the parameter block.                 */
            rc = slapi_pblock_set(     /* Set keys based on values.    */
                pb,
                SLAPI_PLUGIN_MR_KEYS,
                slapi_ch_bvecdup(values)
            );
        }
    }

    return rc;
}

Here, obj points to the indexer object, and values points to the berval array of attribute values. Notice that the function returns LDAP_OPERATIONS_ERROR on failure. Technically, LDAP_OPERATIONS_ERROR indicates bad sequencing of LDAP operations. For historical reasons, the error is used in this context to indicate an internal error.

10.4.2.1 Input Parameters for Indexers

An indexer function can get values for the following from the parameter block:

  • SLAPI_PLUGIN_MR_VALUES

  • SLAPI_PLUGIN_MR_OBJECT

10.4.2.2 Output Parameter for Indexers

An indexer function should generate the berval array of keys. The function should set SLAPI_PLUGIN_MR_KEYS in the parameter block before returning control to Directory Server. Refer to Plug-In API Reference for details.

10.4.2.3 Thread Safety and Indexers

Directory Server never calls an indexer for the same indexer object concurrently. Directory Server can, however, call the function concurrently for different indexer objects. If you use global variables, ensure that your function handles such variables safely.

10.4.3 Indexer Factory Function

The indexer factory function takes a parameter block from Directory Server. The function sets parameters such that Directory Server can update an index or sort results based on a matching rule.

The following figure shows how Directory Server uses the indexer factory function.

Figure 10-6 Indexer Factory Function Context

Description of Figure 10-6 follows
Description of "Figure 10-6 Indexer Factory Function Context"

The following example shows the indexer factory function for the case exact matching rule.

Example 10-8 Indexer Factory Function (matchingrule.c)

#include "slapi-plugin.h"

#define PLG_OID       "1.3.6.1.4.1.42.2.27.999.4.1"
#define SUBSYS        "CaseExactMatching Plugin"

/* Functions to obtain connection information for logging.             */
static long mypblock_get_msgid( Slapi_PBlock * pb);
static int  mypblock_get_connid(Slapi_PBlock * pb);
static int  mypblock_get_opid(  Slapi_PBlock * pb);

static int
plg_indexer_create(Slapi_PBlock * pb)
{
    int    rc     = LDAP_UNAVAILABLE_CRITICAL_EXTENSION; /* Init failed*/
    char * mr_oid = NULL;              /* MR OID from the server       */
    
    if (slapi_pblock_get(pb, SLAPI_PLUGIN_MR_OID, mr_oid) ||
        (mr_oid == NULL)) {
        slapi_log_error_ex(
            -1, /* errorId */
            mypblock_get_msgid(pb),
            mypblock_get_connid(pb),
            mypblock_get_opid(pb),
            SUBSYS,
            SLAPI_INTERNAL_ERROR,
            "plg_indexer_create failed: NULL OID values are invalid.\n"
        );
    } else if (strcmp(mr_oid, PLG_OID) == 0) {
        if ( /* The MR OID from the server is handled by this plug-in. */
            (slapi_pblock_set(         /* This is for completeness.    */
                pb,                    /* Your plug-in may set a       */
                SLAPI_PLUGIN_MR_OID,   /* different OID than the one   */
                PLG_OID                /* provided by the server.      */
            ) == 0) &&
            (slapi_pblock_set(         /* Provide an appropriate       */
                pb,                    /* indexer function pointer.    */
                SLAPI_PLUGIN_MR_INDEX_FN,
                (void *)plg_index_entry
            ) == 0) &&
            (slapi_pblock_set(         /* Set the object destructor.   */
                pb,
                SLAPI_PLUGIN_DESTROY_FN,
                (void *)plg_filter_destroy
            ) == 0)) {
                rc = LDAP_SUCCESS;
        } else {
            slapi_log_error_ex(
                -1, /* errorId */
                mypblock_get_msgid(pb),
                mypblock_get_connid(pb),
                mypblock_get_opid(pb),
                SUBSYS,
                SLAPI_INTERNAL_ERROR,
                "plg_indexer_create failed %d \n", rc
            );
        }
    }
    
    return rc;
}

Here, PLG_OID is the object identifier for the matching rule. plg_index_entry() is the indexer. plg_filter_destroy () is the destructor. Notice that the function returns LDAP_UNAVAILABLE_CRITICAL_EXTENSION on failure.

10.4.3.1 Input Parameters for Indexer Factory Functions

An indexer factory function can read the values for the following from the parameter block:

  • SLAPI_PLUGIN_MR_OID

  • SLAPI_PLUGIN_MR_TYPE

  • SLAPI_PLUGIN_MR_USAGE, indicates whether results must be sorted

  • SLAPI_PLUGIN_PRIVATE, if your plug-in uses private data that you specify in the plug-in initialization function

10.4.3.2 Output Parameters for Indexer Factory Functions

An indexer factory function should set values for at least the following in the parameter block before returning control to Directory Server:

  • SLAPI_PLUGIN_DESTROY_FN, a pointer to the indexer object destructor

  • SLAPI_PLUGIN_MR_INDEX_FN, a pointer to the indexer

  • SLAPI_PLUGIN_OBJECT, a pointer to the indexer object

Refer to Chapter 17, "Parameter Block Reference" for details about the parameter block.

10.4.3.3 Thread Safety and Indexer Factory Functions

This function must be thread safe. Directory Server can call this function concurrently.

10.4.4 Indexer Object Destructor Function

An indexer object destructor function frees memory that was allocated for an indexer object set up by the indexer factory function. Directory Server calls the destructor after an index operation completes.

Indexer object destructors take a parameter block as their only argument, as do filter object destructors. The parameter block holds a pointer to the object in SLAPI_PLUGIN_OBJECT. Refer to the Filter Object Destructor Function for details.

Directory Server never calls a destructor for the same object concurrently.

10.5 Enabling Sorting According to a Matching Rule

Clients can request that Directory Server sort results from an extensible match search. This section explains how to enable sorting based on a matching rule.

10.5.1 How Directory Server Performs Sorting According to a Matching Rule

Directory Server performs sorting as a variation of indexing, using the keys generated by an indexer function to sort results. The process is as follows:

  1. Directory Server creates a parameter block as for indexing, setting SLAPI_PLUGIN_MR_USAGE to SLAPI_PLUGIN_MR_USAGE_SORT, before passing the parameter block to the indexer factory function.

  2. The indexer factory function should set parameters in the parameter block as for indexing.

    If the sort function is different from the normal indexer function, ensure that the function checks the value of SLAPI_PLUGIN_MR_USAGE, and then sets SLAPI_MR_INDEXER_FN accordingly.

  3. Directory Server sets SLAPI_PLUGIN_MR_VALUES in the parameter block as a pointer to the values to be sorted. Directory Server then passes the parameter block the indexer function.

  4. Directory Server sorts results based on the keys the indexer function set in SLAPI_PLUGIN_MR_KEYS.

  5. Directory Server frees memory that was allocated for the operation.

    Refer to Indexing Entries According to a Matching Rule for details on how matching rule plug-ins can allow Directory Server to perform indexing based on a matching rule.

10.6 Handling an Unknown Matching Rule

This section explains how Directory Server finds a plug-in for a matching rule OID not recognized by that Directory Server.

10.6.1 Internal List of Correspondences

Directory Server identifies matching rules by OID. The server keeps an internal list of which matching rule plug-ins handle which OIDs.

Directory Server uses the internal list to determine which plug-in to call when the server receives an extensible match filter search request that includes a matching rule OID. Directory Server initially builds the list as matching rule plug-ins register OIDs that the plug-ins handle using slapi_matchingrule_register() in the plug-in initialization function.

10.6.2 OIDs Not in the Internal List

When the server encounters a matching rule OID that is not in the list, Directory Server queries each matching rule plug-in through registered factory functions. For every matching rule plug-in, Directory Server passes a parameter block that contains the OID to the matching rule factory function. The server then checks whether the factory function has in return provided a pointer in the parameter block. This pointer identifies the appropriate indexing or matching function for the OID.

If the factory function sets a pointer to the appropriate function, Directory Server assumes that the plug-in supports the matching rule.

The following shows how Directory Server checks whether a plug-in handles a specific filter match operation. The process for indexing closely resembles the process for matching.

Figure 10-7 Finding a Matching Rule Plug-In for an Unknown OID

Description of Figure 10-7 follows
Description of "Figure 10-7 Finding a Matching Rule Plug-In for an Unknown OID"

The following summarizes the process shown in Figure 10-7.

  1. Directory Server finds no match in the internal list of plug-ins to handle the OID. The server therefore creates a parameter block, and sets appropriate parameters for the factory, including SLAPI_PLUGIN_MR_OID.

  2. Directory Server calls the factory function. The factory function either sets a pointer in the parameter block to the appropriate function to handle the matching rule, or the function leaves the pointer NULL.

  3. Directory Server checks the parameter block after the factory function returns. If the factory function has set a function pointer in the parameter block, Directory Server updates its internal list. The function thus indicates that the plug-in supports the matching rule operation that is associated with the OID. Otherwise, the pointer remains NULL, and Directory Server tries the process again on the next matching rule plug-in that is registered.

    If after calling all matching rule plug-ins, Directory Server has found no plug-in to handle the matching rule OID, the server returns LDAP_UNAVAILABLE_CRITICAL_EXTENSION to the client.

  4. Directory Server frees the memory that was allocated for the operation.

    Be aware that Directory Server calls plug-in factory functions for the purpose of extending the internal list of correspondences.