Commit e8c24148 authored by Howard Chu's avatar Howard Chu
Browse files

Preliminary sorted value support

parent ebb9e029
......@@ -490,6 +490,9 @@ slap_mods2entry(
} else {
attr->a_nvals = attr->a_vals;
}
/* slap_mods_check() gives us sorted results */
if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL )
attr->a_flags |= SLAP_ATTR_SORTED_VALS;
*tail = attr;
tail = &attr->a_next;
......
......@@ -281,6 +281,74 @@ attrs_dup( Attribute *a )
return anew;
}
int
attr_valfind(
Attribute *a,
unsigned flags,
struct berval *val,
unsigned *slot,
void *ctx )
{
struct berval nval = BER_BVNULL, *cval;
MatchingRule *mr = a->a_desc->ad_type->sat_equality;
const char *text;
int match = -1, rc;
unsigned i;
if( !SLAP_IS_MR_ASSERTED_VALUE_NORMALIZED_MATCH( flags ) &&
mr->smr_normalize )
{
rc = (mr->smr_normalize)(
flags & (SLAP_MR_TYPE_MASK|SLAP_MR_SUBTYPE_MASK|SLAP_MR_VALUE_OF_SYNTAX),
a->a_desc->ad_type->sat_syntax,
mr, val, &nval, ctx );
if( rc != LDAP_SUCCESS ) {
return LDAP_INVALID_SYNTAX;
}
cval = &nval;
} else {
cval = val;
}
if ( a->a_flags & SLAP_ATTR_SORTED_VALS ) {
/* Binary search */
unsigned base = 0, n = a->a_numvals;
int val = 0;
while ( 0 < n ) {
unsigned pivot = n >> 1;
i = base + pivot;
rc = value_match( &match, a->a_desc, mr, flags,
&a->a_nvals[i], cval, &text );
if ( rc == LDAP_SUCCESS && match == 0 )
break;
n = pivot;
if ( match > 0 )
base = i+1;
}
if ( match > 0 )
i++;
} else {
/* Linear search */
for ( i = 0; i < a->a_numvals; i++ ) {
const char *text;
rc = ordered_value_match( &match, a->a_desc, mr, flags,
&a->a_nvals[i], cval, &text );
if ( rc == LDAP_SUCCESS && match == 0 )
break;
}
}
*slot = i;
if ( match )
rc = LDAP_NO_SUCH_ATTRIBUTE;
if ( nval.bv_val )
slap_sl_free( nval.bv_val, ctx );
return rc;
}
int
attr_valadd(
Attribute *a,
......@@ -312,22 +380,52 @@ attr_valadd(
a->a_nvals = a->a_vals;
}
v2 = &a->a_vals[a->a_numvals];
for ( i = 0 ; i < nn; i++ ) {
ber_dupbv( &v2[i], &vals[i] );
if ( BER_BVISNULL( &v2[i] ) ) break;
}
BER_BVZERO( &v2[i] );
if ( nvals ) {
v2 = &a->a_nvals[a->a_numvals];
/* If sorted and old vals exist, must insert */
if (( a->a_flags & SLAP_ATTR_SORTED_VALS ) && a->a_numvals ) {
unsigned slot;
int j, rc;
v2 = nvals ? nvals : vals;
for ( i = 0; i < nn; i++ ) {
rc = attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX |
SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
&v2[i], &slot, NULL );
if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) {
/* should never happen */
if ( rc == LDAP_SUCCESS )
rc = LDAP_TYPE_OR_VALUE_EXISTS;
return rc;
}
for ( j = a->a_numvals; j > slot; j-- ) {
a->a_vals[j+1] = a->a_vals[j];
if ( nvals )
a->a_nvals[j+1] = a->a_nvals[j];
}
ber_dupbv( &a->a_nvals[j], &v2[i] );
if ( nvals )
ber_dupbv( &a->a_vals[j], &vals[i] );
a->a_numvals++;
}
BER_BVZERO( &a->a_vals[a->a_numvals] );
if ( a->a_vals != a->a_nvals )
BER_BVZERO( &a->a_nvals[a->a_numvals] );
} else {
v2 = &a->a_vals[a->a_numvals];
for ( i = 0 ; i < nn; i++ ) {
ber_dupbv( &v2[i], &nvals[i] );
ber_dupbv( &v2[i], &vals[i] );
if ( BER_BVISNULL( &v2[i] ) ) break;
}
BER_BVZERO( &v2[i] );
if ( nvals ) {
v2 = &a->a_nvals[a->a_numvals];
for ( i = 0 ; i < nn; i++ ) {
ber_dupbv( &v2[i], &nvals[i] );
if ( BER_BVISNULL( &v2[i] ) ) break;
}
BER_BVZERO( &v2[i] );
}
a->a_numvals += i;
}
a->a_numvals += i;
return 0;
}
......
......@@ -210,6 +210,7 @@ bdb_monitor_free(
mod.sm_op = LDAP_MOD_DELETE;
mod.sm_desc = slap_schema.si_ad_objectClass;
mod.sm_values = values;
mod.sm_numvals = 1;
values[ 0 ] = oc_olmBDBDatabase->soc_cname;
BER_BVZERO( &values[ 1 ] );
......@@ -218,9 +219,10 @@ bdb_monitor_free(
/* don't care too much about return code... */
/* remove attrs */
mod.sm_values = NULL;
mod.sm_numvals = 0;
for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
mod.sm_desc = *s_at[ i ].ad;
mod.sm_values = NULL;
rc = modify_delete_values( e, &mod, 1, &text,
textbuf, sizeof( textbuf ) );
/* don't care too much about return code... */
......
......@@ -177,6 +177,7 @@ enum {
CFG_HIDDEN,
CFG_MONITORING,
CFG_SERVERID,
CFG_SORTVALS,
CFG_LAST
};
......@@ -552,6 +553,11 @@ static ConfigTable config_back_cf_table[] = {
{ "sockbuf_max_incoming_auth", "max", 2, 2, 0, ARG_BER_LEN_T,
&sockbuf_max_incoming_auth, "( OLcfgGlAt:62 NAME 'olcSockbufMaxIncomingAuth' "
"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
{ "sortvals", "attr", 2, 0, 0, ARG_MAGIC|CFG_SORTVALS,
&config_generic, "( OLcfgGlAt:83 NAME 'olcSortVals' "
"DESC 'Attributes whose values will always be sorted' "
"EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString )", NULL, NULL },
{ "subordinate", "[advertise]", 1, 2, 0, ARG_DB|ARG_MAGIC,
&config_subordinate, "( OLcfgDbAt:0.15 NAME 'olcSubordinate' "
"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
......@@ -765,7 +771,7 @@ static ConfigOCs cf_ocs[] = {
"NAME 'olcFrontendConfig' "
"DESC 'OpenLDAP frontend configuration' "
"AUXILIARY "
"MAY ( olcDefaultSearchBase $ olcPasswordHash ) )",
"MAY ( olcDefaultSearchBase $ olcPasswordHash $ olcSortVals ) )",
Cft_Database, NULL, NULL },
#ifdef SLAPD_MODULES
{ "( OLcfgGlOc:8 "
......@@ -786,6 +792,13 @@ typedef struct ServerID {
static ServerID *sid_list;
typedef struct ADlist {
struct ADlist *al_next;
AttributeDescription *al_desc;
} ADlist;
static ADlist *sortVals;
static int
config_generic(ConfigArgs *c) {
int i;
......@@ -1000,6 +1013,14 @@ config_generic(ConfigArgs *c) {
case CFG_SSTR_IF_MIN:
c->value_int = index_substr_if_minlen;
break;
case CFG_SORTVALS: {
ADlist *sv;
rc = 1;
for ( sv = sortVals; sv; sv = sv->al_next ) {
value_add_one( &c->rvalue_vals, &sv->al_desc->ad_cname );
rc = 0;
}
} break;
#ifdef SLAPD_MODULES
case CFG_MODLOAD: {
ModPaths *mp = c->private;
......@@ -1224,6 +1245,27 @@ config_generic(ConfigArgs *c) {
}
}
break;
case CFG_SORTVALS:
if ( c->valx < 0 ) {
ADlist *sv;
for ( sv = sortVals; sv; sv = sortVals ) {
sortVals = sv->al_next;
sv->al_desc->ad_type->sat_flags &= ~SLAP_AT_SORTED_VAL;
ch_free( sv );
}
} else {
ADlist *sv, **prev;
int i = 0;
for ( prev = &sortVals, sv = sortVals; i < c->valx; i++ ) {
prev = &sv->al_next;
sv = sv->al_next;
}
sv->al_desc->ad_type->sat_flags &= ~SLAP_AT_SORTED_VAL;
*prev = sv->al_next;
ch_free( sv );
}
break;
case CFG_LIMITS:
/* FIXME: there is no limits_free function */
......@@ -1447,6 +1489,52 @@ config_generic(ConfigArgs *c) {
return(1);
break;
case CFG_SORTVALS: {
ADlist *svnew = NULL, *svtail, *sv;
for ( i = 1; i < c->argc; i++ ) {
AttributeDescription *ad = NULL;
const char *text;
int rc;
rc = slap_str2ad( c->argv[i], &ad, &text );
if ( rc ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown attribute type #%d",
c->argv[0], i );
sortval_reject:
Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
c->log, c->cr_msg, c->argv[i] );
for ( sv = svnew; sv; sv = svnew ) {
svnew = sv->al_next;
ch_free( sv );
}
return 1;
}
if (( ad->ad_type->sat_flags & SLAP_AT_ORDERED ) ||
ad->ad_type->sat_single_value ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> inappropriate attribute type #%d",
c->argv[0], i );
goto sortval_reject;
}
sv = ch_malloc( sizeof( ADlist ));
sv->al_desc = ad;
if ( !svnew ) {
svnew = sv;
} else {
svtail->al_next = sv;
}
svtail = sv;
}
for ( sv = svnew; sv; sv = sv->al_next )
sv->al_desc->ad_type->sat_flags |= SLAP_AT_SORTED_VAL;
for ( sv = sortVals; sv && sv->al_next; sv = sv->al_next );
if ( sv )
sv->al_next = svnew;
else
sortVals = svnew;
}
break;
case CFG_ACL:
/* Don't append to the global ACL if we're on a specific DB */
i = c->valx;
......@@ -4544,7 +4632,7 @@ config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs,
*/
if ( ct && ml->sml_values ) {
delrec *d;
for (i=0; ml->sml_values[i].bv_val; i++);
i = ml->sml_numvals;
d = ch_malloc( sizeof(delrec) + (i - 1)* sizeof(int));
d->nidx = i;
d->next = NULL;
......@@ -4577,8 +4665,7 @@ config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs,
if ( ct->arg_type & ARG_NO_INSERT ) {
Attribute *a = attr_find( e->e_attrs, ml->sml_desc );
if ( a ) {
for (i = 0; a->a_vals[i].bv_val; i++ );
navals = i;
navals = a->a_numvals;
}
}
for ( i=0; !BER_BVISNULL( &ml->sml_values[i] ); i++ ) {
......
......@@ -277,6 +277,18 @@ str2entry2( char *s, int checkvals )
if (( ad_prev && ad != ad_prev ) || ( i == lines )) {
int j, k;
/* FIXME: we only need this when migrating from an unsorted DB */
if ( atail != &ahead && atail->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
rc = slap_sort_vals( (Modifications *)atail, &text, &j, NULL );
if ( rc == LDAP_SUCCESS ) {
atail->a_flags |= SLAP_ATTR_SORTED_VALS;
} else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
Debug( LDAP_DEBUG_ANY,
"str2entry: attributeType %s value #%d provided more than once\n",
atail->a_desc->ad_cname.bv_val, j, 0 );
goto fail;
}
}
atail->a_next = attr_alloc( NULL );
atail = atail->a_next;
atail->a_flags = 0;
......@@ -892,6 +904,19 @@ int entry_decode(EntryHeader *eh, Entry **e)
} else {
a->a_nvals = a->a_vals;
}
/* FIXME: This is redundant once a sorted entry is saved into the DB */
if ( a->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
rc = slap_sort_vals( (Modifications *)a, &text, &j, NULL );
if ( rc == LDAP_SUCCESS ) {
a->a_flags |= SLAP_ATTR_SORTED_VALS;
} else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
/* should never happen */
Debug( LDAP_DEBUG_ANY,
"entry_decode: attributeType %s value #%d provided more than once\n",
a->a_desc->ad_cname.bv_val, j, 0 );
return rc;
}
}
a = a->a_next;
nattrs--;
if ( !nattrs )
......
......@@ -640,200 +640,200 @@ int slap_mods_check(
/* check for duplicates, but ignore Deletes.
*/
if( nvals > 1 && ml->sml_op != LDAP_MOD_DELETE ) {
#define SLAP_MODS_CHECK_QUICKSORT
#ifndef SLAP_MODS_CHECK_QUICKSORT
int i, j, rc, match;
MatchingRule *mr = ad->ad_type->sat_equality;
for ( i = 1; i < nvals ; i++ ) {
/* test asserted values against themselves */
for( j = 0; j < i; j++ ) {
rc = ordered_value_match( &match, ml->sml_desc, mr,
SLAP_MR_EQUALITY
| SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX
| SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
| SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
ml->sml_nvalues
? &ml->sml_nvalues[i]
: &ml->sml_values[i],
ml->sml_nvalues
? &ml->sml_nvalues[j]
: &ml->sml_values[j],
text );
if ( rc == LDAP_SUCCESS && match == 0 ) {
/* value exists already */
snprintf( textbuf, textlen,
"%s: value #%d provided more than once",
ml->sml_desc->ad_cname.bv_val, j );
*text = textbuf;
return LDAP_TYPE_OR_VALUE_EXISTS;
} else if ( rc != LDAP_SUCCESS ) {
return rc;
}
}
int i;
rc = slap_sort_vals( ml, text, &i, ctx );
if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
/* value exists already */
snprintf( textbuf, textlen,
"%s: value #%d provided more than once",
ml->sml_desc->ad_cname.bv_val, i );
*text = textbuf;
}
#else /* SLAP_MODS_CHECK_QUICKSORT */
if ( rc )
return rc;
}
} else {
ml->sml_numvals = 0;
}
}
return LDAP_SUCCESS;
}
/* Sort a set of values. An (Attribute *) may be used interchangeably here
* instead of a (Modifications *) structure.
*
* Uses Quicksort + Insertion sort for small arrays
*/
/* Quicksort + Insertion sort for small arrays */
int
slap_sort_vals(
Modifications *ml,
const char **text,
int *dup,
void *ctx )
{
AttributeDescription *ad;
MatchingRule *mr;
int istack[sizeof(int)*16];
int i, j, k, l, ir, jstack, match, *ix, itmp, nvals, rc;
int is_norm;
struct berval a, *cv;
#define SMALL 8
#define SWAP(a,b,tmp) tmp=(a);(a)=(b);(b)=tmp
#define COMP(a,b) match=0; rc = ordered_value_match( &match, \
ml->sml_desc, mr, SLAP_MR_EQUALITY \
ad, mr, SLAP_MR_EQUALITY \
| SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX \
| SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH \
| SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, \
&(a), &(b), text );
MatchingRule *mr = ad->ad_type->sat_equality;
int istack[sizeof(int)*16];
int i, j, k, l, ir, jstack, match, *ix, itmp;
struct berval a, *cv;
/* If PRESERVE_ORDER is defined only the index array is sorted; the
* actual values are left in their incoming order. Otherwise, the
* only reason to keep the index array is to identify the offending
* value when duplicates are found.
*/
#define PRESERVE_ORDER
#ifndef PRESERVE_ORDER
struct berval va, *v, *nv, bvtmp;
#define IX(x) x
#define EXCH(x,y) SWAP(ix[x],ix[y],itmp); SWAP(cv[x],cv[y],bvtmp); \
if (nv) {SWAP(v[x],v[y],bvtmp);}
#define SETA(x) itmp = ix[x]; a = cv[x]; if (nv) va=v[x]
#define GETA(x) ix[x] = itmp; cv[x] = a; if (nv) v[x]=va
#define SET(x,y) ix[x] = ix[y]; cv[x] = cv[y]; if (nv) v[x]=v[y]
v = ml->sml_values;
nv = ml->sml_nvalues;
#else /* PRESERVE_ORDER */
#define IX(x) ix[x]
#define EXCH(x,y) SWAP(ix[x],ix[y],itmp)
#define SETA(x) itmp = ix[x]; a = cv[itmp]
#define GETA(x) ix[x] = itmp;
#define SET(x,y) ix[x] = ix[y]
#endif /* PRESERVE_ORDER */
cv = ml->sml_nvalues ? ml->sml_nvalues : ml->sml_values;
if ( ad == slap_schema.si_ad_objectClass )
mr = NULL; /* shortcut matching */
/* record indices to preserve input ordering */
ix = slap_sl_malloc( nvals * sizeof(int), ctx );
for (i=0; i<nvals; i++) ix[i] = i;
ir = nvals-1;
l = 0;
jstack = 0;
for(;;) {
if (ir - l < SMALL) { /* Insertion sort */
match=1;
for (j=l+1;j<=ir;j++) {
SETA(j);
for (i=j-1;i>=0;i--) {
COMP(cv[IX(i)], a);
if ( match <= 0 )
break;
SET(i+1,i);
}
GETA(i+1);
if ( match == 0 ) goto done;
}
if ( jstack == 0 ) break;
if ( match == 0 ) break;
ir = istack[jstack--];
l = istack[jstack--];
} else {
k = (l + ir) >> 1; /* Choose median of left, center, right */
EXCH(k, l+1);
COMP( cv[IX(l)], cv[IX(ir)] );
if ( match > 0 ) {
EXCH(l, ir);
} else if ( match == 0 ) {
i = ir;
break;
}
COMP( cv[IX(l+1)], cv[IX(ir)] );
if ( match > 0 ) {
EXCH(l+1, ir);
} else if ( match == 0 ) {
i = ir;
break;
}
COMP( cv[IX(l)], cv[IX(l+1)] );
if ( match > 0 ) {
EXCH(l, l+1);
} else if ( match == 0 ) {
i = l;
break;
}
i = l+1;
j = ir;
a = cv[IX(i)];
for(;;) {
do {
i++;
COMP( cv[IX(i)], a );
} while( match < 0 );
while( match > 0 ) {
j--;
COMP( cv[IX(j)], a );
}
if (j < i) {
match = 1;
break;
}
if ( match == 0 ) {
i = l+1;
break;
}
EXCH(i,j);
}
if ( match == 0 )
break;
EXCH(l+1,j);
jstack += 2;
if (ir-i+1 >= j) {
istack[jstack] = ir;
istack[jstack-1] = i;
ir = j;
} else {
istack[jstack] = j;
istack[jstack-1] = l;
l = i;
}
}
}
done:
if ( i >= 0 )
j = ix[i];
ad = ml->sml_desc;
nvals = ml->sml_numvals;
slap_sl_free( ix, ctx );
/* For Modifications, sml_nvalues is NULL if normalization wasn't needed.
* For Attributes, sml_nvalues == sml_values when normalization isn't needed.
*/
if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) {
cv = ml->sml_nvalues;
is_norm = 1;
} else {
cv = ml->sml_values;
is_norm = 0;
}
if ( rc != LDAP_SUCCESS ) {
return rc;
} else if ( match == 0 ) {
/* value exists already */
assert( i >= 0 );
assert( i < nvals );
snprintf( textbuf, textlen,
"%s: value #%d provided more than once",
ml->sml_desc->ad_cname.bv_val, j );
*text = textbuf;
return LDAP_TYPE_OR_VALUE_EXISTS;
if ( ad == slap_schema.si_ad_objectClass )