]> git.sur5r.net Git - openldap/commitdiff
add (and honor) a (configurable) baseObject to workaround ldap_entries view for RDBMS...
authorPierangelo Masarati <ando@openldap.org>
Sat, 2 Oct 2004 17:33:32 +0000 (17:33 +0000)
committerPierangelo Masarati <ando@openldap.org>
Sat, 2 Oct 2004 17:33:32 +0000 (17:33 +0000)
doc/man/man5/slapd-sql.5
servers/slapd/back-sql/back-sql.h
servers/slapd/back-sql/bind.c
servers/slapd/back-sql/compare.c
servers/slapd/back-sql/config.c
servers/slapd/back-sql/entry-id.c
servers/slapd/back-sql/init.c
servers/slapd/back-sql/proto-sql.h
servers/slapd/back-sql/search.c

index 7c5e33e5d63963dfedeae78684844206e190db95..e49b7c75f90e14d15f55041374a21cb0edd01b3a 100644 (file)
@@ -107,7 +107,7 @@ Note that the parameter number and order must not be changed.
 Specifies the name of a function that converts a given value to uppercase.
 This is used for CIS matching when the RDBMS is case sensitive.
 .TP
-.B upper_needs_cast { yes | no }
+.B upper_needs_cast { NO | yes}
 Set this directive to 
 .B yes
 if 
@@ -151,7 +151,7 @@ This is
 .B experimental 
 and may change in future releases.
 .TP
-.B has_ldapinfo_dn_ru { yes | no }
+.B has_ldapinfo_dn_ru { NO | yes }
 Explicitly inform the backend whether the SQL schema has dn_ru column
 (dn in reverse uppercased form) or not.
 Overrides automatic check (required by PostgreSQL/unixODBC).
@@ -160,7 +160,7 @@ This is
 and may change in future releases.
 
 .TP
-.B fail_if_no_mapping { yes | no }
+.B fail_if_no_mapping { NO | yes }
 When set to
 .B yes
 it forces
@@ -181,6 +181,37 @@ This is
 .B experimental 
 and may change in future releases.
 
+.TP
+.B allow_orphans { NO | yes }
+When set to 
+.B yes
+orphaned entries (i.e. without the parent entry in the database)
+can be added.  This option should be used with care, possibly 
+in conjunction with some special rule on the RDBMS side that
+dynamically creates the missing parent.
+
+.TP
+.B baseObject [filename]
+Instructs the database to create and manage an in-memory baseObject
+entry instead of looking for one in the RDBMS.
+If the (optional) 
+.B filename
+argument is given, the entry is read from file
+.B filename
+in
+.BR LDIF (5)
+form.
+This is particularly useful when
+.B ldap_entries 
+information is stored in a view rather than in a table, and 
+.B union
+is not supported for views, so that the view can only specify
+one rule to compute the entry structure for one objectClass.
+This topic is discussed further in section "METAINFORMATION USED".
+This is
+.B experimental 
+and may change in future releases.
+
 .SH METAINFORMATION USED
 .LP
 Almost everything mentioned later is illustrated in examples located
@@ -369,13 +400,26 @@ like this (by Robin Elfrink):
 .LP
 .nf
   CREATE VIEW ldap_entries (id, dn, oc_map_id, parent, keyval)
-      AS SELECT (1000000000+userid),
+      AS
+          SELECT 0, UPPER('o=MyCompany,c=NL'),
+  3, 0, 'baseObject' FROM unixusers WHERE userid='root' UNION
+          SELECT (1000000000+userid),
   UPPER(CONCAT(CONCAT('cn=',gecos),',o=MyCompany,c=NL')),
   1, 0, userid FROM unixusers UNION
           SELECT (2000000000+groupnummer),
   UPPER(CONCAT(CONCAT('cn=',groupnaam),',o=MyCompany,c=NL')),
   2, 0, groupnummer FROM groups;
 .fi
+
+.LP
+If your RDBMS does not support
+.B unions
+in views, only one objectClass can be mapped in
+.BR ldap_entries ,
+and the baseObject cannot be created; in this case, see the 
+.B baseObject
+directive for a possible workaround.
+
 .LP
 .SH Typical SQL backend operation
 Having metainformation loaded, the SQL backend uses these tables to
index 4e3803c9e4314db766f7e63dbb1f3f57d948bcb7..fa74a36d2a1c7c05c2d8354f54d93bfb26304cb0 100644 (file)
@@ -357,6 +357,19 @@ typedef struct {
        (!BER_BVISNULL( &(si)->sql_upper_func ))
 #define BACKSQL_ALLOW_ORPHANS(si) \
        ((si)->sql_flags & BSQLF_ALLOW_ORPHANS)
+
+       Entry           *sql_baseObject;
+#ifdef BACKSQL_ARBITRARY_KEY
+#define BACKSQL_BASEOBJECT_IDSTR       "baseObject"
+#define BACKSQL_BASEOBJECT_KEYVAL      BACKSQL_BASEOBJECT_IDSTR
+#define        BACKSQL_IS_BASEOBJECT_ID(id)    (bvmatch((id), &backsql_baseObject_bv)
+#else /* ! BACKSQL_ARBITRARY_KEY */
+#define BACKSQL_BASEOBJECT_ID          0
+#define BACKSQL_BASEOBJECT_IDSTR       "0"
+#define BACKSQL_BASEOBJECT_KEYVAL      0
+#define        BACKSQL_IS_BASEOBJECT_ID(id)    (*(id) == BACKSQL_BASEOBJECT_ID)
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+#define BACKSQL_BASEOBJECT_OC          0
        
        Avlnode         *sql_db_conns;
        Avlnode         *sql_oc_by_oc;
index 461ef8d18c1da0661faf89eb07a0f00c43c4b1a2..836b865fa4d758602f368a335a672d99ba109638 100644 (file)
@@ -73,7 +73,7 @@ backsql_bind( Operation *op, SlapReply *rs )
                return 1;
        }
 
-       dn = op->o_req_dn;
+       dn = op->o_req_ndn;
        if ( backsql_api_dn2odbc( op, rs, &dn ) ) {
                Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
                        "backsql_api_dn2odbc failed\n", 
index 5296f46beda05d2f3188963752ab488445ddf31b..ab2a2d0bdaf34e01d81515bc26b04636241cf1e1 100644 (file)
@@ -58,7 +58,7 @@ backsql_compare( Operation *op, SlapReply *rs )
                goto return_results;
        }
 
-       dn = op->o_req_dn;
+       dn = op->o_req_ndn;
        if ( backsql_api_dn2odbc( op, rs, &dn ) ) {
                Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
                        "backsql_api_dn2odbc failed\n", 
index 6fe8a912072e0278599e3b1963b610c51899103d..afc0dcf09a40cfec777dfe4805f38eb2ae080055 100644 (file)
 #include <sys/types.h>
 
 #include "slap.h"
+#include "ldif.h"
 #include "proto-sql.h"
 
+static int
+create_baseObject(
+       BackendDB       *be,
+       const char      *fname,
+       int             lineno );
+
+static int
+read_baseObject(
+       BackendDB       *be,
+       const char      *fname );
+
 int
 backsql_db_config(
        BackendDB       *be,
@@ -389,6 +401,39 @@ backsql_db_config(
                        "allow_orphans=%s\n", 
                        BACKSQL_ALLOW_ORPHANS( bi ) ? "yes" : "no", 0, 0 );
 
+       } else if ( !strcasecmp( argv[ 0 ], "baseobject" ) ) {
+               if ( be->be_suffix == NULL ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<==backsql_db_config (%s line %d): : "
+                               "must be defined after \"suffix\"\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
+               if ( bi->sql_baseObject ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<==backsql_db_config (%s line %d): : "
+                               "\"baseObject\" already provided (will be overwritten)\n",
+                               fname, lineno, 0 );
+                       entry_free( bi->sql_baseObject );
+               }
+       
+               switch ( argc ) {
+               case 1:
+                       return create_baseObject( be, fname, lineno );
+
+               case 2:
+                       return read_baseObject( be, argv[ 1 ] );
+
+               default:
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<==backsql_db_config (%s line %d): "
+                               "trailing values "
+                               "in \"baseObject\" directive?\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
        } else if ( !strcasecmp( argv[ 0 ], "sqllayer") ) {
                if ( backsql_api_config( bi, argv[ 1 ] ) ) {
                        Debug( LDAP_DEBUG_TRACE,
@@ -405,5 +450,207 @@ backsql_db_config(
        return 0;
 }
 
+/*
+ * Read the entries specified in fname and merge the attributes
+ * to the user defined baseObject entry. Note that if we find any errors
+ * what so ever, we will discard the entire entries, print an
+ * error message and return.
+ */
+static int
+read_baseObject( 
+       BackendDB       *be,
+       const char      *fname )
+{
+       backsql_info    *bi = (backsql_info *)be->be_private;
+       FILE            *fp;
+       int             rc = 0, lineno = 0, lmax = 0;
+       char            *buf = NULL;
+
+       assert( fname );
+
+       fp = fopen( fname, "r" );
+       if ( fp == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "could not open back-sql baseObject attr file \"%s\" - absolute path?\n",
+                       fname, 0, 0 );
+               perror( fname );
+               return LDAP_OTHER;
+       }
+
+       bi->sql_baseObject = (Entry *) SLAP_CALLOC( 1, sizeof(Entry) );
+       if ( bi->sql_baseObject == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                       "read_baseObject_file: SLAP_CALLOC failed", 0, 0, 0 );
+               fclose( fp );
+               return LDAP_NO_MEMORY;
+       }
+       bi->sql_baseObject->e_name = be->be_suffix[0];
+       bi->sql_baseObject->e_nname = be->be_nsuffix[0];
+       bi->sql_baseObject->e_attrs = NULL;
+
+       while ( ldif_read_record( fp, &lineno, &buf, &lmax ) ) {
+               Entry           *e = str2entry( buf );
+               Attribute       *a;
+
+               if( e == NULL ) {
+                       fprintf( stderr, "back-sql baseObject: could not parse entry (line=%d)\n",
+                               lineno );
+                       rc = LDAP_OTHER;
+                       break;
+               }
+
+               /* make sure the DN is the database's suffix */
+               if ( !be_issuffix( be, &e->e_nname ) ) {
+                       fprintf( stderr,
+                               "back-sql: invalid baseObject - dn=\"%s\" (line=%d)\n",
+                               e->e_dn, lineno );
+                       entry_free( e );
+                       rc = EXIT_FAILURE;
+                       break;
+               }
+
+               /*
+                * we found a valid entry, so walk thru all the attributes in the
+                * entry, and add each attribute type and description to baseObject
+                */
+               for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+                       if ( attr_merge( bi->sql_baseObject, a->a_desc, a->a_vals,
+                               ( a->a_nvals == a->a_vals ) ? NULL : a->a_nvals ) )
+                       {
+                               rc = LDAP_OTHER;
+                               break;
+                       }
+               }
+
+               entry_free( e );
+               if ( rc ) {
+                       break;
+               }
+       }
+
+       if ( rc ) {
+               entry_free( bi->sql_baseObject );
+               bi->sql_baseObject = NULL;
+       }
+
+       ch_free( buf );
+
+       fclose( fp );
+
+       Debug( LDAP_DEBUG_CONFIG, "back-sql baseObject file \"%s\" read.\n", fname, 0, 0 );
+
+       return rc;
+}
+
+static int
+create_baseObject(
+       BackendDB       *be,
+       const char      *fname,
+       int             lineno )
+{
+       backsql_info    *bi = (backsql_info *)be->be_private;
+       LDAPRDN         rdn;
+       char            *p;
+       int             rc, iAVA;
+       char            buf[1024];
+
+       snprintf( buf, sizeof(buf),
+                       "dn: %s\n"
+                       "objectClass: extensibleObject\n"
+                       "description: builtin baseObject for back-sql\n"
+                       "description: all entries mapped in the \"ldap_entries\" table\n"
+                       "description: must have \"" BACKSQL_BASEOBJECT_IDSTR "\" "
+                               "in the \"parent\" column",
+                       be->be_suffix[0].bv_val );
+
+       bi->sql_baseObject = str2entry( buf );
+       if ( bi->sql_baseObject == NULL ) {
+               Debug( LDAP_DEBUG_TRACE,
+                       "<==backsql_db_config (%s line %d): "
+                       "unable to parse baseObject entry\n",
+                       fname, lineno, 0 );
+               return 1;
+       }
+
+       if ( BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ) {
+               return 0;
+       }
+
+       rc = ldap_bv2rdn( &be->be_suffix[ 0 ], &rdn, (char **) &p, LDAP_DN_FORMAT_LDAP );
+       if ( rc != LDAP_SUCCESS ) {
+               snprintf( buf, sizeof(buf),
+                       "unable to extract RDN from baseObject DN \"%s\" (%d: %s)",
+                       be->be_suffix[ 0 ].bv_val, rc, ldap_err2string( rc ) );
+               Debug( LDAP_DEBUG_TRACE,
+                       "<==backsql_db_config (%s line %d): %s\n",
+                       fname, lineno, buf );
+               return 1;
+       }
+
+       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+               LDAPAVA                         *ava = rdn[ iAVA ];
+               AttributeDescription            *ad = NULL;
+               slap_syntax_transform_func      *transf = NULL;
+               struct berval                   bv = BER_BVNULL;
+               const char                      *text = NULL;
+
+               assert( ava );
+
+               rc = slap_bv2ad( &ava->la_attr, &ad, &text );
+               if ( rc != LDAP_SUCCESS ) {
+                       snprintf( buf, sizeof(buf),
+                               "AttributeDescription of naming "
+                               "attribute #%d from baseObject "
+                               "DN \"%s\": %d: %s",
+                               iAVA, be->be_suffix[ 0 ].bv_val,
+                               rc, ldap_err2string( rc ) );
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<==backsql_db_config (%s line %d): %s\n",
+                               fname, lineno, buf );
+                       return 1;
+               }
+               
+               transf = ad->ad_type->sat_syntax->ssyn_pretty;
+               if ( transf ) {
+                       /*
+                        * transform value by pretty function
+                        *      if value is empty, use empty_bv
+                        */
+                       rc = ( *transf )( ad->ad_type->sat_syntax,
+                               ava->la_value.bv_len
+                                       ? &ava->la_value
+                                       : (struct berval *) &slap_empty_bv,
+                               &bv, NULL );
+       
+                       if ( rc != LDAP_SUCCESS ) {
+                               snprintf( buf, sizeof(buf),
+                                       "prettying of attribute #%d from baseObject "
+                                       "DN \"%s\" failed: %d: %s",
+                                       iAVA, be->be_suffix[ 0 ].bv_val,
+                                       rc, ldap_err2string( rc ) );
+                               Debug( LDAP_DEBUG_TRACE,
+                                       "<==backsql_db_config (%s line %d): %s\n",
+                                       fname, lineno, buf );
+                               return 1;
+                       }
+               }
+
+               if ( !BER_BVISNULL( &bv ) ) {
+                       if ( ava->la_flags & LDAP_AVA_FREE_VALUE ) {
+                               ber_memfree( ava->la_value.bv_val );
+                       }
+                       ava->la_value = bv;
+                       ava->la_flags |= LDAP_AVA_FREE_VALUE;
+               }
+
+               attr_merge_normalize_one( bi->sql_baseObject,
+                               ad, &ava->la_value, NULL );
+       }
+
+       ldap_rdnfree( rdn );
+
+       return 0;
+}
+
 #endif /* SLAPD_SQL */
 
index 35108c34cbe5d8e6c2c1580cdf0e6bd077ee6ac2..13d9840dbb64f4f6d1a29a0b64b083fcc892df34 100644 (file)
 #include "slap.h"
 #include "proto-sql.h"
 
+#ifdef BACKSQL_ARBITRARY_KEY
+struct berval backsql_baseObject_bv = BER_BVC( BACKSQL_BASEOBJECT_IDSTR );
+#endif /* BACKSQL_ARBITRARY_KEY */
+
 backsql_entryID *
 backsql_free_entryID( backsql_entryID *id, int freeit )
 {
@@ -40,15 +44,18 @@ backsql_free_entryID( backsql_entryID *id, int freeit )
 
        if ( id->eid_dn.bv_val != NULL ) {
                free( id->eid_dn.bv_val );
+               BER_BVZERO( &id->eid_dn );
        }
 
 #ifdef BACKSQL_ARBITRARY_KEY
        if ( id->eid_id.bv_val ) {
                free( id->eid_id.bv_val );
+               BER_BVZERO( &id->eid_id );
        }
 
        if ( id->eid_keyval.bv_val ) {
                free( id->eid_keyval.bv_val );
+               BER_BVZERO( &id->eid_keyval );
        }
 #endif /* BACKSQL_ARBITRARY_KEY */
 
@@ -93,9 +100,29 @@ backsql_dn2id(
                        dn->bv_val, dn->bv_len, BACKSQL_MAX_DN_LEN );
                return LDAP_OTHER;
        }
+
+       /* return baseObject if available and matches */
+       if ( bi->sql_baseObject != NULL && bvmatch( dn, &bi->sql_baseObject->e_nname ) ) {
+               if ( id != NULL ) {
+#ifdef BACKSQL_ARBITRARY_KEY
+                       ber_dupbv( &id->eid_id, &backsql_baseObject_bv );
+                       ber_dupbv( &id->eid_keyval, &backsql_baseObject_bv );
+#else /* ! BACKSQL_ARBITRARY_KEY */
+                       id->eid_id = BACKSQL_BASEOBJECT_ID;
+                       id->eid_keyval = BACKSQL_BASEOBJECT_KEYVAL;
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+                       id->eid_oc_id = BACKSQL_BASEOBJECT_OC;
+
+                       ber_dupbv( &id->eid_dn, &bi->sql_baseObject->e_nname );
+
+                       id->eid_next = NULL;
+               }
+
+               return LDAP_SUCCESS;
+       }
        
        /* begin TimesTen */
-       Debug(LDAP_DEBUG_TRACE, "id_query \"%s\"\n", bi->sql_id_query, 0, 0);
+       Debug( LDAP_DEBUG_TRACE, "id_query \"%s\"\n", bi->sql_id_query, 0, 0 );
        assert( bi->sql_id_query );
        rc = backsql_Prepare( dbh, &sth, bi->sql_id_query, 0 );
        if ( rc != SQL_SUCCESS ) {
@@ -405,6 +432,7 @@ backsql_get_attr_vals( void *v_at, void *v_bsi )
 int
 backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *eid )
 {
+       backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
        int                     i;
        int                     rc;
        AttributeDescription    *ad_oc = slap_schema.si_ad_objectClass;
@@ -415,6 +443,19 @@ backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *eid )
 
        memset( bsi->bsi_e, 0, sizeof( Entry ) );
 
+       if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
+               Entry   *e;
+
+               e = entry_dup( bi->sql_baseObject );
+               if ( e == NULL ) {
+                       return LDAP_NO_MEMORY;
+               }
+                       
+               *bsi->bsi_e = *e;
+               free( e );
+               goto done;
+       }
+
        rc = dnPrettyNormal( NULL, &eid->eid_dn,
                        &bsi->bsi_e->e_name, &bsi->bsi_e->e_nname,
                        bsi->bsi_op->o_tmpmemctx );
@@ -528,6 +569,7 @@ next:;
                }
        }
 
+done:;
        Debug( LDAP_DEBUG_TRACE, "<==backsql_id2entry()\n", 0, 0, 0 );
 
        return LDAP_SUCCESS;
index 45669f72360ee0a14b2de8144715e950799766a3..e4eb0c5fcc2e138749b3f95255cb5897f2d128c1 100644 (file)
@@ -163,6 +163,11 @@ backsql_db_destroy(
        free( bi->sql_delentry_query );
        free( bi->sql_delobjclasses_query );
        free( bi->sql_delreferrals_query );
+
+       if ( bi->sql_baseObject ) {
+               entry_free( bi->sql_baseObject );
+       }
+       
        free( bi );
        
        Debug( LDAP_DEBUG_TRACE, "<==backsql_db_destroy()\n", 0, 0, 0 );
index 2f623900c4ba9ec2cd73d56cee28f562443d4650..a24b902e425b289778883e69ad88f0aa272e849b 100644 (file)
@@ -105,6 +105,9 @@ int backsql_api_odbc2dn( Operation *op, SlapReply *rs, struct berval *dn );
 /*
  * entry-id.c
  */
+#ifdef BACKSQL_ARBITRARY_KEY
+extern struct berval   backsql_baseObject_bv;
+#endif /* BACKSQL_ARBITRARY_KEY */
 
 /* stores in *id the ID in table ldap_entries corresponding to DN, if any */
 int backsql_dn2id( backsql_info *bi, backsql_entryID *id,
index f87da1db5cc9c45da698ecef9a2f76dbb69c74d5..8e4f59479547870e52908519ddb75db5ee3d971b 100644 (file)
@@ -184,6 +184,8 @@ backsql_init_search(
        bsi->bsi_filter_oc = NULL;
 
        if ( get_base_id ) {
+               assert( op->o_bd->be_private );
+
                rc = backsql_dn2id( (backsql_info *)op->o_bd->be_private,
                                &bsi->bsi_base_id, dbh, base );
        }
@@ -1393,8 +1395,9 @@ backsql_oc_get_candidates( void *v_oc, void *v_bsi )
        backsql_BindRowAsStrings( sth, &row );
        rc = SQLFetch( sth );
        for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
-               struct berval           dn;
+               struct berval           dn, ndn;
                backsql_entryID         *c_id = NULL;
+               int                     ret;
 
                ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
 
@@ -1402,6 +1405,20 @@ backsql_oc_get_candidates( void *v_oc, void *v_bsi )
                        continue;
                }
 
+               ret = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
+               if ( dn.bv_val != row.cols[ 3 ] ) {
+                       free( dn.bv_val );
+               }
+
+               if ( ret != LDAP_SUCCESS ) {
+                       continue;
+               }
+
+               if ( bi->sql_baseObject && bvmatch( &ndn, &bi->sql_baseObject->e_nname ) ) {
+                       free( ndn.bv_val );
+                       continue;
+               }
+
                c_id = (backsql_entryID *)ch_calloc( 1, 
                                sizeof( backsql_entryID ) );
 #ifdef BACKSQL_ARBITRARY_KEY
@@ -1413,10 +1430,10 @@ backsql_oc_get_candidates( void *v_oc, void *v_bsi )
 #endif /* ! BACKSQL_ARBITRARY_KEY */
                c_id->eid_oc_id = bsi->bsi_oc->bom_id;
 
-               if ( dn.bv_val == row.cols[ 3 ] ) {
-                       ber_dupbv( &c_id->eid_dn, &dn );
+               if ( ndn.bv_val == row.cols[ 3 ] ) {
+                       ber_dupbv( &c_id->eid_dn, &ndn );
                } else {
-                       c_id->eid_dn = dn;
+                       c_id->eid_dn = ndn;
                }
 
                /* append at end of list ... */
@@ -1459,7 +1476,7 @@ backsql_search( Operation *op, SlapReply *rs )
        Entry                   user_entry = { 0 };
        int                     manageDSAit;
        time_t                  stoptime = 0;
-       backsql_srch_info       srch_info;
+       backsql_srch_info       bsi;
        backsql_entryID         *eid = NULL;
        struct berval           base;
 
@@ -1504,7 +1521,7 @@ backsql_search( Operation *op, SlapReply *rs )
        /* compute it anyway; root does not use it */
        stoptime = op->o_time + op->ors_tlimit;
 
-       base = op->o_req_dn;
+       base = op->o_req_ndn;
        if ( backsql_api_dn2odbc( op, rs, &base ) ) {
                Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
                        "backsql_api_dn2odbc failed\n", 
@@ -1516,7 +1533,7 @@ backsql_search( Operation *op, SlapReply *rs )
        }
 
        /* init search */
-       rs->sr_err = backsql_init_search( &srch_info, &base,
+       rs->sr_err = backsql_init_search( &bsi, &base,
                        op->ors_scope,
                        op->ors_slimit, op->ors_tlimit,
                        stoptime, op->ors_filter,
@@ -1526,20 +1543,47 @@ backsql_search( Operation *op, SlapReply *rs )
                goto done;
        }
 
-       /*
-        * 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
-        */
-       srch_info.bsi_n_candidates =
-               ( op->ors_limit == NULL /* isroot == FALSE */ ? -2 : 
+       bsi.bsi_n_candidates =
+               ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 : 
                ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
                ( op->ors_limit->lms_s_unchecked ) ) );
-       avl_apply( bi->sql_oc_by_oc, backsql_oc_get_candidates,
-                       &srch_info, BACKSQL_AVL_STOP, AVL_INORDER );
-       if ( op->ors_limit != NULL      /* isroot == TRUE */
+
+       switch ( bsi.bsi_scope ) {
+       case LDAP_SCOPE_BASE:
+       case BACKSQL_SCOPE_BASE_LIKE:
+               /*
+                * probably already found...
+                */
+               bsi.bsi_id_list = &bsi.bsi_base_id;
+               bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
+               break;
+
+       case LDAP_SCOPE_SUBTREE:
+               /*
+                * if baseObject is defined, and if it is the root 
+                * of the search, add it to the candidate list
+                */
+               if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
+               {
+                       bsi.bsi_id_list = &bsi.bsi_base_id;
+                       bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
+               }
+
+               /* FALLTHRU */
+       default:
+
+               /*
+                * 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
+                */
+               avl_apply( bi->sql_oc_by_oc, backsql_oc_get_candidates,
+                               &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
+       }
+
+       if ( op->ors_limit != NULL      /* isroot == FALSE */
                        && op->ors_limit->lms_s_unchecked != -1
-                       && srch_info.bsi_n_candidates == -1 )
+                       && bsi.bsi_n_candidates == -1 )
        {
                rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
                send_ldap_result( op, rs );
@@ -1549,15 +1593,16 @@ backsql_search( Operation *op, SlapReply *rs )
        /*
         * now we load candidate entries (only those attributes 
         * mentioned in attrs and filter), test it against full filter 
-        * and then send to client
+        * and then send to client; don't free entry_id if baseObject...
         */
-       for ( eid = srch_info.bsi_id_list;
+       for ( eid = bsi.bsi_id_list;
                        eid != NULL; 
-                       eid = backsql_free_entryID( eid, 1 ) )
+                       eid = backsql_free_entryID( eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
        {
                int             rc;
                Attribute       *hasSubordinate = NULL,
                                *a = NULL;
+               Entry           *e = NULL;
 
                /* check for abandon */
                if ( op->o_abandon ) {
@@ -1588,20 +1633,28 @@ backsql_search( Operation *op, SlapReply *rs )
                        eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
 #endif /* ! BACKSQL_ARBITRARY_KEY */
 
-               srch_info.bsi_e = &user_entry;
-               rc = backsql_id2entry( &srch_info, eid );
-               if ( rc != LDAP_SUCCESS ) {
-                       Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
-                               "error %d in backsql_id2entry() "
-                               "- skipping\n", rc, 0, 0 );
-                       continue;
+               /* don't recollect baseObject ... */
+               if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
+                       e = bi->sql_baseObject;
+
+               } else {
+                       bsi.bsi_e = &user_entry;
+                       rc = backsql_id2entry( &bsi, eid );
+                       if ( rc != LDAP_SUCCESS ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+                                       "error %d in backsql_id2entry() "
+                                       "- skipping\n", rc, 0, 0 );
+                               continue;
+                       }
+
+                       e = &user_entry;
                }
 
                /* check scope */
                switch ( op->ors_scope ) {
                case LDAP_SCOPE_BASE:
                case BACKSQL_SCOPE_BASE_LIKE:
-                       if ( !bvmatch( &user_entry.e_nname, &op->o_req_ndn ) ) {
+                       if ( !bvmatch( &e->e_nname, &op->o_req_ndn ) ) {
                                goto next_entry;
                        }
                        break;
@@ -1617,7 +1670,7 @@ backsql_search( Operation *op, SlapReply *rs )
                }
 
                case LDAP_SCOPE_SUBTREE:
-                       if ( !dnIsSuffix( &user_entry.e_nname, &op->o_req_ndn ) ) {
+                       if ( !dnIsSuffix( &e->e_nname, &op->o_req_ndn ) ) {
                                goto next_entry;
                        }
                        break;
@@ -1626,23 +1679,23 @@ backsql_search( Operation *op, SlapReply *rs )
                if ( !manageDSAit &&
                                op->ors_scope != LDAP_SCOPE_BASE &&
                                op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
-                               is_entry_referral( &user_entry ) )
+                               is_entry_referral( e ) )
                {
                        BerVarray refs;
 
-                       refs = get_entry_referrals( op, &user_entry );
+                       refs = get_entry_referrals( op, e );
                        if ( !refs ) {
-                               backsql_srch_info       srch_info2 = { 0 };
+                               backsql_srch_info       bsi2 = { 0 };
                                Entry                   user_entry2 = { 0 };
 
                                /* retry with the full entry... */
-                               (void)backsql_init_search( &srch_info2,
-                                               &user_entry.e_name,
+                               (void)backsql_init_search( &bsi2,
+                                               &e->e_nname,
                                                LDAP_SCOPE_BASE, 
                                                -1, -1, -1, NULL,
                                                dbh, op, rs, NULL, 0 );
-                               srch_info2.bsi_e = &user_entry2;
-                               rc = backsql_id2entry( &srch_info2, eid );
+                               bsi2.bsi_e = &user_entry2;
+                               rc = backsql_id2entry( &bsi2, eid );
                                if ( rc == LDAP_SUCCESS ) {
                                        if ( is_entry_referral( &user_entry2 ) )
                                        {
@@ -1655,7 +1708,7 @@ backsql_search( Operation *op, SlapReply *rs )
 
                        if ( refs ) {
                                rs->sr_ref = referral_rewrite( refs,
-                                               &user_entry.e_name,
+                                               &e->e_name,
                                                &op->o_req_dn,
                                                op->ors_scope );
                                ber_bvarray_free( refs );
@@ -1681,8 +1734,8 @@ backsql_search( Operation *op, SlapReply *rs )
                 * anyway; we should have used the frontend API function
                 * filter_has_subordinates()
                 */
-               if ( srch_info.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
-                       rc = backsql_has_children( bi, dbh, &user_entry.e_nname );
+               if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
+                       rc = backsql_has_children( bi, dbh, &e->e_nname );
 
                        switch ( rc ) {
                        case LDAP_COMPARE_TRUE:
@@ -1708,9 +1761,9 @@ backsql_search( Operation *op, SlapReply *rs )
                        }
                }
 
-               if ( test_filter( op, &user_entry, op->ors_filter )
+               if ( test_filter( op, e, op->ors_filter )
                                == LDAP_COMPARE_TRUE ) {
-                       if ( hasSubordinate && !( srch_info.bsi_flags & BSQL_SF_ALL_OPER ) 
+                       if ( hasSubordinate && !( bsi.bsi_flags & BSQL_SF_ALL_OPER ) 
                                        && !ad_inlist( slap_schema.si_ad_hasSubordinates, op->ors_attrs ) ) {
                                a->a_next = NULL;
                                attr_free( hasSubordinate );
@@ -1719,8 +1772,10 @@ backsql_search( Operation *op, SlapReply *rs )
 
                        rs->sr_attrs = op->ors_attrs;
                        rs->sr_operational_attrs = NULL;
-                       rs->sr_entry = &user_entry;
-                       rs->sr_flags = REP_ENTRY_MODIFIABLE;
+                       rs->sr_entry = e;
+                       if ( e == &user_entry ) {
+                               rs->sr_flags = REP_ENTRY_MODIFIABLE;
+                       }
                        sres = send_search_entry( op, rs );
                        rs->sr_entry = NULL;
                        rs->sr_attrs = NULL;
@@ -1764,7 +1819,7 @@ end_of_search:;
                        : LDAP_REFERRAL;
 
        } else {
-               rs->sr_err = srch_info.bsi_status;
+               rs->sr_err = bsi.bsi_status;
        }
        send_ldap_result( op, rs );
 
@@ -1774,12 +1829,12 @@ end_of_search:;
        }
 
 done:;
-       if ( !BER_BVISNULL( &srch_info.bsi_base_id.eid_dn ) ) {
-               (void)backsql_free_entryID( &srch_info.bsi_base_id, 0 );
+       if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_dn ) ) {
+               (void)backsql_free_entryID( &bsi.bsi_base_id, 0 );
        }
 
-       if ( srch_info.bsi_attrs ) {
-               ch_free( srch_info.bsi_attrs );
+       if ( bsi.bsi_attrs ) {
+               ch_free( bsi.bsi_attrs );
        }
 
        if ( base.bv_val != op->o_req_ndn.bv_val ) {