/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 1999-2006 The OpenLDAP Foundation.
+ * Copyright 1999-2011 The OpenLDAP Foundation.
* Portions Copyright 1999 Dmitry Kovalev.
* Portions Copyright 2002 Pierangelo Masarati.
* Portions Copyright 2004 Mark Adamson.
static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f,
backsql_at_map_rec *at );
+/* For LDAP_CONTROL_PAGEDRESULTS, a 32 bit cookie is available to keep track of
+ the state of paged results. The ldap_entries.id and oc_map_id values of the
+ last entry returned are used as the cookie, so 6 bits are used for the OC id
+ and the other 26 for ldap_entries ID number. If your max(oc_map_id) is more
+ than 63, you will need to steal more bits from ldap_entries ID number and
+ put them into the OC ID part of the cookie. */
+
+/* NOTE: not supported when BACKSQL_ARBITRARY_KEY is defined */
+#ifndef BACKSQL_ARBITRARY_KEY
+#define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F))
+#define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6)
+#define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F)
+
+static int parse_paged_cookie( Operation *op, SlapReply *rs );
+
+static void send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid );
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
static int
backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
{
return 1;
}
+ /* strip ';binary' */
+ if ( slap_ad_is_binary( ad ) ) {
+ ad = ad->ad_type->sat_ad;
+ }
+
for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
an = &bsi->bsi_attrs[ n_attrs ];
BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
- if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
+ if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
/* handle "*" */
bsi->bsi_flags |= BSQL_SF_ALL_USER;
}
continue;
- } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
+ } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
/* handle "+" */
bsi->bsi_flags |= BSQL_SF_ALL_OPER;
}
continue;
- } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
+ } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_no_attrs ) == 0 ) {
/* ignore "1.1" */
continue;
/* use hints if available */
for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
- if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
+ if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
/* handle "*" */
bsi->bsi_flags |= BSQL_SF_ALL_USER;
}
continue;
- } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
+ } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
/* handle "+" */
bsi->bsi_flags |= BSQL_SF_ALL_OPER;
rs->sr_err = rc;
}
}
+
+ if ( gotit && BACKSQL_IS_GET_OC( flags ) ) {
+ bsi->bsi_base_id.eid_oc = backsql_id2oc( bi,
+ bsi->bsi_base_id.eid_oc_id );
+ if ( bsi->bsi_base_id.eid_oc == NULL ) {
+ /* error? */
+ backsql_free_entryID( &bsi->bsi_base_id, 1,
+ op->o_tmpmemctx );
+ rc = rs->sr_err = LDAP_OTHER;
+ }
+ }
}
bsi->bsi_status = rc;
goto done;
}
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "1=0" ), "1=0" );
+ done = 1;
+ rc = 1;
+ goto done;
+ }
+
switch( f->f_choice ) {
case LDAP_FILTER_OR:
rc = backsql_process_filter_list( bsi, f->f_or,
struct berval keyval;
#else /* ! BACKSQL_ARBITRARY_KEY */
unsigned long keyval;
- char keyvalbuf[] = "18446744073709551615";
+ char keyvalbuf[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
#endif /* ! BACKSQL_ARBITRARY_KEY */
switch ( f->f_choice ) {
#ifdef BACKSQL_SYNCPROV
} else if ( ad == slap_schema.si_ad_entryCSN ) {
/*
- * support for syncrepl as producer...
+ * support for syncrepl as provider...
*/
#if 0
if ( !bsi->bsi_op->o_sync ) {
&at->bam_join_where );
}
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "1=0" ), "1=0" );
+ return 1;
+ }
+
switch ( f->f_choice ) {
case LDAP_FILTER_EQUALITY:
filter_value = &f->f_av_value;
(ber_len_t)STRLENOF( "9=9"), "9=9");
} else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
+ /* This should always be true... */
backsql_strfcat_x( &bsi->bsi_join_where,
bsi->bsi_op->o_tmpmemctx,
"b",
assert( 0 );
}
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* If paged results are in effect, ignore low ldap_entries.id numbers */
+ if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
+ unsigned long lowid = 0;
+
+ /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
+ if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
+ {
+ lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
+ }
+
+ if ( lowid ) {
+ char lowidstring[48];
+ int lowidlen;
+
+ lowidlen = snprintf( lowidstring, sizeof( lowidstring ),
+ " AND ldap_entries.id>%lu", lowid );
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)lowidlen,
+ lowidstring );
+ }
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
rc = backsql_process_filter( bsi, bsi->bsi_filter );
if ( rc > 0 ) {
struct berbuf bb = BB_NULL;
return BACKSQL_AVL_STOP;
}
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* If paged results have already completed this objectClass, skip it */
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
+ {
+ return BACKSQL_AVL_CONTINUE;
+ }
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
if ( bsi->bsi_n_candidates == -1 ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"unchecked limit has been overcome\n", 0, 0, 0 );
return BACKSQL_AVL_CONTINUE;
}
- Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
+ Debug( LDAP_DEBUG_TRACE, "id: '" BACKSQL_IDNUMFMT "'\n",
+ bsi->bsi_oc->bom_id, 0, 0 );
- rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
+ rc = backsql_BindParamNumID( sth, 1, SQL_PARAM_INPUT,
&bsi->bsi_oc->bom_id );
if ( rc != SQL_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
case LDAP_SCOPE_ONELEVEL:
assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
-#ifdef BACKSQL_ARBITRARY_KEY
- Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
- bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
-#else /* ! BACKSQL_ARBITRARY_KEY */
- Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
- bsi->bsi_base_id.eid_id, 0, 0 );
-#endif /* ! BACKSQL_ARBITRARY_KEY */
+ Debug( LDAP_DEBUG_TRACE, "(one)id=" BACKSQL_IDFMT "\n",
+ BACKSQL_IDARG(bsi->bsi_base_id.eid_id), 0, 0 );
rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
&bsi->bsi_base_id.eid_id );
if ( rc != SQL_SUCCESS ) {
ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
op->o_tmpmemctx );
#else /* ! BACKSQL_ARBITRARY_KEY */
- if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
+ if ( BACKSQL_STR2ID( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
goto cleanup;
}
- if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
+ if ( BACKSQL_STR2ID( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
goto cleanup;
}
#endif /* ! BACKSQL_ARBITRARY_KEY */
+ c_id->eid_oc = bsi->bsi_oc;
c_id->eid_oc_id = bsi->bsi_oc->bom_id;
c_id->eid_dn = pdn;
*bsi->bsi_id_listtail = c_id;
bsi->bsi_id_listtail = &c_id->eid_next;
-#ifdef BACKSQL_ARBITRARY_KEY
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
- "added entry id=%s, keyval=%s dn=\"%s\"\n",
- c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
+ "added entry id=" BACKSQL_IDFMT " keyval=" BACKSQL_IDFMT " dn=\"%s\"\n",
+ BACKSQL_IDARG(c_id->eid_id),
+ BACKSQL_IDARG(c_id->eid_keyval),
row.cols[ 3 ] );
-#else /* ! BACKSQL_ARBITRARY_KEY */
- Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
- "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
- c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
-#endif /* ! BACKSQL_ARBITRARY_KEY */
/* count candidates, for unchecked limit */
bsi->bsi_n_candidates--;
backsql_srch_info bsi = { 0 };
backsql_entryID *eid = NULL;
struct berval nbase = BER_BVNULL;
+#ifndef BACKSQL_ARBITRARY_KEY
+ ID lastid = 0;
+#endif /* ! BACKSQL_ARBITRARY_KEY */
Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
"base=\"%s\", filter=\"%s\", scope=%d,",
( op->ors_limit->lms_s_unchecked == -1 ? -2 :
( op->ors_limit->lms_s_unchecked ) ) );
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* If paged results are in effect, check the paging cookie */
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ rs->sr_err = parse_paged_cookie( op, rs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
switch ( bsi.bsi_scope ) {
case LDAP_SCOPE_BASE:
case BACKSQL_SCOPE_BASE_LIKE:
/*
* for each objectclass we try to construct query which gets IDs
* of entries matching LDAP query filter and scope (or at least
- * candidates), and get the IDs
+ * candidates), and get the IDs. Do this in ID order for paging.
*/
- avl_apply( bi->sql_oc_by_oc, backsql_oc_get_candidates,
+ avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
&bsi, BACKSQL_AVL_STOP, AVL_INORDER );
/* check for abandon */
* and then send to client; don't free entry_id if baseObject...
*/
for ( eid = bsi.bsi_id_list;
- eid != NULL;
- eid = backsql_free_entryID( op,
- eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
+ eid != NULL;
+ eid = backsql_free_entryID(
+ eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
{
int rc;
Attribute *a_hasSubordinate = NULL,
goto send_results;
}
-#ifdef BACKSQL_ARBITRARY_KEY
Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
- "for entry id=%s, oc_id=%ld, keyval=%s\n",
- eid->eid_id.bv_val, eid->eid_oc_id,
- eid->eid_keyval.bv_val );
-#else /* ! BACKSQL_ARBITRARY_KEY */
- Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
- "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
- eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
-#endif /* ! BACKSQL_ARBITRARY_KEY */
+ "for entry id=" BACKSQL_IDFMT " oc_id=" BACKSQL_IDNUMFMT ", keyval=" BACKSQL_IDFMT "\n",
+ BACKSQL_IDARG(eid->eid_id),
+ eid->eid_oc_id,
+ BACKSQL_IDARG(eid->eid_keyval) );
/* check scope */
switch ( op->ors_scope ) {
case LDAP_SCOPE_SUBTREE:
/* FIXME: this should never fail... */
if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
- assert( 0 );
goto next_entry2;
}
break;
rs->sr_ref = NULL;
rs->sr_matched = NULL;
rs->sr_entry = NULL;
+ if ( rs->sr_err == LDAP_REFERRAL ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
goto next_entry;
}
if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
{
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* If paged results are in effect, see if the page limit was exceeded */
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
+ {
+ e = NULL;
+ send_paged_response( op, rs, &lastid );
+ goto done;
+ }
+ lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
rs->sr_attrs = op->ors_attrs;
rs->sr_operational_attrs = NULL;
rs->sr_entry = e;
+ e->e_private = (void *)eid;
rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
/* FIXME: need the whole entry (ITS#3480) */
rs->sr_err = send_search_entry( op, rs );
+ e->e_private = NULL;
rs->sr_entry = NULL;
rs->sr_attrs = NULL;
rs->sr_operational_attrs = NULL;
send_results:;
if ( rs->sr_err != SLAPD_ABANDON ) {
- send_ldap_result( op, rs );
+#ifndef BACKSQL_ARBITRARY_KEY
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ send_paged_response( op, rs, NULL );
+ } else
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ {
+ send_ldap_result( op, rs );
+ }
}
/* cleanup in case of abandon */
for ( ; eid != NULL;
- eid = backsql_free_entryID( op,
- eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
+ eid = backsql_free_entryID(
+ eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
;
backsql_entry_clean( op, &base_entry );
#ifdef BACKSQL_SYNCPROV
if ( op->o_sync ) {
Operation op2 = *op;
- SlapReply rs2 = { 0 };
- Entry e = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Entry *e = entry_alloc();
slap_callback cb = { 0 };
op2.o_tag = LDAP_REQ_ADD;
- op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0, 0 );
- op2.ora_e = &e;
+ op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
+ op2.ora_e = e;
op2.o_callback = &cb;
- e.e_name = op->o_bd->be_suffix[0];
- e.e_nname = op->o_bd->be_nsuffix[0];
+ ber_dupbv( &e->e_name, op->o_bd->be_suffix );
+ ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
cb.sc_response = slap_null_cb;
op2.o_bd->be_add( &op2, &rs2 );
+
+ if ( op2.ora_e == e )
+ entry_free( e );
}
#endif /* BACKSQL_SYNCPROV */
done:;
- (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
if ( bsi.bsi_attrs != NULL ) {
op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
*ent = NULL;
rc = backsql_get_db_conn( op, &dbh );
- if ( !dbh ) {
- return LDAP_OTHER;
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
}
if ( at ) {
BER_BVZERO( &anlist[ 1 ].an_name );
}
- bsi.bsi_e = ch_malloc( sizeof( Entry ) );
+ bsi.bsi_e = entry_alloc();
rc = backsql_init_search( &bsi,
ndn,
LDAP_SCOPE_BASE,
BACKSQL_ISF_GET_ENTRY );
if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
- (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
}
if ( rc == LDAP_SUCCESS ) {
{
backsql_entry_clean( op, e );
- ch_free( e );
+ entry_free( e );
return 0;
}
+
+#ifndef BACKSQL_ARBITRARY_KEY
+/* This function is copied verbatim from back-bdb/search.c */
+static int
+parse_paged_cookie( Operation *op, SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+ PagedResultsState *ps = op->o_pagedresults_state;
+
+ /* this function must be invoked only if the pagedResults
+ * control has been detected, parsed and partially checked
+ * by the frontend */
+ assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
+
+ /* cookie decoding/checks deferred to backend... */
+ if ( ps->ps_cookieval.bv_len ) {
+ PagedResultsCookie reqcookie;
+ if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
+
+ if ( reqcookie > ps->ps_cookie ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+
+ } else if ( reqcookie < ps->ps_cookie ) {
+ rs->sr_text = "paged results cookie is invalid or old";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ } else {
+ /* Initial request. Initialize state. */
+ ps->ps_cookie = 0;
+ ps->ps_count = 0;
+ }
+
+done:;
+
+ return rc;
+}
+
+/* This function is copied nearly verbatim from back-bdb/search.c */
+static void
+send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid )
+{
+ LDAPControl ctrl, *ctrls[2];
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ PagedResultsCookie respcookie;
+ struct berval cookie;
+
+ Debug(LDAP_DEBUG_ARGS,
+ "send_paged_response: lastid=0x%08lx nentries=%d\n",
+ lastid ? *lastid : 0, rs->sr_nentries, NULL );
+
+ BER_BVZERO( &ctrl.ldctl_value );
+ ctrls[0] = &ctrl;
+ ctrls[1] = NULL;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if ( lastid ) {
+ respcookie = ( PagedResultsCookie )(*lastid);
+ cookie.bv_len = sizeof( respcookie );
+ cookie.bv_val = (char *)&respcookie;
+
+ } else {
+ respcookie = ( PagedResultsCookie )0;
+ BER_BVSTR( &cookie, "" );
+ }
+
+ op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
+ op->o_conn->c_pagedresults_state.ps_count =
+ ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
+ rs->sr_nentries;
+
+ /* return size of 0 -- no estimate */
+ ber_printf( ber, "{iO}", 0, &cookie );
+
+ if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
+ goto done;
+ }
+
+ ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ ctrls[0]->ldctl_iscritical = 0;
+
+ rs->sr_ctrls = ctrls;
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result( op, rs );
+ rs->sr_ctrls = NULL;
+
+done:
+ (void) ber_free_buf( ber );
+}
+#endif /* ! BACKSQL_ARBITRARY_KEY */