static int node_cmp( const void* val1, const void* val2 ) { sort_node *sn1 = (sort_node *)val1; sort_node *sn2 = (sort_node *)val2; sort_ctrl *sc; MatchingRule *mr; int i, cmp = 0; assert( sort_conns[sn1->sn_conn] && sort_conns[sn1->sn_conn][sn1->sn_session] && sort_conns[sn1->sn_conn][sn1->sn_session]->so_ctrl ); sc = sort_conns[sn1->sn_conn][sn1->sn_session]->so_ctrl; for ( i=0; cmp == 0 && i<sc->sc_nkeys; i++ ) { if ( BER_BVISNULL( &sn1->sn_vals[i] )) { if ( BER_BVISNULL( &sn2->sn_vals[i] )) cmp = 0; else cmp = sc->sc_keys[i].sk_direction; } else if ( BER_BVISNULL( &sn2->sn_vals[i] )) { cmp = sc->sc_keys[i].sk_direction * -1; } else { mr = sc->sc_keys[i].sk_ordering; mr->smr_match( &cmp, 0, mr->smr_syntax, mr, &sn1->sn_vals[i], &sn2->sn_vals[i] ); if ( cmp ) cmp *= sc->sc_keys[i].sk_direction; } } return cmp; }
/* RFC 2981 Section 2.2 * If a sort key is a multi-valued attribute, and an entry happens to * have multiple values for that attribute and no other controls are * present that affect the sorting order, then the server SHOULD use the * least value (according to the ORDERING rule for that attribute). */ static struct berval* select_value( Attribute *attr, sort_key *key ) { struct berval* ber1, *ber2; MatchingRule *mr = key->sk_ordering; unsigned i; int cmp; ber1 = &(attr->a_nvals[0]); ber2 = ber1+1; for ( i = 1; i < attr->a_numvals; i++,ber2++ ) { mr->smr_match( &cmp, 0, mr->smr_syntax, mr, ber1, ber2 ); if ( cmp > 0 ) { ber1 = ber2; } } Debug(LDAP_DEBUG_TRACE, "%s: value selected for compare: %s\n", debug_header, SAFESTR(ber1->bv_val, "<Empty>"), 0); return ber1; }
static void send_list( Operation *op, SlapReply *rs, sort_op *so) { TAvlnode *cur_node, *tmp_node; vlv_ctrl *vc = op->o_controls[vlv_cid]; int i, j, dir, rc; BackendDB *be; Entry *e; LDAPControl *ctrls[2]; rs->sr_attrs = op->ors_attrs; /* FIXME: it may be better to just flatten the tree into * an array before doing all of this... */ /* Are we just counting an offset? */ if ( BER_BVISNULL( &vc->vc_value )) { if ( vc->vc_offset == vc->vc_count ) { /* wants the last entry in the list */ cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT); so->so_vlv_target = so->so_nentries; } else if ( vc->vc_offset == 1 ) { /* wants the first entry in the list */ cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT); so->so_vlv_target = 1; } else { int target; /* Just iterate to the right spot */ if ( vc->vc_count && vc->vc_count != so->so_nentries ) { if ( vc->vc_offset > vc->vc_count ) goto range_err; target = so->so_nentries * vc->vc_offset / vc->vc_count; } else { if ( vc->vc_offset > so->so_nentries ) { range_err: so->so_vlv_rc = LDAP_VLV_RANGE_ERROR; pack_vlv_response_control( op, rs, so, ctrls ); ctrls[1] = NULL; slap_add_ctrls( op, rs, ctrls ); rs->sr_err = LDAP_VLV_ERROR; return; } target = vc->vc_offset; } so->so_vlv_target = target; /* Start at left and go right, or start at right and go left? */ if ( target < so->so_nentries / 2 ) { cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT); dir = TAVL_DIR_RIGHT; } else { cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT); dir = TAVL_DIR_LEFT; target = so->so_nentries - target + 1; } for ( i=1; i<target; i++ ) cur_node = tavl_next( cur_node, dir ); } } else { /* we're looking for a specific value */ sort_ctrl *sc = so->so_ctrl; MatchingRule *mr = sc->sc_keys[0].sk_ordering; sort_node *sn; struct berval bv; if ( mr->smr_normalize ) { rc = mr->smr_normalize( SLAP_MR_VALUE_OF_SYNTAX, mr->smr_syntax, mr, &vc->vc_value, &bv, op->o_tmpmemctx ); if ( rc ) { so->so_vlv_rc = LDAP_INAPPROPRIATE_MATCHING; pack_vlv_response_control( op, rs, so, ctrls ); ctrls[1] = NULL; slap_add_ctrls( op, rs, ctrls ); rs->sr_err = LDAP_VLV_ERROR; return; } } else { bv = vc->vc_value; } sn = op->o_tmpalloc( sizeof(sort_node) + sc->sc_nkeys * sizeof(struct berval), op->o_tmpmemctx ); sn->sn_vals = (struct berval *)(sn+1); sn->sn_conn = op->o_conn->c_conn_idx; sn->sn_session = find_session_by_so( so->so_info->svi_max_percon, op->o_conn->c_conn_idx, so ); sn->sn_vals[0] = bv; for (i=1; i<sc->sc_nkeys; i++) { BER_BVZERO( &sn->sn_vals[i] ); } cur_node = tavl_find3( so->so_tree, sn, node_cmp, &j ); /* didn't find >= match */ if ( j > 0 ) { if ( cur_node ) cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT ); } op->o_tmpfree( sn, op->o_tmpmemctx ); if ( !cur_node ) { so->so_vlv_target = so->so_nentries + 1; } else { sort_node *sn = so->so_tree->avl_data; /* start from the left or the right side? */ mr->smr_match( &i, 0, mr->smr_syntax, mr, &bv, &sn->sn_vals[0] ); if ( i > 0 ) { tmp_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT); dir = TAVL_DIR_LEFT; } else { tmp_node = tavl_end(so->so_tree, TAVL_DIR_LEFT); dir = TAVL_DIR_RIGHT; } for (i=0; tmp_node != cur_node; tmp_node = tavl_next( tmp_node, dir ), i++); so->so_vlv_target = (dir == TAVL_DIR_RIGHT) ? i+1 : so->so_nentries - i; } if ( bv.bv_val != vc->vc_value.bv_val ) op->o_tmpfree( bv.bv_val, op->o_tmpmemctx ); } if ( !cur_node ) { i = 1; cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT); } else { i = 0; } for ( ; i<vc->vc_before; i++ ) { tmp_node = tavl_next( cur_node, TAVL_DIR_LEFT ); if ( !tmp_node ) break; cur_node = tmp_node; } j = i + vc->vc_after + 1; be = op->o_bd; for ( i=0; i<j; i++ ) { sort_node *sn = cur_node->avl_data; if ( slapd_shutdown ) break; op->o_bd = select_backend( &sn->sn_dn, 0 ); e = NULL; rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e ); if ( e && rc == LDAP_SUCCESS ) { rs->sr_entry = e; rs->sr_flags = REP_ENTRY_MUSTRELEASE; rs->sr_err = send_search_entry( op, rs ); if ( rs->sr_err == LDAP_UNAVAILABLE ) break; } cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT ); if ( !cur_node ) break; } so->so_vlv_rc = LDAP_SUCCESS; op->o_bd = be; }