Net2/usr/src/contrib/isode/dsap/common/turbo_index.c

Compare this file to the similar file:
Show the results in this format:

/* turbo_index.c */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/dsap/common/RCS/turbo_index.c,v 7.1 91/02/22 09:20:38 mrose Interim $";
#endif

/* 
 * $Header: /f/osi/dsap/common/RCS/turbo_index.c,v 7.1 91/02/22 09:20:38 mrose Interim $
 *
 *
 * $Log:	turbo_index.c,v $
 * Revision 7.1  91/02/22  09:20:38  mrose
 * Interim 6.8
 * 
 */

/*
 *				  NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


#include <stdio.h>
#include "quipu/config.h"
#include "quipu/attr.h"
#include "quipu/entry.h"
#include "quipu/turbo.h"
#include "logger.h"

#ifdef TURBO_INDEX

extern LLog * log_dsap;

AttributeType	*turbo_index_types;	/* array of attributes to optimize */
int		turbo_index_num;	/* number of attributes to optimize */
Avlnode		*subtree_index;		/* array of subtree indexes */
Avlnode		*sibling_index;		/* array of sibling indexes */
int		optimized_only;		/* only allow indexed searches */

static index_cmp( a, b )
Index_node	*a;
Index_node	*b;
{
	return( AttrV_cmp( (AttributeValue) a->in_value,
	    (AttributeValue) b->in_value ) );
}

static sindex_cmp( a, b )
Index_node	*a;
Index_node	*b;
{
	return( strcmp( (char *) a->in_value, (char *) b->in_value ) );
}

index_soundex_cmp( code, node )
char		*code;
Index_node	*node;
{
	return( strcmp( code, (char *) node->in_value ) );
}

index_soundex_prefix( code, node, len )
char		*code;
Index_node	*node;
int		len;
{
	return( strncmp( code, (char *) node->in_value, len ) );
}

substring_prefix_cmp( val, node, len )
AttributeValue	val;
Index_node	*node;
int		len;
{
	return(strncmp((char *) val->av_struct,
	    (char *) (((AttributeValue) node->in_value)->av_struct), len));
}

substring_prefix_case_cmp( val, node, len )
AttributeValue	val;
Index_node	*node;
int		len;
{
	return(lexnequ((char *) val->av_struct,
	    (char *) (((AttributeValue) node->in_value)->av_struct), len));
}

indexav_cmp( av, node )
AttributeValue	av;
Index_node	*node;
{
	return( AttrV_cmp( av, (AttributeValue) node->in_value ) );
}

static index_dup( node, dup )
Index_node	*node;
Index_node	*dup;
{
	int	i, j;
	Entry	tmp1, tmp2;

	/* check for duplicates */
	for ( i = 0; i < node->in_num; i++ ) {
		if ( node->in_entries[ i ] == dup->in_entries[ 0 ] )
			return( NOTOK );
		if (node->in_entries[i] > dup->in_entries[0])
			break;
	}

	node->in_entries = (struct entry **) realloc( (char *)node->in_entries, 
	    (unsigned) (sizeof(struct entry *) * (node->in_num + 1) ));
	node->in_num++;

	tmp1 = dup->in_entries[0];
	for (j = i; j < node->in_num; j++) {
		tmp2 = node->in_entries[j];
		node->in_entries[j] = tmp1;
		tmp1 = tmp2;
	}

	return( NOTOK );
}

static indexav_free( node )
Index_node	*node;
{
	AttrV_free( (AttributeValue) node->in_value );
	free( (char *) node->in_entries );
	free( (char *) node );
}

static soundex_free( node )
Index_node	*node;
{
	free( (char *) node->in_value );
	free( (char *) node->in_entries );
	free( (char *) node );
}

static index_free( pindex )
Index	*pindex;
{
	dn_free( pindex->i_dn );
	(void) avl_free( pindex->i_root, indexav_free );
	(void) avl_free( pindex->i_sroot, soundex_free );
	free( (char *) pindex );
}

/* ARGSUSED */
static i_dup( a )
Index	*a;
{
	return( NOTOK );
}

static i_cmp( a, b )
Index	*a;
Index	*b;
{
	return( dn_order_cmp( a->i_dn, b->i_dn ) );
}

idn_cmp( a, b )
DN	a;
Index	*b;
{
	return( dn_order_cmp( a, b->i_dn ) );
}

/*
 * siblings - return 1 if a and b are siblings, 0 otherwise
 */

static siblings( a, b )
DN      a;
DN      b;
{
        for ( ; a && b; a = a->dn_parent, b = b->dn_parent ) {
                if ( dn_cmp( a, b ) == NOTOK ) {
                        if ( a->dn_parent == NULLDN && b->dn_parent == NULLDN )
                                return( 1 );
                        else
                                return( 0 );
                }
        }

        if ( a != NULLDN || b != NULLDN )
                return( 0 );

        return( 1 );
}

/*
 * prefix - returns the following:
 *      -1      =>      a is a prefix of b
 *      0       =>      a and b are equal
 *      1       =>      b is a prefix of a
 *      2       =>      neither a nor b is a prefix of the other
 */

static prefix( a, b )
DN      a;
DN      b;
{
        for ( ; a && b; a = a->dn_parent, b = b->dn_parent )
                if ( dn_comp_cmp( a, b ) == NOTOK )
                        return( 2 );    /* neither is prefix */

        if ( a == NULLDN && b == NULLDN )
                return( 0 );            /* they are equal */
        else if ( a == NULLDN )
                return( -1 );           /* a is a prefix of b */
        else
                return( 1 );            /* b is a prefix of a */
}

static Index *new_index( dn )
DN	dn;
{
	Index	*pindex;
	int	i;

	pindex = (Index *) malloc( (unsigned) (sizeof(Index) * turbo_index_num ));

	for ( i = 0; i < turbo_index_num; i++ ) {
		pindex[ i ].i_attr = turbo_index_types[ i ];
		pindex[ i ].i_count = 0;
		pindex[ i ].i_scount = 0;
		pindex[ i ].i_root = NULLAVL;
		pindex[ i ].i_sroot = NULLAVL;
		pindex[ i ].i_nonleafkids = (struct entry **) 0;
		pindex[ i ].i_nonlocalaliases = (struct entry **) 0;
		pindex[ i ].i_dn = dn_cpy( dn );
	}

	return( pindex );
}

#ifdef DEBUG

/* ARGSUSED */
static print_soundex_node( n, ps )
Index_node	*n;
int		ps;
{
	int	i;

	(void) printf( "\t(%s)\n",n->in_value );
	for ( i = 0; i < n->in_num; i++ )
		(void) printf( "\t\t%s\n",
		    n->in_entries[i]->e_name->rdn_av.av_struct);
	return( OK );
}

#endif DEBUG

/*
 * add_nonlocalalias - add entry e to the list of nonlocal aliases
 * kept with index index.
 */

static add_nonlocalalias( e, pindex )
Index		*pindex;
struct entry	*e;
{
	struct entry	**tmp;
	int		i;

	if ( pindex == NULLINDEX )
		return;

	if ( pindex->i_nonlocalaliases == (struct entry **) 0 ) {
		pindex->i_nonlocalaliases = (struct entry **) malloc(
		    sizeof(struct entry *) * 2 );
		pindex->i_nonlocalaliases[ 0 ] = (struct entry *) 0;
	}

	/* first, check for duplicates */
	for ( i = 0, tmp = pindex->i_nonlocalaliases;
	    *tmp != (struct entry *) 0; 
	    tmp++, i++ ) {
		if ( *tmp == e )
			return;
	}

	pindex->i_nonlocalaliases = (struct entry **) realloc(
	    (char *)pindex->i_nonlocalaliases, (unsigned) sizeof(struct entry *) * (i + 2) );

	pindex->i_nonlocalaliases[ i ] = e;
	pindex->i_nonlocalaliases[ i + 1 ] = NULLENTRY;
}

/* 
 * addnonleafkids - add entry e to the list of nonlocal kids kept
 * in index index.
 */

static add_nonleafkid( e, pindex )
Index		*pindex;
struct entry	*e;
{
	struct entry	**tmp;
	int		i;

	if ( pindex == NULLINDEX )
		return;

	if ( pindex->i_nonleafkids == (struct entry **) 0 ) {
		pindex->i_nonleafkids = (struct entry **) malloc( sizeof(Entry)
		    * 2 );
		pindex->i_nonleafkids[ 0 ] = (struct entry *) 0;
	}

	/* first, check for duplicates */
	for ( i = 0, tmp = pindex->i_nonleafkids; *tmp != (struct entry *) 0; 
	    tmp++, i++ ) {
		if ( *tmp == e )
			return;
	}

	pindex->i_nonleafkids = (struct entry **) realloc(
	    (char *)pindex->i_nonleafkids, (unsigned)sizeof(struct entry *) * (i + 2) );

	pindex->i_nonleafkids[ i ] = e;
	pindex->i_nonleafkids[ i + 1 ] = NULLENTRY;
}

/*
 * delete_nonleafkid - delete a reference to nonleaf child entry e
 * in index index.
 */

static delete_nonleafkid( e, pindex )
struct entry	*e;
Index		*pindex;
{
	int		i, j;
	struct entry	**tmp;

	if ( pindex->i_nonleafkids == (struct entry **) 0 ) {
		LLOG(log_dsap, LLOG_EXCEPTIONS, ("Index has no non-leaf entries"));
		return;
	}

	for ( i = 0, tmp = pindex->i_nonleafkids; *tmp; tmp++, i++ )
		if ( *tmp == e ) break;

	if ( *tmp == (struct entry *) 0 ) {
		LLOG(log_dsap, LLOG_EXCEPTIONS, ("Cannot find non-leaf entry"));
		return;
	}

	for ( j = i + 1; pindex->i_nonleafkids[ j ]; j++ )
		pindex->i_nonleafkids[ j - 1 ] = pindex->i_nonleafkids[ j ];

	return;
}

/*
 * delete_nonlocalalias - delete a reference to nonlocal alias entry e
 * in index index.
 */

static delete_nonlocalalias( e, pindex )
struct entry	*e;
Index		*pindex;
{
	int		i, j;
	struct entry	**tmp;

	if ( pindex->i_nonlocalaliases == (struct entry **) 0 ) {
		LLOG(log_dsap, LLOG_EXCEPTIONS, ("index has no non-local aliases"));
		return;
	}

	for ( i = 0, tmp = pindex->i_nonlocalaliases; *tmp; tmp++, i++ )
		if ( *tmp == e ) break;

	if ( *tmp == (struct entry *) 0 ) {
		LLOG(log_dsap, LLOG_EXCEPTIONS, ("Cannot find non-local alias"));
		return;
	}

	for ( j = i + 1; pindex->i_nonlocalaliases[ j ]; j++ )
		pindex->i_nonlocalaliases[ j - 1 ] =
		    pindex->i_nonlocalaliases[ j ];

	return;
}

/*
 * turbo_attr_insert -- mark entry e as having attribute value val in
 * index for attribute type attr.
 */

static turbo_attr_insert( pindex, e, attr, values )
Index		*pindex;
Entry		e;
AttributeType	attr;
AV_Sequence	values;
{
	int		i;
	AV_Sequence	av;
	Index_node	*imem;
	char		*word, *code;
	char		*next_word();
	IFP		approxfn();
	int		soundex_match();

	/* find the appropriate index */
	for ( i = 0; i < turbo_index_num; i++ )
		if ( AttrT_cmp( pindex[ i ].i_attr, attr ) == 0 )
			break;

	if ( i == turbo_index_num ) {
		LLOG( log_dsap, LLOG_EXCEPTIONS, ("turbo_attr_insert: cannot find optimized attribute") );
		return;
	}

	/* insert all values */
	for ( av = values; av != NULLAV; av = av->avseq_next ) {
		imem = (Index_node *) malloc( sizeof(Index_node) );
		imem->in_value = (caddr_t) AttrV_cpy( &av->avseq_av );
		imem->in_entries = (struct entry **) malloc( sizeof(struct
		    entry *) );
		imem->in_entries[ 0 ] = (struct entry *) e;
		imem->in_num = 1;

		/*
		 * Now we insert the entry into the appropriate index.
		 * If the attribute has a soundex approximate matching
		 * function, we insert the entry into the appropriate
		 * soundex index for that attribute.
		 */

		/* a return of OK means it was the first one inserted */
		if ( avl_insert( &pindex[ i ].i_root, (caddr_t) imem, index_cmp,
		    index_dup ) == OK ) {
			pindex[ i ].i_count++;
			imem = NULLINDEXNODE;
		} else {
			AttrV_free( (AttributeValue) imem->in_value );
			free( (char *) imem->in_entries );
			free( (char *) imem );
		}

		if ( approxfn( attr->oa_syntax ) != soundex_match )
			continue;

		for ( word = (char *) av->avseq_av.av_struct; word;
		    word = next_word( word ) ) {
			code = NULL;
			soundex( word, &code );
			imem = (Index_node *) malloc( sizeof(Index_node) );
			imem->in_value = (caddr_t) code;
			imem->in_entries = (struct entry **) malloc(
			    sizeof(struct entry *) );
			imem->in_entries[ 0 ] = (struct entry *) e;
			imem->in_num = 1;

			if ( avl_insert( &pindex[i].i_sroot, (caddr_t) imem, sindex_cmp,
			    index_dup ) == OK ) {
				pindex[ i ].i_scount++;
			} else {
				free( (char *) imem->in_value );
				free( (char *) imem->in_entries );
				free( (char *) imem );
			}
		}
	}
}

/*
 * turbo_add2index -- search through the given entry's attribute list for
 * attrs to optimize. if an attr to optimize is found, we add that attribute 
 * along with a pointer to the corresponding entry to the appropriate 
 * attribute index.
 */

turbo_add2index( e )
Entry	e;		/* the entry these attrs belong to */
{
	Entry		parent, tmp;
	Attr_Sequence	as, entry_find_type();
	DN		pdn, dn, tmpdn;
	int		opt, pcmp, nonleaf;
	Index		*subindex;
	Index		*sibindex;
	extern AttributeType at_masterdsa, at_slavedsa;

	if ( sibling_index == (Avlnode *) 0 && subtree_index == (Avlnode *) 0 )
		return;

	nonleaf = (entry_find_type( e, at_masterdsa ) != NULLATTR ||
	    entry_find_type( e, at_slavedsa ) != NULLATTR);

	parent = e->e_parent;
	pdn = get_copy_dn( parent );
	dn = get_copy_dn( e );

	sibindex = get_sibling_index( pdn );

	/* for each attribute in the entry... */
	for ( as = e->e_attributes; as != NULLATTR; as = as->attr_link ) {
		opt = turbo_isoptimized( as->attr_type );

		/* sibling index */
		if ( sibindex && opt ) {
			(void) turbo_attr_insert( sibindex, e, as->attr_type,
			    as->attr_value );

			if ( e->e_alias && (! siblings( dn, sibindex->i_dn )) )
				add_nonlocalalias( e, sibindex );
		}

		/* could be a subtree index with all parents & this node */
		for ( tmp = e; tmp->e_parent; tmp = tmp->e_parent ) {
			tmpdn = get_copy_dn( tmp );
			if ( subindex = get_subtree_index( tmpdn ) ) {
				if ( opt ) {
					(void) turbo_attr_insert( subindex, e,
					    as->attr_type, as->attr_value );

					if ( e->e_alias ) {
						pcmp = prefix( subindex->i_dn,
						    e->e_alias );
						if ( pcmp != 0 && pcmp != -1 ) {
							add_nonlocalalias(e,
							    subindex);
						}
					}
				}

				if ( nonleaf ) {
					add_nonleafkid(e, subindex);
					nonleaf = 0;
				}
			}
			dn_free( tmpdn );
		}
	}

	dn_free( dn );
	dn_free( pdn );
	return;
}

/*
 * turbo_attr_delete -- delete entry e from index for values of attribute
 * attr.
 */

static turbo_attr_delete( pindex, e, attr, values )
Index		*pindex;
Entry		e;
AttributeType	attr;
AV_Sequence	values;
{
	int		i, j, k;
	AV_Sequence	av;
	Index_node	*node, *imem;
	struct entry	**p;
	char		*code, *word;
	char		*next_word();

	/* find the appropriate index */
	for ( i = 0; i < turbo_index_num; i++ )
		if ( AttrT_cmp( pindex[ i ].i_attr, attr ) == 0 )
			break;

	if ( i == turbo_index_num ) {
		LLOG( log_dsap, LLOG_EXCEPTIONS, ("turbo_attr_delete: cannot find optimized attribute") );
		return;
	}

	/* delete all values */
	for ( av = values; av != NULLAV; av = av->avseq_next ) {
		node = (Index_node *) avl_find( pindex[ i ].i_root, 
		    (caddr_t) &av->avseq_av, (IFP)indexav_cmp );

		if ( node == NULLINDEXNODE ) {
			LLOG( log_dsap, LLOG_EXCEPTIONS, ("Optimized attribute value not found!\n") );
			continue;
		}

		/* find the entry we want to delete */
		p = node->in_entries;
		for ( j = 0; j < node->in_num; j++, p++ )
			if ( *p == (struct entry *) e )
				break;

		if ( j == node->in_num ) {
			LLOG( log_dsap, LLOG_EXCEPTIONS, ("Optimized av entry not found") );
			continue;
		}

		if ( --(node->in_num) == 0 ) {
			imem = (Index_node *) avl_delete( &pindex[ i ].i_root,
			    (caddr_t) &av->avseq_av, indexav_cmp );
			( void ) AttrV_free( (AttributeValue) imem->in_value );
			( void ) free( (char *) imem->in_entries );
			( void ) free( (char *) imem );
			pindex[ i ].i_count--;
		} else {
			for ( k = j; k < node->in_num; k++ )
				node->in_entries[ k ] =
				    node->in_entries[ k + 1 ];
			node->in_entries = (struct entry **)
			    realloc( (char *) node->in_entries, (unsigned) node->in_num
			    * sizeof(struct entry *) );
		}

		/* if there's a soundex index, delete from that too */
		if ( pindex[i].i_sroot == NULLAVL )
			continue;

		for ( word = av->avseq_av.av_struct; word != NULL;
		    word = next_word( word ) ) {
			code = NULL;
			soundex( word, &code );

			/*
			 * not finding the node is ok if the entry happens
			 * to be the only one with this code and was deleted
			 * on a previous pass through this loop.  we hope.
			 */

			if ((imem = (Index_node *) avl_find(pindex[i].i_sroot,
			    code, index_soundex_cmp)) == NULLINDEXNODE) {
				free(code);
				continue;
			}

			/* find the entry */
			p = imem->in_entries;
			for ( j = 0; j < imem->in_num; j++, p++ )
				if ( *p == (struct entry *) e )
					break;

			/*
			 * not finding the entry is this is ok for the soundex
			 * index since an entry can appear more than once and
			 * might have already been deleted on a previous pass
			 */

			if ( j == imem->in_num )
				continue;

			if ( --(imem->in_num) == 0 ) {
				imem = (Index_node *)
				    avl_delete( &pindex[ i ].i_sroot,
				    (caddr_t) code, index_soundex_cmp );

				free( (char *) imem->in_value );
				free( (char *) imem->in_entries );
				free( (char *) imem );
			} else {
				for ( k = j; k < imem->in_num; k++ )
					imem->in_entries[ k ] =
					    imem->in_entries[ k+1 ];

				imem->in_entries = (struct entry **)
				    realloc( (char *) imem->in_entries,
				    (unsigned) imem->in_num * sizeof(struct entry *) );
			}
			free(code);
		}
	}
}

/*
 * turbo_index_delete -- delete attribute index entries for the given
 * entry from the attribute index associated with the entry's parent
 * node.
 */

turbo_index_delete( e )
Entry	e;
{
	Entry		parent, tmp;
	Attr_Sequence	as;
	DN		pdn, dn, tmpdn;
	Index		*subindex;
	Index		*sibindex;
	int		pcmp, nonleaf;

	if ( subtree_index == NULLAVL && sibling_index == NULLAVL )
		return;

	nonleaf = (! isleaf(e));

	parent = e->e_parent;
	pdn = get_copy_dn( parent );
	dn = get_copy_dn( e );

	sibindex = get_sibling_index( pdn );

	/* for each attribute in the entry... */
	for ( as = e->e_attributes; as != NULLATTR; as = as->attr_link ) {
		if ( turbo_isoptimized( as->attr_type ) == 0 )
			continue;

		/* sibling index */
		if ( sibindex ) {
			(void) turbo_attr_delete( sibindex, e, as->attr_type,
			    as->attr_value );
		}

		for ( tmp = e; tmp; tmp = tmp->e_parent ) {
			tmpdn = get_copy_dn( tmp );
			if ( subindex = get_subtree_index( tmpdn ) ) {
				(void) turbo_attr_delete( subindex, e,
				    as->attr_type, as->attr_value );
			}
			dn_free( tmpdn );
		}
	}

	/* now delete references in nonleafkids and nonlocalaliases... */
	if ( sibindex && e->e_alias && (! siblings( dn, sibindex->i_dn )) )
		delete_nonlocalalias( e, sibindex );

	dn_free( pdn );
	dn_free( dn );

	if ( nonleaf == 0 && e->e_alias == NULLDN )
		return;

	for ( tmp = e; tmp; tmp = tmp->e_parent ) {
		tmpdn = get_copy_dn( tmp );
		if ( subindex = get_subtree_index( tmpdn ) ) {
			if ( e->e_alias ) {
				pcmp = prefix( subindex->i_dn, e->e_alias );
				if ( pcmp != 0 && pcmp != -1 )
					delete_nonlocalalias( e, subindex );
			}

			if ( nonleaf )
				delete_nonleafkid( e, subindex );
		}
		dn_free( tmpdn );
	}

	return;
}

/*
 * turbo_isoptimized -- return TRUE if attr is to be optimized, FALSE
 * otherwise.
 */

turbo_isoptimized( attr )
AttributeType	attr;
{
	int	i;

	for ( i = 0; i < turbo_index_num; i++ ) {
		if ( AttrT_cmp( attr, turbo_index_types[ i ] ) == 0 )
			return( 1 );
	}

	return( 0 );
}

/*
 * turbo_optimize -- add attribute attr to the list of attributes to be 
 * optimized this routine creates an empty index and arranges for the 
 * attribute to be optimized during loading.
 */

turbo_optimize( attr )
char	*attr;
{
	AttributeType	a;

	if ( (a = str2AttrT( attr )) == NULLAttrT ) {
		LLOG(log_dsap, LLOG_EXCEPTIONS, ("Bad attribute type (%s)", attr));
		return;
	}

	if ( subtree_index || sibling_index )
		LLOG(log_dsap, LLOG_EXCEPTIONS, ("WARNING: optimized attributes MUST be specified before subtree or sibling index"));

	if ( turbo_index_types == (AttributeType *) 0 )
		turbo_index_types = (AttributeType *) malloc(
		    sizeof(AttributeType *));
	else
		turbo_index_types = (AttributeType *) realloc(
		    (char *) turbo_index_types, (unsigned) (turbo_index_num + 1) *
		    sizeof(AttributeType *));

	if ( turbo_index_types == (AttributeType *) 0 )
		fatal(66, "turbo_optimize: malloc failed!\n");

	turbo_index_types[ turbo_index_num ] = AttrT_cpy(a);
	turbo_index_num++;

	return;
}

/*
 * index_subtree - arrange for the subtree starting at tree to be indexed.
 */

index_subtree( tree )
char	*tree;
{
	DN		dn, str2dn();
	Index		*pindex;

	if ( (dn = str2dn( tree )) == NULLDN ) {
		LLOG( log_dsap, LLOG_EXCEPTIONS, ("Invalid subtree (%s)\n", tree) );
		return;
	}

	pindex = new_index( dn );
	dn_free( dn );
	if ( avl_insert( &subtree_index, (caddr_t) pindex, i_cmp, i_dup ) == NOTOK ) {
		LLOG(log_dsap, LLOG_EXCEPTIONS, ("Subtree index for %s already exists\n", tree));
		index_free( pindex );
	}

	return;
}

/*
 * index_siblings - arrange for the children of parent to be indexed.
 */

index_siblings( parent )
char	*parent;
{
	DN		dn, str2dn();
	Index		*pindex;

	if ( (dn = str2dn( parent )) == NULLDN ) {
		LLOG( log_dsap, LLOG_EXCEPTIONS, ("Invalid parent (%s)\n", parent) );
		return;
	}

	pindex = new_index( dn );
	dn_free( dn );
	if ( avl_insert( &sibling_index, (caddr_t) pindex, i_cmp, i_dup ) == NOTOK ) {
		LLOG(log_dsap, LLOG_EXCEPTIONS, ("Sibling index for %s already exists\n", parent));
		index_free( pindex );
	}

	return;
}

#endif /* turbo_index */