Sun Java logo     Previous      Contents      Index      Next     

Sun logo
Sun Java(TM) System Directory Server 5 2004Q2 Plug-In Developer's Guide 

Chapter 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.

This chapter includes the following sections:

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

Ensure you 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.


How Matching Rule Plug-Ins Work

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

What a Matching Rule Is

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

Each matching rule is identified by a unique object identifier (OID) string. A client application requesting a search may specify the matching rule OID in the search filter, indicating to Directory Server how to check for a match of two attribute values.

In practice, a client application may 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 may want to sort entries using the rules for a given locale. Directory Server actually uses a matching rule plug-in to handle locale specific matching.

The Directory Server Administration Reference includes a list of matching rules Directory Server supports for internationalized searches. You can also view the list by searching the default schema. For example:

$ ldapsearch -p port -h host -D "cn=Directory Manager" -w password \
-b cn=schema cn=schema matchingRules

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 something like the following:

(cn:2.5.13.5:=Quentin)

This filter tells the server to search for Quentin in the CN of the entries, using matching rule 2.5.13.5, which happens to be case exact match. Refer to Directory Server Administration Guide for further details on searching with extensible match filters.

The case exact matching rule plug-in supporting OID 2.5.13.5 enables Directory Server to perform the search correctly. Directory Server calls code in this matching rule plug-in not only to check for matches during a search, but also to generate indexes to accelerate case exact searches and to sort entries found during such searches.

What a Matching Rule Plug-In Does

A matching rule plug-in may provide code to:

To enable these capabilities, the plug-in implements matching and indexing routines 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 may 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.

Table 10-1 lists all the functions that matching rule plug-in may implement.

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

Type

Parameter Block Identifier

Required?

Filter factory

SLAPI_PLUGIN_MR_FILTER_CREATE_FN

Yes

Filter index (used to check an index for matches)

SLAPI_PLUGIN_MR_FILTER_INDEX_FN

No

Filter match

SLAPI_PLUGIN_MR_FILTER_MATCH_FN

Yes

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

No

Indexer factory

SLAPI_PLUGIN_MR_INDEXER_CREATE_FN

No

Indexer object destructor

SLAPI_PLUGIN_DESTROY_FN

If needed to free memory

Plug-in initialization function

not applicable (specified in configuration entry)

Yes

Server shutdown (cleanup) function

SLAPI_CLOSE_FN

No

Server startup function

SLAPI_START_FN

No

Refer to the Directory Server Plug-In Developer’s Reference for details on all parameter block identifiers available for use with matching rule plug-ins.


An Example Matching Rule Plug-In

The example code cited in this chapter, from ServerRoot/examples/slapd/slapi/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 it to implement your own matching rule. The example uses a different OID from the plug-in provided with Directory Server, so the sample plug-in does not interfere with the existing plug-in if you do choose to load it.

Matching Rule Plug-In Configuration Entry

Matching rule plug-ins have type matchingrule, as shown in Code Example 10-1.

Code Example 10-1  Configuration Entry Template (matchingrule.c

dn: cn=Common Name,cn=plugins,cn=config

objectClass: top

objectClass: nsSlapdPlugin

objectClass: extensibleObject

cn: Common Name

nsslapd-pluginPath: <ServerRoot>/plugins/slapd/slapi/examples/<LibName>

nsslapd-pluginInitfunc: initialization function

nsslapd-pluginType: matchingRule

nsslapd-pluginId: plug-in identifier

nsslapd-pluginEnabled: on

nsslapd-pluginVersion: plug-in revision number

nsslapd-pluginVendor: vendor name

nsslapd-pluginDescription: plug-in description

Directory Server plug-ins may depend on matching rule plug-ins by type. Directory Server cannot load plug-ins unless plug-ins of the type they depend on load without error.

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

Registering Matching Rule Plug-Ins

Recall that matching rule plug-ins include factory functions. Factory functions themselves 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 handling both indexing and matching registers both factory functions and a description, as shown in Code Example 10-2.

Code Example 10-2  Registering Matching Rule Factory Functions (matchingrule.c

#include "slapi-plugin.h"

static Slapi_PluginDesc plg_desc = {

    "caseExactMatchingRule",           /* Plug-in identifier           */

    "Sun Microsystems, Inc.",          /* Vendor name                  */

    "5.2",                             /* 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 as in Code Example 10-3. */

    return rc;

}

In Code Example 10-2, 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.


Tip

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


Register matching rules themselves using slapi_matchingrule_register() as shown in Code Example 10-3, rather than using slapi_pblock_set() in the initialization function.

Code Example 10-3  Registering a Matching Rule (matchingrule.c

#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 as in Code Example 10-2.*/

    /* 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 we 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;

}

In Code Example 10-3:

The sample plug-in #defines each of these. If the plug-in implements several matching rules, the initialization function must register each one. Code Example 10-3 registers only one.

Notice plug-ins must register factory functions separately, using slapi_pblock_set() as shown in Code Example 10-2.


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.

The following steps outline how Directory Server handles an extensible match search.

  1. When Directory Server receives a search request containing an extensible match filter, it calls the filter factory function in the plug-in handling the corresponding matching rule, passing the filter factory a parameter block containing the extensible match filter information.
  2. The filter factory function builds a filter object, and sets pointers to the object and 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. Directory Server only considers all entries in the directory, perhaps slowing down 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
    • In order to read an index, the server calls the filter index function. The filter index function helps the server locate the appropriate indexer function.
    • Directory Server calls the indexer function to generate the keys. Refer to Indexer Function for details on that function.
  4. Directory Server builds a list of candidate entries.
  5. For each entry in the list of candidates, Directory Server calls the filter matching function. The filter matching function indicates whether an attribute value matches the search value.
  6. Directory Server verifies that the entry is in the scope of the search before returning it to the client as a search result. Finally, Directory Server frees memory allocated for the operation.

Figure 10-1 summarizes how Directory Server processes the search request.

Figure 10-1  Directory Server Performing an Extensible Match Search

The server relies on the matching rule plug-in to process extensible match searches.

Filter Matching Function

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

Figure 10-2 shows how Directory Server uses the filter matching function.

Figure 10-2  Filter Match Function Context

The filter match function checks a single entry for a match.

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

Code Example 10-4 shows the filter match function for the case exact matching rule where a match occurs when the two berval structures match.

Code Example 10-4  Filter Match 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;

/* 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 may do something more complex for the comparison.

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. This forces comparison of attribute subtypes, such as the locale of an attribute, in addition to attribute types. Refer to the Directory Server Plug-In Developer’s Reference for details on plug-in API functions and their arguments.

Thread Safety

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

Filter Index Function

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

Code Example 10-5 shows the filter index function for the case exact matching rule where the keys are the same as the values.

Code Example 10-5  Filter Index 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"

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 our 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;

}

Code Example 10-5 uses the following variables and macros:

Input Parameters

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

Output Parameters

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

Refer to the Directory Server Plug-In Developer’s Reference for details about the parameter block.

Thread Safety

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

Filter Factory Function

Your filter factory function takes a parameter block from Directory Server, and sets parameters such that Directory Server can build a list of candidate entries and check them for matches.

Figure 10-3 shows how the filter factory function operates.

Figure 10-3  Filter Factory Function Context

The filter factory function provides pointers to the filter match and filter index functions.

Code Example 10-6 shows the filter factory function for the case exact matching rule.

Code Example 10-6  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 this function returns LDAP_UNAVAILABLE_CRITICAL_EXTENSION if it does not handle the matching rule OID in the parameter block. Refer to Handling an Unknown Matching Rule for details on this behavior.

Input Parameters

A filter factory function may get values for the following from the parameter block.

Output Parameters

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

Refer to the Directory Server Plug-In Developer’s Reference for details about the parameter block.

Thread Safety

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

Filter Object Destructor

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

Code Example 10-7 shows an example object and destructor.

Code Example 10-7  Filter Object and Destructor (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;

/* Free memory allocated for the filter object.                        */

static int

plg_filter_destroy(Slapi_PBlock * pb)

{

    void         * obj  = NULL;        /* Server lets our 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;

}

Thread Safety

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


Indexing Entries According to a Matching Rule

This section covers providing plug-in support for a directory index based on a matching rule. Matching rule plug-ins are not required to enable indexing for the matching rules they 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, potentially ruining 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 searched regularly.

Refer to the Directory Server Administration Guide for details on how to manage directory indexes.

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 capable of translating attribute values to index keys. Directory Server calls this indexer function to create the index, and subsequently when adding, modifying, or deleting attribute values, because such changes may 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 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.

Figure 10-4 shows how Directory Server performs indexing based on a matching rule.

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

The server relies on the matching rule plug-in to maintain indexes.

The following steps summarize 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, allocates an indexer object if necessary, and sets pointers to the indexer and filter index functions and if necessary 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 and if necessary frees the indexer object using the destructor provided.

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

Indexer Function

An indexer function takes a parameter block from Directory Server containing a pointer to a berval array of attribute values, generates a berval array of corresponding keys from the values, and sets a pointer in the parameter block to the keys.

Figure 10-5 shows how Directory Server uses the indexer function.

Figure 10-5  Indexer Function Context

The indexer function generates keys from attribute values.

Code Example 10-8 shows the indexer function for the case exact matching rule where the keys are the same as the values.

Code Example 10-8  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 our 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;

}

In Code Example 10-8, 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, we use it in this context to indicate an internal error.

Input Parameters

An indexer function may get values for the following from the parameter block.

Output Parameter

An indexer function should generate the berval array of keys, and set SLAPI_PLUGIN_MR_KEYS in the parameter block before returning control to Directory Server. Refer to the Directory Server Plug-In Developer’s Reference for details about the parameter block.

Thread Safety

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

Indexer Factory Function

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

Figure 10-6 shows how Directory Server uses the indexer factory function.

Figure 10-6  Indexer Factory Function Context

The indexer factory function provides a pointers to the indexer function.

Code Example 10-9 shows the indexer factory function for the case exact matching rule.

Code Example 10-9  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;

}

In Code Example 10-9, PLG_OID is the object identifier for the matching rule, plg_index_entry() is the indexer, and plg_filter_destroy() is the destructor. Notice that the function returns LDAP_UNAVAILABLE_CRITICAL_EXTENSION on failure.

Input Parameters

An indexer factory function may read the values for the following from the parameter block.

Output Parameters

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

Refer to the Directory Server Plug-In Developer’s Reference for details about the parameter block.

Thread Safety

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

Indexer Object Destructor

An indexer object destructor function frees memory 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 Code Example 10-7 for details.

Thread safety

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


Enabling Sorting According to a Matching Rule

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

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

  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 your indexer factory function.
  2. Your indexer factory function should set parameters in the parameter block as for indexing.
  3. If your sort function is different from your normal indexer function, ensure that your function checks the value of SLAPI_PLUGIN_MR_USAGE, and then sets SLAPI_MR_INDEXER_FN accordingly.

  4. 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.
  5. Directory Server sorts results based on the keys your indexer function set in SLAPI_PLUGIN_MR_KEYS.
  6. Directory Server frees memory 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.


Handling an Unknown Matching Rule

This section explains how Directory Server finds a plug-in for a matching rule OID not known to Directory Server.

Internal List of Correspondences

Directory Server identifies matching rules by OID. It 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 it receives extensible match filter search request including a matching rule OID. Directory Server initially builds the list as matching rule plug-ins register OIDs they handle using slapi_matchingrule_register() in the plug-in initialization function.

OIDs Not in the Internal List

When it encounters a matching rule OID not in the list, Directory Server queries each matching rule plug-in through registered factory functions. For each matching rule plug-in, Directory Server passes a parameter block containing the OID to the matching rule factory function. It then checks whether the factory function has in return provided a pointer in the parameter block to the appropriate indexing or matching function for the OID.

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

Figure 10-7 summarizes 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

For unknown OIDs, the server queries matching rule plug-ins

The following steps summarize the steps in Figure 10-7.

  1. Directory Server finds it has no match in the internal list of plug-ins to handle the OID. It 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 it 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, indicating that the plug-in supports the matching rule operation associated with the OID. Otherwise, the pointer remains NULL, and Directory Server tries the process again on the next matching rule plug-in registered.
  4. If after calling all matching rule plug-ins, Directory Server has found no plug-in to handle the matching rule OID, it returns LDAP_UNAVAILABLE_CRITICAL_EXTENSION to the client.

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

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



Previous      Contents      Index      Next     


Copyright 2004 Sun Microsystems, Inc. All rights reserved.