1/*	$NetBSD: index.c,v 1.2 2021/08/14 16:15:02 christos Exp $	*/
2
3/* OpenLDAP WiredTiger backend */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2002-2021 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18/* ACKNOWLEDGEMENTS:
19 * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
20 * based on back-bdb for inclusion in OpenLDAP Software.
21 * WiredTiger is a product of MongoDB Inc.
22 */
23
24#include <sys/cdefs.h>
25__RCSID("$NetBSD: index.c,v 1.2 2021/08/14 16:15:02 christos Exp $");
26
27#include "portable.h"
28
29#include <stdio.h>
30#include "back-wt.h"
31#include "slap-config.h"
32
33static char presence_keyval[] = {0,0};
34static struct berval presence_key = BER_BVC(presence_keyval);
35
36AttrInfo *wt_index_mask(
37	Backend *be,
38	AttributeDescription *desc,
39	struct berval *atname )
40{
41	AttributeType *at;
42	AttrInfo *ai = wt_attr_mask( be->be_private, desc );
43
44	if( ai ) {
45		*atname = desc->ad_cname;
46		return ai;
47	}
48
49	/* If there is a tagging option, did we ever index the base
50	 * type? If so, check for mask, otherwise it's not there.
51	 */
52	if( slap_ad_is_tagged( desc ) && desc != desc->ad_type->sat_ad ) {
53		/* has tagging option */
54		ai = wt_attr_mask( be->be_private, desc->ad_type->sat_ad );
55
56		if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOTAGS ) ) {
57			*atname = desc->ad_type->sat_cname;
58			return ai;
59		}
60	}
61
62	/* see if supertype defined mask for its subtypes */
63	for( at = desc->ad_type; at != NULL ; at = at->sat_sup ) {
64		/* If no AD, we've never indexed this type */
65		if ( !at->sat_ad ) continue;
66
67		ai = wt_attr_mask( be->be_private, at->sat_ad );
68
69		if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOSUBTYPES ) ) {
70			*atname = at->sat_cname;
71			return ai;
72		}
73	}
74
75	return 0;
76}
77
78/* This function is only called when evaluating search filters.
79 */
80int wt_index_param(
81	Backend *be,
82	AttributeDescription *desc,
83	int ftype,
84	slap_mask_t *maskp,
85	struct berval *prefixp )
86{
87	AttrInfo *ai;
88	int rc;
89	slap_mask_t mask, type = 0;
90
91	ai = wt_index_mask( be, desc, prefixp );
92
93	if ( !ai ) {
94		/* TODO: add monitor */
95		return LDAP_INAPPROPRIATE_MATCHING;
96	}
97	mask = ai->ai_indexmask;
98
99	switch( ftype ) {
100	case LDAP_FILTER_PRESENT:
101		type = SLAP_INDEX_PRESENT;
102		if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
103			*prefixp = presence_key;
104			*maskp = mask;
105			return LDAP_SUCCESS;
106		}
107		break;
108
109	case LDAP_FILTER_APPROX:
110		type = SLAP_INDEX_APPROX;
111		if ( desc->ad_type->sat_approx ) {
112			if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
113				*maskp = mask;
114				return LDAP_SUCCESS;
115			}
116			break;
117		}
118
119		/* Use EQUALITY rule and index for approximate match */
120		/* fall thru */
121
122	case LDAP_FILTER_EQUALITY:
123		type = SLAP_INDEX_EQUALITY;
124		if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
125			*maskp = mask;
126			return LDAP_SUCCESS;
127		}
128		break;
129
130	case LDAP_FILTER_SUBSTRINGS:
131		type = SLAP_INDEX_SUBSTR;
132		if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
133			*maskp = mask;
134			return LDAP_SUCCESS;
135		}
136		break;
137
138	default:
139		return LDAP_OTHER;
140	}
141
142	/* TODO: add monitor index */
143	return LDAP_INAPPROPRIATE_MATCHING;
144}
145
146static int indexer(
147	Operation *op,
148	wt_ctx *wc,
149	AttributeDescription *ad,
150	struct berval *atname,
151	BerVarray vals,
152	ID id,
153	int opid,
154	slap_mask_t mask )
155{
156	int rc, i;
157	struct berval *keys;
158	WT_CURSOR *cursor = NULL;
159	WT_SESSION *session = wc->session;
160	assert( mask != 0 );
161
162	cursor = wt_ctx_index_cursor(wc, atname, 1);
163	if( !cursor ) {
164		Debug( LDAP_DEBUG_ANY,
165			   LDAP_XSTRING(indexer)
166			   ": open index cursor failed: %s\n",
167			   atname->bv_val );
168		goto done;
169	}
170
171	if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
172		rc = wt_key_change( op->o_bd, cursor, &presence_key, id, opid );
173		if( rc ) {
174			goto done;
175		}
176	}
177
178	if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
179		rc = ad->ad_type->sat_equality->smr_indexer(
180			LDAP_FILTER_EQUALITY,
181			mask,
182			ad->ad_type->sat_syntax,
183			ad->ad_type->sat_equality,
184			atname, vals, &keys, op->o_tmpmemctx );
185
186		if( rc == LDAP_SUCCESS && keys != NULL ) {
187			for( i=0; keys[i].bv_val != NULL; i++ ) {
188				rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
189				if( rc ) {
190					ber_bvarray_free_x( keys, op->o_tmpmemctx );
191					goto done;
192				}
193			}
194			ber_bvarray_free_x( keys, op->o_tmpmemctx );
195		}
196		rc = LDAP_SUCCESS;
197	}
198
199	if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
200		rc = ad->ad_type->sat_approx->smr_indexer(
201			LDAP_FILTER_APPROX,
202			mask,
203			ad->ad_type->sat_syntax,
204			ad->ad_type->sat_approx,
205			atname, vals, &keys, op->o_tmpmemctx );
206
207		if( rc == LDAP_SUCCESS && keys != NULL ) {
208			for( i=0; keys[i].bv_val != NULL; i++ ) {
209				rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
210				if( rc ) {
211					ber_bvarray_free_x( keys, op->o_tmpmemctx );
212					goto done;
213				}
214			}
215			ber_bvarray_free_x( keys, op->o_tmpmemctx );
216		}
217
218		rc = LDAP_SUCCESS;
219	}
220
221	if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
222		rc = ad->ad_type->sat_substr->smr_indexer(
223			LDAP_FILTER_SUBSTRINGS,
224			mask,
225			ad->ad_type->sat_syntax,
226			ad->ad_type->sat_substr,
227			atname, vals, &keys, op->o_tmpmemctx );
228
229		if( rc == LDAP_SUCCESS && keys != NULL ) {
230			for( i=0; keys[i].bv_val != NULL; i++ ) {
231				rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
232				if( rc ) {
233					ber_bvarray_free_x( keys, op->o_tmpmemctx );
234					goto done;
235				}
236			}
237			ber_bvarray_free_x( keys, op->o_tmpmemctx );
238		}
239
240		rc = LDAP_SUCCESS;
241	}
242
243done:
244	if(cursor){
245		cursor->close(cursor);
246	}
247	return rc;
248}
249
250static int index_at_values(
251	Operation *op,
252	wt_ctx *wc,
253	AttributeDescription *ad,
254	AttributeType *type,
255	struct berval *tags,
256	BerVarray vals,
257	ID id,
258	int opid )
259{
260	int rc;
261	slap_mask_t mask = 0;
262	int ixop = opid;
263	AttrInfo *ai = NULL;
264
265	if ( opid == WT_INDEX_UPDATE_OP )
266		ixop = SLAP_INDEX_ADD_OP;
267
268	if( type->sat_sup ) {
269		/* recurse */
270		rc = index_at_values( op, wc, NULL,
271							  type->sat_sup, tags,
272							  vals, id, opid );
273
274		if( rc ) return rc;
275	}
276
277	/* If this type has no AD, we've never used it before */
278	if( type->sat_ad ) {
279		ai = wt_attr_mask( op->o_bd->be_private, type->sat_ad );
280		if ( ai ) {
281			#ifdef LDAP_COMP_MATCH
282			/* component indexing */
283			if ( ai->ai_cr ) {
284				ComponentReference *cr;
285				for( cr = ai->ai_cr ; cr ; cr = cr->cr_next ) {
286					rc = indexer( op, wc, cr->cr_ad, &type->sat_cname,
287								  cr->cr_nvals, id, ixop,
288								  cr->cr_indexmask );
289				}
290			}
291			#endif
292			ad = type->sat_ad;
293			/* If we're updating the index, just set the new bits that aren't
294             * already in the old mask.
295             */
296			if ( opid == WT_INDEX_UPDATE_OP )
297				mask = ai->ai_newmask & ~ai->ai_indexmask;
298			else
299				/* For regular updates, if there is a newmask use it. Otherwise
300				 * just use the old mask.
301				 */
302				mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
303			if( mask ) {
304				rc = indexer( op, wc, ad, &type->sat_cname,
305							  vals, id, ixop, mask );
306				if( rc ) return rc;
307			}
308		}
309	}
310
311	if( tags->bv_len ) {
312		AttributeDescription *desc;
313
314		desc = ad_find_tags( type, tags );
315		if( desc ) {
316			ai = wt_attr_mask( op->o_bd->be_private, desc );
317
318			if( ai ) {
319				if ( opid == WT_INDEX_UPDATE_OP )
320					mask = ai->ai_newmask & ~ai->ai_indexmask;
321				else
322					mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
323				if ( mask ) {
324					rc = indexer( op, wc, desc, &desc->ad_cname,
325								  vals, id, ixop, mask );
326
327					if( rc ) {
328						return rc;
329					}
330				}
331			}
332		}
333	}
334
335	return LDAP_SUCCESS;
336}
337
338int wt_index_values(
339	Operation *op,
340	wt_ctx *wc,
341	AttributeDescription *desc,
342	BerVarray vals,
343	ID id,
344	int opid )
345{
346	int rc;
347
348	/* Never index ID 0 */
349	if ( id == 0 )
350		return 0;
351
352	rc = index_at_values( op, wc, desc,
353						  desc->ad_type, &desc->ad_tags,
354						  vals, id, opid );
355
356	return rc;
357}
358
359int
360wt_index_entry( Operation *op, wt_ctx *wc, int opid, Entry *e )
361{
362	int rc;
363	Attribute *ap = e->e_attrs;
364
365	if ( e->e_id == 0 )
366		return 0;
367
368	Debug( LDAP_DEBUG_TRACE, "=> index_entry_%s( %ld, \"%s\" )\n",
369		   opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
370		   (long) e->e_id, e->e_dn ? e->e_dn : "" );
371
372	for ( ; ap != NULL; ap = ap->a_next ) {
373		rc = wt_index_values( op, wc, ap->a_desc,
374							  ap->a_nvals, e->e_id, opid );
375		if( rc != LDAP_SUCCESS ) {
376			Debug( LDAP_DEBUG_TRACE,
377				   "<= index_entry_%s( %ld, \"%s\" ) failure\n",
378				   opid == SLAP_INDEX_ADD_OP ? "add" : "del",
379				   (long) e->e_id, e->e_dn );
380			return rc;
381		}
382	}
383
384	Debug( LDAP_DEBUG_TRACE, "<= index_entry_%s( %ld, \"%s\" ) success\n",
385		   opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
386		   (long) e->e_id, e->e_dn ? e->e_dn : "" );
387	return 0;
388}
389
390/*
391 * Local variables:
392 * indent-tabs-mode: t
393 * tab-width: 4
394 * c-basic-offset: 4
395 * End:
396 */
397