openldap/servers/slapd/back-sql/search.c

2423 lines
60 KiB
C
Raw Normal View History

2003-12-08 03:19:18 +08:00
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
2005-01-02 04:49:32 +08:00
* Copyright 1999-2005 The OpenLDAP Foundation.
2003-12-08 03:19:18 +08:00
* Portions Copyright 1999 Dmitry Kovalev.
* Portions Copyright 2002 Pierangelo Masarati.
2005-01-02 04:43:42 +08:00
* Portions Copyright 2004 Mark Adamson.
2003-12-08 03:19:18 +08:00
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*/
/* ACKNOWLEDGEMENTS:
* This work was initially developed by Dmitry Kovalev for inclusion
* by OpenLDAP Software. Additional significant contributors include
* Pierangelo Masarati and Mark Adamson.
*/
#include "portable.h"
#include <stdio.h>
#include <sys/types.h>
#include "ac/string.h"
#include "ac/ctype.h"
#include "slap.h"
#include "proto-sql.h"
static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
static int backsql_process_filter_eq( backsql_srch_info *bsi,
backsql_at_map_rec *at,
int casefold, struct berval *filter_value );
static int backsql_process_filter_like( backsql_srch_info *bsi,
backsql_at_map_rec *at,
int casefold, struct berval *filter_value );
static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f,
backsql_at_map_rec *at );
static int
backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
{
int n_attrs = 0;
AttributeName *an = NULL;
if ( bsi->bsi_attrs == NULL ) {
return 1;
}
/*
* clear the list (retrieve all attrs)
*/
if ( ad == NULL ) {
bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx );
bsi->bsi_attrs = NULL;
bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
return 1;
}
for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
an = &bsi->bsi_attrs[ n_attrs ];
Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
"attribute \"%s\" is in list\n",
an->an_name.bv_val, 0, 0 );
/*
* We can live with strcmp because the attribute
* list has been normalized before calling be_search
*/
if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
return 1;
}
}
Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
"adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
sizeof( AttributeName ) * ( n_attrs + 2 ),
bsi->bsi_op->o_tmpmemctx );
if ( an == NULL ) {
return -1;
}
an[ n_attrs ].an_name = ad->ad_cname;
an[ n_attrs ].an_desc = ad;
BER_BVZERO( &an[ n_attrs + 1 ].an_name );
bsi->bsi_attrs = an;
return 1;
}
/*
* Initializes the search structure.
*
* If get_base_id != 0, the field bsi_base_id is filled
* with the entryID of bsi_base_ndn; it must be freed
* by backsql_free_entryID() when no longer required.
*
* NOTE: base must be normalized
*/
int
backsql_init_search(
backsql_srch_info *bsi,
struct berval *nbase,
int scope,
int slimit,
int tlimit,
time_t stoptime,
Filter *filter,
SQLHDBC dbh,
Operation *op,
SlapReply *rs,
AttributeName *attrs,
unsigned flags )
{
backsql_info *bi = (backsql_info *)op->o_bd->be_private;
int rc = LDAP_SUCCESS;
bsi->bsi_base_ndn = nbase;
bsi->bsi_use_subtree_shortcut = 0;
BER_BVZERO( &bsi->bsi_base_id.eid_dn );
BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
bsi->bsi_scope = scope;
bsi->bsi_slimit = slimit;
bsi->bsi_tlimit = tlimit;
bsi->bsi_filter = filter;
bsi->bsi_dbh = dbh;
bsi->bsi_op = op;
bsi->bsi_rs = rs;
bsi->bsi_flags = BSQL_SF_NONE;
bsi->bsi_attrs = NULL;
if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) {
/*
* if requested, simply try to fetch all attributes
*/
bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
} else {
if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) {
bsi->bsi_flags |= BSQL_SF_ALL_USER;
} else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) {
bsi->bsi_flags |= BSQL_SF_ALL_OPER;
}
if ( attrs == NULL ) {
/* NULL means all user attributes */
bsi->bsi_flags |= BSQL_SF_ALL_USER;
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
} else {
AttributeName *p;
int got_oc = 0;
bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc(
sizeof( AttributeName ),
bsi->bsi_op->o_tmpmemctx );
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 ) {
/* handle "*" */
bsi->bsi_flags |= BSQL_SF_ALL_USER;
/* if all attrs are requested, there's
* no need to continue */
if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
bsi->bsi_op->o_tmpmemctx );
bsi->bsi_attrs = NULL;
break;
}
continue;
} else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
/* handle "+" */
bsi->bsi_flags |= BSQL_SF_ALL_OPER;
/* if all attrs are requested, there's
* no need to continue */
if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
bsi->bsi_op->o_tmpmemctx );
bsi->bsi_attrs = NULL;
break;
}
continue;
} else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
/* ignore "1.1" */
continue;
} else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
got_oc = 1;
}
backsql_attrlist_add( bsi, p->an_desc );
}
if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) {
/* add objectClass if not present,
* because it is required to understand
* if an entry is a referral, an alias
* or so... */
backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
}
}
if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
AttributeName *p;
/* use hints if available */
for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
/* handle "*" */
bsi->bsi_flags |= BSQL_SF_ALL_USER;
/* if all attrs are requested, there's
* no need to continue */
if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
bsi->bsi_op->o_tmpmemctx );
bsi->bsi_attrs = NULL;
break;
}
continue;
} else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
/* handle "+" */
bsi->bsi_flags |= BSQL_SF_ALL_OPER;
/* if all attrs are requested, there's
* no need to continue */
if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
bsi->bsi_op->o_tmpmemctx );
bsi->bsi_attrs = NULL;
break;
}
continue;
}
backsql_attrlist_add( bsi, p->an_desc );
}
}
}
bsi->bsi_id_list = NULL;
bsi->bsi_id_listtail = &bsi->bsi_id_list;
bsi->bsi_n_candidates = 0;
bsi->bsi_stoptime = stoptime;
BER_BVZERO( &bsi->bsi_sel.bb_val );
bsi->bsi_sel.bb_len = 0;
BER_BVZERO( &bsi->bsi_from.bb_val );
bsi->bsi_from.bb_len = 0;
BER_BVZERO( &bsi->bsi_join_where.bb_val );
bsi->bsi_join_where.bb_len = 0;
BER_BVZERO( &bsi->bsi_flt_where.bb_val );
bsi->bsi_flt_where.bb_len = 0;
bsi->bsi_filter_oc = NULL;
if ( BACKSQL_IS_GET_ID( flags ) ) {
2005-01-16 02:43:34 +08:00
int matched = BACKSQL_IS_MATCHED( flags );
int getentry = BACKSQL_IS_GET_ENTRY( flags );
int gotit = 0;
2005-01-16 02:43:34 +08:00
assert( op->o_bd->be_private );
rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
2005-01-16 02:43:34 +08:00
matched, 1 );
/* the entry is collected either if requested for by getentry
* or if get noSuchObject and requested to climb the tree,
* so that a matchedDN or a referral can be returned */
2005-01-16 02:43:34 +08:00
if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) {
if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) {
assert( bsi->bsi_e != NULL );
if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) )
{
gotit = 1;
}
2005-01-16 02:43:34 +08:00
/*
* let's see if it is a referral and, in case, get it
*/
backsql_attrlist_add( bsi, slap_schema.si_ad_ref );
rc = backsql_id2entry( bsi, &bsi->bsi_base_id );
if ( rc == LDAP_SUCCESS ) {
if ( is_entry_referral( bsi->bsi_e ) )
{
BerVarray erefs = get_entry_referrals( op, bsi->bsi_e );
if ( erefs ) {
rc = rs->sr_err = LDAP_REFERRAL;
rs->sr_ref = referral_rewrite( erefs,
&bsi->bsi_e->e_nname,
&op->o_req_dn,
scope );
ber_bvarray_free( erefs );
} else {
rc = rs->sr_err = LDAP_OTHER;
rs->sr_text = "bad referral object";
}
2005-01-16 02:43:34 +08:00
} else if ( !gotit ) {
rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
}
2005-01-16 02:43:34 +08:00
}
} else {
rs->sr_ref = referral_rewrite( default_referral,
NULL, &op->o_req_dn, scope );
rc = rs->sr_err = LDAP_REFERRAL;
}
}
}
bsi->bsi_status = rc;
switch ( rc ) {
case LDAP_SUCCESS:
case LDAP_REFERRAL:
break;
default:
bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
bsi->bsi_op->o_tmpmemctx );
break;
}
return rc;
}
static int
backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
{
int res;
if ( !f ) {
return 0;
}
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
backsql_strfcat( &bsi->bsi_flt_where, "c", '(' /* ) */ );
while ( 1 ) {
res = backsql_process_filter( bsi, f );
if ( res < 0 ) {
/*
* TimesTen : If the query has no answers,
* don't bother to run the query.
*/
return -1;
}
A big bunch of improvements, contributed by Sam Drake and Raj Damani. Summary of changes is cited below. The patch still needs some cosmetic changes to be made, but is ready for testing. -----Original Message----- From: Sam Drake [mailto:drake@timesten.com] Sent: Saturday, April 07, 2001 10:40 PM To: 'mitya@seismic.ru' Cc: openldap-devel@OpenLDAP.org Subject: RE: Slapd frontend performance issues FYI, here is a short description of the changes I made. I'll package up the changes asap, but it may take a couple of days. The performance numbers quoted in this report were seen at my location with a 100,000 object database ... the slower numbers I mentioned earlier were reported by a customer with a 1,000,000 object database. I also can't explain the very poor performance I saw with OpenLDAP and LDBM with a 100,000 object database. ...Sam Drake / TimesTen Performance Software ---------- Work Performed OpenLDAP 2.0.9, including back-sql, was built successfully on Solaris 8 using gcc. The LDAP server itself, slapd, passed all tests bundled with OpenLDAP. OpenLDAP was built using Sleepycat LDBM release 3.1.17 as the "native" storage manager. The experimental back-sql facility in slapd was also built successfully. It was built using Oracle release 8.1.7 and the Oracle ODBC driver and ODBC Driver Manager from Merant. Rudimentary testing was performed with the data and examples provided with back-sql, and back-sql was found to be functional. Slapd and back-sql were then tested with TimesTen, using TimesTen 4.1.1. Back-sql was not immediately functional with TimesTen due to a number of SQL limitations in the TimesTen product. Functional issues encountered were: 1. Back-sql issued SELECT statements including the construct, "UPPER(?)". While TimesTen supports UPPER, it does not support the use of parameters as input to builtin functions. Back-sql was modified to convert the parameter to upper case prior to giving it to the underlying database ... a change that is appropriate for all databases. 2. Back-sql issued SELECT statements using the SQL CONCAT function. TimesTen does not support this function. Back-sql was modified to concatentate the necessary strings itself (in "C" code) prior to passing the parameters to SQL. This change is also appropriate for all databases, not just TimesTen. Once these two issues were resolved, back-sql could successfully process LDAP searches using the sample data and examples provided with back-sql. While performance was not measured at this point, numerous serious performance problems were observed with the back-sql code and the generated SQL. In particular: 1. In the process of implementing an LDAP search, back-sql will generate and execute a SQL query for all object classes stored in back-sql. During the source of generating each SQL query, it is common for back-sql to determine that a particular object class can not possibly have any members satisfying the search. For example, this can occur if the query searches an attribute of the LDAP object that does not exist in the SQL schema. In this case, back-sql would generate and issue the SQL query anyway, including a clause such as "WHERE 1=0" in the generated SELECT. The overhead of parsing, optimizing and executing the query is non-trivial, and the answer (the empty set) is known in advance. Solution: Back-sql was modified to stop executing a SQL query when it can be predetermined that the query will return no rows. 2. Searches in LDAP are fundamentally case-insensitive ("abc" is equal to "aBc"). However, in SQL this is not normally the case. Back-sql thus generated SQL SELECT statements including clauses of the form, "WHERE UPPER(attribute) = 'JOE'". Even if an index is defined on the attribute in the relational database, the index can not be used to satisfy the query, as the index is case sensitive. The relational database then is forced to scan all rows in the table in order to satisfy the query ... an expensive and non-scalable proposition. Solution: Back-sql was modified to allow the schema designer to add additional "upper cased" columns to the SQL schema. These columns, if present, contain an upper cased version of the "standard" field, and will be used preferentially for searching. Such columns can be provided for all searchable columns, some columns, or no columns. An application using database "triggers" or similar mechanisms can automatically maintain these upper cased columns when the standard column is changed. 3. In order to implement the hierarchical nature of LDAP object hierarchies, OpenLDAP uses suffix searches in SQL. For example, to find all objects in the subtree "o=TimesTen,c=us", a SQL SELECT statement of the form, "WHERE UPPER(dn) LIKE '%O=TIMESTEN,C=US'" would be employed. Aside from the UPPER issue discussed above, a second performance problem in this query is the use of suffix search. In TimesTen (and most relational databases), indexes can be used to optimize exact-match searches and prefix searches. However, suffix searches must be performed by scanning every row in the table ... an expensive and non-scalable proposition. Solution: Back-sql was modified to optionally add a new "dn_ru" column to the ldap_entries table. This additional column, if present, contains a byte-reversed and upper cased version of the DN. This allows back-sql to generate indexable prefix searches. This column is also easily maintained automatically through the use of triggers. Results A simple database schema was generated holding the LDAP objects and attributes specified by our customer. An application was written to generate test databases. Both TimesTen and Oracle 8.1.7 were populated with 100,000 entry databases. Load Times Using "slapadd" followed by "slapindex", loading and indexing 100,000 entries in an LDBM database ran for 19 minutes 10 seconds. Using a C++ application that used ODBC, loading 100,000 entries into a disk based RDBMS took 17 minutes 53 seconds. Using a C++ application that used ODBC, loading 100,000 entries into TimesTen took 1 minute 40 seconds. Search Times The command, "timex timesearch.sh '(cn=fname210100*)'" was used to test search times. This command issues the same LDAP search 4000 times over a single LDAP connection. Both the client and server (slapd) were run on the same machine. With TimesTen as the database, 4000 queries took 14.93 seconds, for a rate of 267.9 per second. With a disk based RDBMS as the database, 4000 queries took 77.79 seconds, for a rate of 51.42 per second. With LDBM as the database, 1 query takes 76 seconds, or 0.076 per second. Something is clearly broken.
2001-08-03 01:28:59 +08:00
f = f->f_next;
if ( f == NULL ) {
break;
}
switch ( op ) {
case LDAP_FILTER_AND:
backsql_strfcat( &bsi->bsi_flt_where, "l",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " AND " ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
" AND " );
break;
case LDAP_FILTER_OR:
backsql_strfcat( &bsi->bsi_flt_where, "l",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " OR " ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
" OR " );
break;
}
}
backsql_strfcat( &bsi->bsi_flt_where, "c", /* ( */ ')' );
return 1;
}
static int
backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
backsql_at_map_rec *at )
{
backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
int i;
int casefold = 0;
if ( !f ) {
return 0;
}
/* always uppercase strings by now */
#ifdef BACKSQL_UPPERCASE_FILTER
if ( f->f_sub_desc->ad_type->sat_substr &&
SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
bi->sql_caseIgnoreMatch ) )
#endif /* BACKSQL_UPPERCASE_FILTER */
{
casefold = 1;
}
if ( f->f_sub_desc->ad_type->sat_substr &&
SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
bi->sql_telephoneNumberMatch ) )
{
struct berval bv;
ber_len_t i, s, a;
/*
* to check for matching telephone numbers
* with intermixed chars, e.g. val='1234'
* use
*
* val LIKE '%1%2%3%4%'
*/
BER_BVZERO( &bv );
if ( f->f_sub_initial.bv_val ) {
bv.bv_len += f->f_sub_initial.bv_len;
}
if ( f->f_sub_any != NULL ) {
for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
bv.bv_len += f->f_sub_any[ a ].bv_len;
}
}
if ( f->f_sub_final.bv_val ) {
bv.bv_len += f->f_sub_final.bv_len;
}
bv.bv_len = 2 * bv.bv_len - 1;
bv.bv_val = ch_malloc( bv.bv_len + 1 );
s = 0;
if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
bv.bv_val[ s + 2 * i - 1 ] = '%';
bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
}
bv.bv_val[ s + 2 * i - 1 ] = '%';
s += 2 * i;
}
if ( f->f_sub_any != NULL ) {
for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
bv.bv_val[ s + 2 * i - 1 ] = '%';
bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
}
bv.bv_val[ s + 2 * i - 1 ] = '%';
s += 2 * i;
}
}
if ( !BER_BVISNULL( &f->f_sub_final ) ) {
bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
bv.bv_val[ s + 2 * i - 1 ] = '%';
bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
}
bv.bv_val[ s + 2 * i - 1 ] = '%';
s += 2 * i;
}
bv.bv_val[ s - 1 ] = '\0';
(void)backsql_process_filter_like( bsi, at, casefold, &bv );
ch_free( bv.bv_val );
return 1;
}
/*
* When dealing with case-sensitive strings
* we may omit normalization; however, normalized
* SQL filters are more liberal.
*/
backsql_strfcat( &bsi->bsi_flt_where, "c", '(' /* ) */ );
/* TimesTen */
Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
at->bam_ad->ad_cname.bv_val, 0, 0 );
Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
at->bam_sel_expr_u.bv_val ? "' '" : "",
at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
/*
* If a pre-upper-cased version of the column
* or a precompiled upper function exists, use it
*/
backsql_strfcat( &bsi->bsi_flt_where,
"bl",
&at->bam_sel_expr_u,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " LIKE '" ),
" LIKE '" );
} else {
backsql_strfcat( &bsi->bsi_flt_where, "bl",
&at->bam_sel_expr,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
}
if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
ber_len_t start;
#ifdef BACKSQL_TRACE
Debug( LDAP_DEBUG_TRACE,
"==>backsql_process_sub_filter(%s): "
"sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
f->f_sub_initial.bv_val, 0 );
#endif /* BACKSQL_TRACE */
start = bsi->bsi_flt_where.bb_val.bv_len;
backsql_strfcat( &bsi->bsi_flt_where, "b",
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
&f->f_sub_initial );
if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
}
}
backsql_strfcat( &bsi->bsi_flt_where, "c", '%' );
if ( f->f_sub_any != NULL ) {
for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
ber_len_t start;
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
#ifdef BACKSQL_TRACE
Debug( LDAP_DEBUG_TRACE,
"==>backsql_process_sub_filter(%s): "
"sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
i, f->f_sub_any[ i ].bv_val );
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
#endif /* BACKSQL_TRACE */
start = bsi->bsi_flt_where.bb_val.bv_len;
backsql_strfcat( &bsi->bsi_flt_where,
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
"bc",
&f->f_sub_any[ i ],
'%' );
if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
/*
* Note: toupper('%') = '%'
*/
ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
}
}
}
if ( !BER_BVISNULL( &f->f_sub_final ) ) {
ber_len_t start;
#ifdef BACKSQL_TRACE
Debug( LDAP_DEBUG_TRACE,
"==>backsql_process_sub_filter(%s): "
"sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
f->f_sub_final.bv_val, 0 );
#endif /* BACKSQL_TRACE */
start = bsi->bsi_flt_where.bb_val.bv_len;
backsql_strfcat( &bsi->bsi_flt_where, "b",
&f->f_sub_final );
if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
}
}
backsql_strfcat( &bsi->bsi_flt_where, "l",
(ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
return 1;
}
static int
backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
{
if ( BER_BVISNULL( from_tbls ) ) {
return LDAP_SUCCESS;
}
if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
char *start, *end, *tmp;
tmp = ch_strdup( from_tbls->bv_val );
for ( start = tmp, end = strchr( start, ',' ); start; ) {
if ( end ) {
end[0] = '\0';
}
if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
{
backsql_strfcat( &bsi->bsi_from, "cs", ',', start );
}
if ( end ) {
/* in case there are spaces after the comma... */
for ( start = &end[1]; isspace( start[0] ); start++ );
if ( start[0] ) {
end = strchr( start, ',' );
} else {
start = NULL;
}
} else {
start = NULL;
}
}
ch_free( tmp );
} else {
backsql_strfcat( &bsi->bsi_from, "b", from_tbls );
}
return LDAP_SUCCESS;
}
static int
backsql_process_filter( backsql_srch_info *bsi, Filter *f )
{
backsql_at_map_rec **vat = NULL;
AttributeDescription *ad = NULL;
unsigned i;
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
int done = 0;
int rc = 0;
Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
"invalid filter\n", 0, 0, 0 );
rc = -1;
goto done;
}
switch( f->f_choice ) {
case LDAP_FILTER_OR:
rc = backsql_process_filter_list( bsi, f->f_or,
LDAP_FILTER_OR );
done = 1;
break;
case LDAP_FILTER_AND:
rc = backsql_process_filter_list( bsi, f->f_and,
LDAP_FILTER_AND );
done = 1;
break;
case LDAP_FILTER_NOT:
backsql_strfcat( &bsi->bsi_flt_where, "l",
(ber_len_t)STRLENOF( "NOT (" /* ) */ ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
"NOT (" /* ) */ );
rc = backsql_process_filter( bsi, f->f_not );
backsql_strfcat( &bsi->bsi_flt_where, "c", /* ( */ ')' );
done = 1;
break;
A big bunch of improvements, contributed by Sam Drake and Raj Damani. Summary of changes is cited below. The patch still needs some cosmetic changes to be made, but is ready for testing. -----Original Message----- From: Sam Drake [mailto:drake@timesten.com] Sent: Saturday, April 07, 2001 10:40 PM To: 'mitya@seismic.ru' Cc: openldap-devel@OpenLDAP.org Subject: RE: Slapd frontend performance issues FYI, here is a short description of the changes I made. I'll package up the changes asap, but it may take a couple of days. The performance numbers quoted in this report were seen at my location with a 100,000 object database ... the slower numbers I mentioned earlier were reported by a customer with a 1,000,000 object database. I also can't explain the very poor performance I saw with OpenLDAP and LDBM with a 100,000 object database. ...Sam Drake / TimesTen Performance Software ---------- Work Performed OpenLDAP 2.0.9, including back-sql, was built successfully on Solaris 8 using gcc. The LDAP server itself, slapd, passed all tests bundled with OpenLDAP. OpenLDAP was built using Sleepycat LDBM release 3.1.17 as the "native" storage manager. The experimental back-sql facility in slapd was also built successfully. It was built using Oracle release 8.1.7 and the Oracle ODBC driver and ODBC Driver Manager from Merant. Rudimentary testing was performed with the data and examples provided with back-sql, and back-sql was found to be functional. Slapd and back-sql were then tested with TimesTen, using TimesTen 4.1.1. Back-sql was not immediately functional with TimesTen due to a number of SQL limitations in the TimesTen product. Functional issues encountered were: 1. Back-sql issued SELECT statements including the construct, "UPPER(?)". While TimesTen supports UPPER, it does not support the use of parameters as input to builtin functions. Back-sql was modified to convert the parameter to upper case prior to giving it to the underlying database ... a change that is appropriate for all databases. 2. Back-sql issued SELECT statements using the SQL CONCAT function. TimesTen does not support this function. Back-sql was modified to concatentate the necessary strings itself (in "C" code) prior to passing the parameters to SQL. This change is also appropriate for all databases, not just TimesTen. Once these two issues were resolved, back-sql could successfully process LDAP searches using the sample data and examples provided with back-sql. While performance was not measured at this point, numerous serious performance problems were observed with the back-sql code and the generated SQL. In particular: 1. In the process of implementing an LDAP search, back-sql will generate and execute a SQL query for all object classes stored in back-sql. During the source of generating each SQL query, it is common for back-sql to determine that a particular object class can not possibly have any members satisfying the search. For example, this can occur if the query searches an attribute of the LDAP object that does not exist in the SQL schema. In this case, back-sql would generate and issue the SQL query anyway, including a clause such as "WHERE 1=0" in the generated SELECT. The overhead of parsing, optimizing and executing the query is non-trivial, and the answer (the empty set) is known in advance. Solution: Back-sql was modified to stop executing a SQL query when it can be predetermined that the query will return no rows. 2. Searches in LDAP are fundamentally case-insensitive ("abc" is equal to "aBc"). However, in SQL this is not normally the case. Back-sql thus generated SQL SELECT statements including clauses of the form, "WHERE UPPER(attribute) = 'JOE'". Even if an index is defined on the attribute in the relational database, the index can not be used to satisfy the query, as the index is case sensitive. The relational database then is forced to scan all rows in the table in order to satisfy the query ... an expensive and non-scalable proposition. Solution: Back-sql was modified to allow the schema designer to add additional "upper cased" columns to the SQL schema. These columns, if present, contain an upper cased version of the "standard" field, and will be used preferentially for searching. Such columns can be provided for all searchable columns, some columns, or no columns. An application using database "triggers" or similar mechanisms can automatically maintain these upper cased columns when the standard column is changed. 3. In order to implement the hierarchical nature of LDAP object hierarchies, OpenLDAP uses suffix searches in SQL. For example, to find all objects in the subtree "o=TimesTen,c=us", a SQL SELECT statement of the form, "WHERE UPPER(dn) LIKE '%O=TIMESTEN,C=US'" would be employed. Aside from the UPPER issue discussed above, a second performance problem in this query is the use of suffix search. In TimesTen (and most relational databases), indexes can be used to optimize exact-match searches and prefix searches. However, suffix searches must be performed by scanning every row in the table ... an expensive and non-scalable proposition. Solution: Back-sql was modified to optionally add a new "dn_ru" column to the ldap_entries table. This additional column, if present, contains a byte-reversed and upper cased version of the DN. This allows back-sql to generate indexable prefix searches. This column is also easily maintained automatically through the use of triggers. Results A simple database schema was generated holding the LDAP objects and attributes specified by our customer. An application was written to generate test databases. Both TimesTen and Oracle 8.1.7 were populated with 100,000 entry databases. Load Times Using "slapadd" followed by "slapindex", loading and indexing 100,000 entries in an LDBM database ran for 19 minutes 10 seconds. Using a C++ application that used ODBC, loading 100,000 entries into a disk based RDBMS took 17 minutes 53 seconds. Using a C++ application that used ODBC, loading 100,000 entries into TimesTen took 1 minute 40 seconds. Search Times The command, "timex timesearch.sh '(cn=fname210100*)'" was used to test search times. This command issues the same LDAP search 4000 times over a single LDAP connection. Both the client and server (slapd) were run on the same machine. With TimesTen as the database, 4000 queries took 14.93 seconds, for a rate of 267.9 per second. With a disk based RDBMS as the database, 4000 queries took 77.79 seconds, for a rate of 51.42 per second. With LDBM as the database, 1 query takes 76 seconds, or 0.076 per second. Something is clearly broken.
2001-08-03 01:28:59 +08:00
case LDAP_FILTER_PRESENT:
ad = f->f_desc;
break;
case LDAP_FILTER_EXT:
ad = f->f_mra->ma_desc;
if ( f->f_mr_dnattrs ) {
/*
* if dn attrs filtering is requested, better return
* success and let test_filter() deal with candidate
* selection; otherwise we'd need to set conditions
* on the contents of the DN, e.g. "SELECT ... FROM
* ldap_entries AS attributeName WHERE attributeName.dn
* like '%attributeName=value%'"
*/
backsql_strfcat( &bsi->bsi_flt_where, "l",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "1=1" ), "1=1" );
bsi->bsi_status = LDAP_SUCCESS;
rc = 1;
goto done;
}
break;
default:
ad = f->f_av_desc;
break;
}
if ( rc == -1 ) {
goto done;
}
if ( done ) {
rc = 1;
goto done;
}
/*
* Turn structuralObjectClass into objectClass
*/
if ( ad == slap_schema.si_ad_objectClass
|| ad == slap_schema.si_ad_structuralObjectClass )
{
/*
* If the filter is LDAP_FILTER_PRESENT, then it's done;
* otherwise, let's see if we are lucky: filtering
* for "structural" objectclass or ancestor...
*/
switch ( f->f_choice ) {
case LDAP_FILTER_EQUALITY:
{
ObjectClass *oc = oc_bvfind( &f->f_av_value );
if ( oc == NULL ) {
Debug( LDAP_DEBUG_TRACE,
"backsql_process_filter(): "
"unknown objectClass \"%s\" "
"in filter\n",
f->f_av_value.bv_val, 0, 0 );
bsi->bsi_status = LDAP_OTHER;
rc = -1;
goto done;
}
/*
* "structural" objectClass inheritance:
* - a search for "person" will also return
* "inetOrgPerson"
* - a search for "top" will return everything
*/
if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
backsql_strfcat( &bsi->bsi_flt_where, "lbl",
(ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
"(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
&bsi->bsi_oc->bom_oc->soc_cname,
(ber_len_t)STRLENOF( /* ((' */ "'))" ),
/* ((' */ "'))" );
bsi->bsi_status = LDAP_SUCCESS;
rc = 1;
goto done;
}
break;
}
case LDAP_FILTER_PRESENT:
backsql_strfcat( &bsi->bsi_flt_where, "l",
(ber_len_t)STRLENOF( "3=3" ), "3=3" );
bsi->bsi_status = LDAP_SUCCESS;
rc = 1;
goto done;
/* FIXME: LDAP_FILTER_EXT? */
default:
Debug( LDAP_DEBUG_TRACE,
"backsql_process_filter(): "
"illegal/unhandled filter "
"on objectClass attribute",
0, 0, 0 );
bsi->bsi_status = LDAP_OTHER;
rc = -1;
goto done;
}
} else if ( ad == slap_schema.si_ad_entryUUID ) {
unsigned long oc_id;
#ifdef BACKSQL_ARBITRARY_KEY
struct berval keyval;
#else /* ! BACKSQL_ARBITRARY_KEY */
unsigned long keyval;
char keyvalbuf[] = "18446744073709551615";
#endif /* ! BACKSQL_ARBITRARY_KEY */
switch ( f->f_choice ) {
case LDAP_FILTER_EQUALITY:
backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
if ( oc_id != bsi->bsi_oc->bom_id ) {
bsi->bsi_status = LDAP_SUCCESS;
rc = -1;
goto done;
}
#ifdef BACKSQL_ARBITRARY_KEY
backsql_strfcat( &bsi->bsi_flt_where, "bcblbc",
&bsi->bsi_oc->bom_keytbl, '.',
&bsi->bsi_oc->bom_keycol,
STRLENOF( " LIKE '" ), " LIKE '",
&keyval, '\'' );
#else /* ! BACKSQL_ARBITRARY_KEY */
snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
backsql_strfcat( &bsi->bsi_flt_where, "bcbcs",
&bsi->bsi_oc->bom_keytbl, '.',
&bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
#endif /* ! BACKSQL_ARBITRARY_KEY */
break;
case LDAP_FILTER_PRESENT:
backsql_strfcat( &bsi->bsi_flt_where, "l",
(ber_len_t)STRLENOF( "4=4" ), "4=4" );
break;
default:
rc = -1;
goto done;
}
bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
rc = 1;
goto done;
#ifdef BACKSQL_SYNCPROV
2004-11-16 06:10:10 +08:00
} else if ( ad == slap_schema.si_ad_entryCSN ) {
/*
* support for syncrepl as producer...
2004-11-16 06:10:10 +08:00
*/
if ( !bsi->bsi_op->o_sync ) {
2004-11-16 06:10:10 +08:00
/* unsupported at present... */
bsi->bsi_status = LDAP_OTHER;
rc = -1;
goto done;
}
2004-11-29 17:36:22 +08:00
bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
/* if doing a syncrepl, try to return as much as possible,
* and always match the filter */
backsql_strfcat( &bsi->bsi_flt_where, "l",
(ber_len_t)STRLENOF( "5=5" ), "5=5" );
/* save for later use in operational attributes */
/* FIXME: saves only the first occurrence, because
* the filter during updates is written as
* "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
* so we want our fake entryCSN to match the greatest
* value
*/
if ( bsi->bsi_op->o_private == NULL ) {
bsi->bsi_op->o_private = &f->f_av_value;
}
bsi->bsi_status = LDAP_SUCCESS;
rc = 1;
goto done;
#endif /* BACKSQL_SYNCPROV */
2004-11-16 06:10:10 +08:00
} else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
/*
* FIXME: this is not robust; e.g. a filter
* '(!(hasSubordinates=TRUE))' fails because
* in SQL it would read 'NOT (1=1)' instead
* of no condition.
* Note however that hasSubordinates is boolean,
* so a more appropriate filter would be
* '(hasSubordinates=FALSE)'
2003-04-17 04:54:56 +08:00
*
* A more robust search for hasSubordinates
* would * require joining the ldap_entries table
* selecting if there are descendants of the
* candidate.
*/
backsql_strfcat( &bsi->bsi_flt_where, "l",
(ber_len_t)STRLENOF( "6=6" ), "6=6" );
if ( ad == slap_schema.si_ad_hasSubordinates ) {
/*
2003-04-17 04:54:56 +08:00
* instruct candidate selection algorithm
* and attribute list to try to detect
* if an entry has subordinates
*/
bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
} else {
/*
* clear attributes to fetch, to require ALL
* and try extended match on all attributes
*/
backsql_attrlist_add( bsi, NULL );
}
rc = 1;
goto done;
}
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
/*
* attribute inheritance:
*/
if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
bsi->bsi_status = LDAP_OTHER;
rc = -1;
goto done;
}
if ( vat == NULL ) {
/* search anyway; other parts of the filter
* may succeeed */
backsql_strfcat( &bsi->bsi_flt_where, "l",
(ber_len_t)STRLENOF( "7=7" ), "7=7" );
bsi->bsi_status = LDAP_SUCCESS;
rc = 1;
goto done;
}
/* if required, open extra level of parens */
done = 0;
if ( vat[0]->bam_next || vat[1] ) {
backsql_strfcat( &bsi->bsi_flt_where, "c", '(' );
done = 1;
}
i = 0;
next:;
/* apply attr */
if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
return -1;
}
/* if more definitions of the same attr, apply */
if ( vat[i]->bam_next ) {
backsql_strfcat( &bsi->bsi_flt_where, "l",
2004-04-20 08:26:42 +08:00
STRLENOF( " OR " ), " OR " );
vat[i] = vat[i]->bam_next;
goto next;
}
/* if more descendants of the same attr, apply */
i++;
if ( vat[i] ) {
backsql_strfcat( &bsi->bsi_flt_where, "l",
2004-04-20 08:26:42 +08:00
STRLENOF( " OR " ), " OR " );
goto next;
}
/* if needed, close extra level of parens */
if ( done ) {
backsql_strfcat( &bsi->bsi_flt_where, "c", ')' );
}
rc = 1;
done:;
if ( vat ) {
ch_free( vat );
}
2004-01-18 02:26:36 +08:00
Debug( LDAP_DEBUG_TRACE,
"<==backsql_process_filter() %s\n",
rc == 1 ? "succeeded" : "failed", 0, 0);
return rc;
}
static int
backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
int casefold, struct berval *filter_value )
{
/*
* maybe we should check type of at->sel_expr here somehow,
* to know whether upper_func is applicable, but for now
* upper_func stuff is made for Oracle, where UPPER is
* safely applicable to NUMBER etc.
*/
if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
ber_len_t start;
backsql_strfcat( &bsi->bsi_flt_where, "cbl",
'(', /* ) */
&at->bam_sel_expr_u,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "='" ),
"='" );
start = bsi->bsi_flt_where.bb_val.bv_len;
backsql_strfcat( &bsi->bsi_flt_where, "bl",
filter_value,
(ber_len_t)STRLENOF( /* (' */ "')" ),
/* (' */ "')" );
ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
} else {
backsql_strfcat( &bsi->bsi_flt_where, "cblbl",
'(', /* ) */
&at->bam_sel_expr,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "='" ), "='",
filter_value,
(ber_len_t)STRLENOF( /* (' */ "')" ),
/* (' */ "')" );
}
return 1;
}
static int
backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
int casefold, struct berval *filter_value )
{
/*
* maybe we should check type of at->sel_expr here somehow,
* to know whether upper_func is applicable, but for now
* upper_func stuff is made for Oracle, where UPPER is
* safely applicable to NUMBER etc.
*/
if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
ber_len_t start;
backsql_strfcat( &bsi->bsi_flt_where, "cbl",
'(', /* ) */
&at->bam_sel_expr_u,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " LIKE '%" ),
" LIKE '%" );
start = bsi->bsi_flt_where.bb_val.bv_len;
backsql_strfcat( &bsi->bsi_flt_where, "bl",
filter_value,
(ber_len_t)STRLENOF( /* (' */ "%')" ),
/* (' */ "%')" );
ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
} else {
backsql_strfcat( &bsi->bsi_flt_where, "cblbl",
'(', /* ) */
&at->bam_sel_expr,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " LIKE '%" ),
" LIKE '%",
filter_value,
(ber_len_t)STRLENOF( /* (' */ "%')" ),
/* (' */ "%')" );
}
return 1;
}
static int
backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
{
backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
int casefold = 0;
struct berval *filter_value = NULL;
MatchingRule *matching_rule = NULL;
struct berval ordering = BER_BVC("<=");
Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
at->bam_ad->ad_cname.bv_val, 0, 0 );
/*
* need to add this attribute to list of attrs to load,
* so that we can do test_filter() later
*/
backsql_attrlist_add( bsi, at->bam_ad );
backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
if ( !BER_BVISNULL( &at->bam_join_where )
&& strstr( bsi->bsi_join_where.bb_val.bv_val,
at->bam_join_where.bv_val ) == NULL )
{
backsql_strfcat( &bsi->bsi_join_where, "lb",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " AND " ), " AND ",
&at->bam_join_where );
}
switch ( f->f_choice ) {
case LDAP_FILTER_EQUALITY:
filter_value = &f->f_av_value;
matching_rule = at->bam_ad->ad_type->sat_equality;
goto equality_match;
/* fail over into next case */
case LDAP_FILTER_EXT:
filter_value = &f->f_mra->ma_value;
matching_rule = f->f_mr_rule;
equality_match:;
/* always uppercase strings by now */
#ifdef BACKSQL_UPPERCASE_FILTER
if ( SLAP_MR_ASSOCIATED( matching_rule,
2004-10-02 20:05:42 +08:00
bi->sql_caseIgnoreMatch ) )
#endif /* BACKSQL_UPPERCASE_FILTER */
{
casefold = 1;
}
/* FIXME: directoryString filtering should use a similar
* approach to deal with non-prettified values like
* " A non prettified value ", by using a LIKE
* filter with all whitespaces collapsed to a single '%' */
if ( SLAP_MR_ASSOCIATED( matching_rule,
2004-10-02 20:05:42 +08:00
bi->sql_telephoneNumberMatch ) )
{
struct berval bv;
ber_len_t i;
/*
* to check for matching telephone numbers
* with intermized chars, e.g. val='1234'
* use
*
* val LIKE '%1%2%3%4%'
*/
bv.bv_len = 2 * filter_value->bv_len - 1;
bv.bv_val = ch_malloc( bv.bv_len + 1 );
bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
for ( i = 1; i < filter_value->bv_len; i++ ) {
bv.bv_val[ 2 * i - 1 ] = '%';
bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
}
bv.bv_val[ 2 * i - 1 ] = '\0';
(void)backsql_process_filter_like( bsi, at, casefold, &bv );
ch_free( bv.bv_val );
break;
}
/* NOTE: this is required by objectClass inheritance
* and auxiliary objectClass use in filters for slightly
* more efficient candidate selection. */
/* FIXME: a bit too many specializations to deal with
* very specific cases... */
if ( at->bam_ad == slap_schema.si_ad_objectClass
|| at->bam_ad == slap_schema.si_ad_structuralObjectClass )
{
backsql_strfcat( &bsi->bsi_flt_where, "lbl",
(ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
"(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
filter_value,
(ber_len_t)STRLENOF( /* (' */ "')" ),
/* (' */ "')" );
break;
}
/*
* maybe we should check type of at->sel_expr here somehow,
* to know whether upper_func is applicable, but for now
* upper_func stuff is made for Oracle, where UPPER is
* safely applicable to NUMBER etc.
*/
(void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
break;
case LDAP_FILTER_GE:
ordering.bv_val = ">=";
/* fall thru to next case */
case LDAP_FILTER_LE:
filter_value = &f->f_av_value;
/* always uppercase strings by now */
#ifdef BACKSQL_UPPERCASE_FILTER
if ( at->bam_ad->ad_type->sat_ordering &&
SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
bi->sql_caseIgnoreMatch ) )
#endif /* BACKSQL_UPPERCASE_FILTER */
{
casefold = 1;
}
/*
* FIXME: should we uppercase the operands?
*/
if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
ber_len_t start;
backsql_strfcat( &bsi->bsi_flt_where, "cbbc",
'(', /* ) */
&at->bam_sel_expr_u,
&ordering,
'\'' );
start = bsi->bsi_flt_where.bb_val.bv_len;
backsql_strfcat( &bsi->bsi_flt_where, "bl",
filter_value,
(ber_len_t)STRLENOF( /* (' */ "')" ),
/* (' */ "')" );
ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
} else {
backsql_strfcat( &bsi->bsi_flt_where, "cbbcbl",
'(' /* ) */ ,
&at->bam_sel_expr,
&ordering,
'\'',
&f->f_av_value,
(ber_len_t)STRLENOF( /* (' */ "')" ),
/* ( */ "')" );
}
break;
case LDAP_FILTER_PRESENT:
backsql_strfcat( &bsi->bsi_flt_where, "lbl",
(ber_len_t)STRLENOF( "NOT (" /* ) */),
"NOT (", /* ) */
&at->bam_sel_expr,
(ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
/* ( */ " IS NULL)" );
break;
case LDAP_FILTER_SUBSTRINGS:
backsql_process_sub_filter( bsi, f, at );
break;
case LDAP_FILTER_APPROX:
/* we do our best */
/*
* maybe we should check type of at->sel_expr here somehow,
* to know whether upper_func is applicable, but for now
* upper_func stuff is made for Oracle, where UPPER is
* safely applicable to NUMBER etc.
*/
(void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
break;
default:
/* unhandled filter type; should not happen */
assert( 0 );
backsql_strfcat( &bsi->bsi_flt_where, "l",
(ber_len_t)STRLENOF( "8=8" ), "8=8" );
break;
}
Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
at->bam_ad->ad_cname.bv_val, 0, 0 );
A big bunch of improvements, contributed by Sam Drake and Raj Damani. Summary of changes is cited below. The patch still needs some cosmetic changes to be made, but is ready for testing. -----Original Message----- From: Sam Drake [mailto:drake@timesten.com] Sent: Saturday, April 07, 2001 10:40 PM To: 'mitya@seismic.ru' Cc: openldap-devel@OpenLDAP.org Subject: RE: Slapd frontend performance issues FYI, here is a short description of the changes I made. I'll package up the changes asap, but it may take a couple of days. The performance numbers quoted in this report were seen at my location with a 100,000 object database ... the slower numbers I mentioned earlier were reported by a customer with a 1,000,000 object database. I also can't explain the very poor performance I saw with OpenLDAP and LDBM with a 100,000 object database. ...Sam Drake / TimesTen Performance Software ---------- Work Performed OpenLDAP 2.0.9, including back-sql, was built successfully on Solaris 8 using gcc. The LDAP server itself, slapd, passed all tests bundled with OpenLDAP. OpenLDAP was built using Sleepycat LDBM release 3.1.17 as the "native" storage manager. The experimental back-sql facility in slapd was also built successfully. It was built using Oracle release 8.1.7 and the Oracle ODBC driver and ODBC Driver Manager from Merant. Rudimentary testing was performed with the data and examples provided with back-sql, and back-sql was found to be functional. Slapd and back-sql were then tested with TimesTen, using TimesTen 4.1.1. Back-sql was not immediately functional with TimesTen due to a number of SQL limitations in the TimesTen product. Functional issues encountered were: 1. Back-sql issued SELECT statements including the construct, "UPPER(?)". While TimesTen supports UPPER, it does not support the use of parameters as input to builtin functions. Back-sql was modified to convert the parameter to upper case prior to giving it to the underlying database ... a change that is appropriate for all databases. 2. Back-sql issued SELECT statements using the SQL CONCAT function. TimesTen does not support this function. Back-sql was modified to concatentate the necessary strings itself (in "C" code) prior to passing the parameters to SQL. This change is also appropriate for all databases, not just TimesTen. Once these two issues were resolved, back-sql could successfully process LDAP searches using the sample data and examples provided with back-sql. While performance was not measured at this point, numerous serious performance problems were observed with the back-sql code and the generated SQL. In particular: 1. In the process of implementing an LDAP search, back-sql will generate and execute a SQL query for all object classes stored in back-sql. During the source of generating each SQL query, it is common for back-sql to determine that a particular object class can not possibly have any members satisfying the search. For example, this can occur if the query searches an attribute of the LDAP object that does not exist in the SQL schema. In this case, back-sql would generate and issue the SQL query anyway, including a clause such as "WHERE 1=0" in the generated SELECT. The overhead of parsing, optimizing and executing the query is non-trivial, and the answer (the empty set) is known in advance. Solution: Back-sql was modified to stop executing a SQL query when it can be predetermined that the query will return no rows. 2. Searches in LDAP are fundamentally case-insensitive ("abc" is equal to "aBc"). However, in SQL this is not normally the case. Back-sql thus generated SQL SELECT statements including clauses of the form, "WHERE UPPER(attribute) = 'JOE'". Even if an index is defined on the attribute in the relational database, the index can not be used to satisfy the query, as the index is case sensitive. The relational database then is forced to scan all rows in the table in order to satisfy the query ... an expensive and non-scalable proposition. Solution: Back-sql was modified to allow the schema designer to add additional "upper cased" columns to the SQL schema. These columns, if present, contain an upper cased version of the "standard" field, and will be used preferentially for searching. Such columns can be provided for all searchable columns, some columns, or no columns. An application using database "triggers" or similar mechanisms can automatically maintain these upper cased columns when the standard column is changed. 3. In order to implement the hierarchical nature of LDAP object hierarchies, OpenLDAP uses suffix searches in SQL. For example, to find all objects in the subtree "o=TimesTen,c=us", a SQL SELECT statement of the form, "WHERE UPPER(dn) LIKE '%O=TIMESTEN,C=US'" would be employed. Aside from the UPPER issue discussed above, a second performance problem in this query is the use of suffix search. In TimesTen (and most relational databases), indexes can be used to optimize exact-match searches and prefix searches. However, suffix searches must be performed by scanning every row in the table ... an expensive and non-scalable proposition. Solution: Back-sql was modified to optionally add a new "dn_ru" column to the ldap_entries table. This additional column, if present, contains a byte-reversed and upper cased version of the DN. This allows back-sql to generate indexable prefix searches. This column is also easily maintained automatically through the use of triggers. Results A simple database schema was generated holding the LDAP objects and attributes specified by our customer. An application was written to generate test databases. Both TimesTen and Oracle 8.1.7 were populated with 100,000 entry databases. Load Times Using "slapadd" followed by "slapindex", loading and indexing 100,000 entries in an LDBM database ran for 19 minutes 10 seconds. Using a C++ application that used ODBC, loading 100,000 entries into a disk based RDBMS took 17 minutes 53 seconds. Using a C++ application that used ODBC, loading 100,000 entries into TimesTen took 1 minute 40 seconds. Search Times The command, "timex timesearch.sh '(cn=fname210100*)'" was used to test search times. This command issues the same LDAP search 4000 times over a single LDAP connection. Both the client and server (slapd) were run on the same machine. With TimesTen as the database, 4000 queries took 14.93 seconds, for a rate of 267.9 per second. With a disk based RDBMS as the database, 4000 queries took 77.79 seconds, for a rate of 51.42 per second. With LDBM as the database, 1 query takes 76 seconds, or 0.076 per second. Something is clearly broken.
2001-08-03 01:28:59 +08:00
return 1;
}
static int
backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
{
backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
int rc;
assert( query );
BER_BVZERO( query );
bsi->bsi_use_subtree_shortcut = 0;
Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
BER_BVZERO( &bsi->bsi_sel.bb_val );
BER_BVZERO( &bsi->bsi_sel.bb_val );
bsi->bsi_sel.bb_len = 0;
BER_BVZERO( &bsi->bsi_from.bb_val );
bsi->bsi_from.bb_len = 0;
BER_BVZERO( &bsi->bsi_join_where.bb_val );
bsi->bsi_join_where.bb_len = 0;
BER_BVZERO( &bsi->bsi_flt_where.bb_val );
bsi->bsi_flt_where.bb_len = 0;
backsql_strfcat( &bsi->bsi_sel, "lbcbc",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
"SELECT DISTINCT ldap_entries.id,",
&bsi->bsi_oc->bom_keytbl,
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
'.',
&bsi->bsi_oc->bom_keycol,
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
',' );
2004-10-02 20:05:42 +08:00
if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
backsql_strfcat( &bsi->bsi_sel, "blbl",
2004-10-02 20:05:42 +08:00
&bi->sql_strcast_func,
(ber_len_t)STRLENOF( "('" /* ') */ ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
"('" /* ') */ ,
&bsi->bsi_oc->bom_oc->soc_cname,
(ber_len_t)STRLENOF( /* (' */ "')" ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
/* (' */ "')" );
} else {
backsql_strfcat( &bsi->bsi_sel, "cbc",
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
'\'',
&bsi->bsi_oc->bom_oc->soc_cname,
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
'\'' );
}
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
backsql_strfcat( &bsi->bsi_sel, "b", &bi->sql_dn_oc_aliasing );
backsql_strfcat( &bsi->bsi_from, "lb",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " FROM ldap_entries," ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
" FROM ldap_entries,",
&bsi->bsi_oc->bom_keytbl );
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
backsql_strfcat( &bsi->bsi_join_where, "lbcbl",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
&bsi->bsi_oc->bom_keytbl,
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
'.',
&bsi->bsi_oc->bom_keycol,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
"=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
switch ( bsi->bsi_scope ) {
case LDAP_SCOPE_BASE:
if ( BACKSQL_CANUPPERCASE( bi ) ) {
backsql_strfcat( &bsi->bsi_join_where, "bl",
2004-10-02 20:05:42 +08:00
&bi->sql_upper_func,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
"(ldap_entries.dn)=?" );
} else {
backsql_strfcat( &bsi->bsi_join_where, "l",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
"ldap_entries.dn=?" );
}
break;
case BACKSQL_SCOPE_BASE_LIKE:
if ( BACKSQL_CANUPPERCASE( bi ) ) {
backsql_strfcat( &bsi->bsi_join_where, "bl",
2004-10-02 20:05:42 +08:00
&bi->sql_upper_func,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
"(ldap_entries.dn) LIKE ?" );
} else {
backsql_strfcat( &bsi->bsi_join_where, "l",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
"ldap_entries.dn LIKE ?" );
}
break;
case LDAP_SCOPE_ONELEVEL:
backsql_strfcat( &bsi->bsi_join_where, "l",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
"ldap_entries.parent=?" );
break;
#ifdef LDAP_SCOPE_SUBORDINATE
case LDAP_SCOPE_SUBORDINATE:
#endif /* LDAP_SCOPE_SUBORDINATE */
case LDAP_SCOPE_SUBTREE:
if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
int i;
BackendDB *bd = bsi->bsi_op->o_bd;
assert( bd->be_nsuffix );
for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
{
if ( dn_match( &bd->be_nsuffix[ i ],
bsi->bsi_base_ndn ) )
{
/* pass this to the candidate selection
* routine so that the DN is not bound
* to the select statement */
bsi->bsi_use_subtree_shortcut = 1;
break;
}
}
}
if ( bsi->bsi_use_subtree_shortcut ) {
/* Skip the base DN filter, as every entry will match it */
backsql_strfcat( &bsi->bsi_join_where, "l",
(ber_len_t)STRLENOF( "9=9"), "9=9");
} else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
backsql_strfcat( &bsi->bsi_join_where, "b", &bi->sql_subtree_cond );
} else if ( BACKSQL_CANUPPERCASE( bi ) ) {
backsql_strfcat( &bsi->bsi_join_where, "bl",
2004-10-02 20:05:42 +08:00
&bi->sql_upper_func,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
"(ldap_entries.dn) LIKE ?" );
} else {
backsql_strfcat( &bsi->bsi_join_where, "l",
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
"ldap_entries.dn LIKE ?" );
}
break;
default:
assert( 0 );
}
rc = backsql_process_filter( bsi, bsi->bsi_filter );
if ( rc > 0 ) {
struct berbuf bb = BB_NULL;
backsql_strfcat( &bb, "bbblb",
&bsi->bsi_sel.bb_val,
&bsi->bsi_from.bb_val,
&bsi->bsi_join_where.bb_val,
2004-04-20 08:26:42 +08:00
(ber_len_t)STRLENOF( " AND " ), " AND ",
&bsi->bsi_flt_where.bb_val );
*query = bb.bb_val;
} else if ( rc < 0 ) {
/*
* Indicates that there's no possible way the filter matches
* anything. No need to issue the query
*/
free( query->bv_val );
BER_BVZERO( query );
}
free( bsi->bsi_sel.bb_val.bv_val );
BER_BVZERO( &bsi->bsi_sel.bb_val );
bsi->bsi_sel.bb_len = 0;
free( bsi->bsi_from.bb_val.bv_val );
BER_BVZERO( &bsi->bsi_from.bb_val );
bsi->bsi_from.bb_len = 0;
free( bsi->bsi_join_where.bb_val.bv_val );
BER_BVZERO( &bsi->bsi_join_where.bb_val );
bsi->bsi_join_where.bb_len = 0;
free( bsi->bsi_flt_where.bb_val.bv_val );
BER_BVZERO( &bsi->bsi_flt_where.bb_val );
bsi->bsi_flt_where.bb_len = 0;
2003-04-17 06:22:57 +08:00
Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
query->bv_val ? query->bv_val : "NULL", 0, 0 );
2003-04-17 06:22:57 +08:00
return ( rc <= 0 ? 1 : 0 );
}
2002-12-15 06:25:52 +08:00
static int
backsql_oc_get_candidates( void *v_oc, void *v_bsi )
{
backsql_oc_map_rec *oc = v_oc;
backsql_srch_info *bsi = v_bsi;
2005-01-16 02:43:34 +08:00
Operation *op = bsi->bsi_op;
backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
struct berval query;
SQLHSTMT sth = SQL_NULL_HSTMT;
RETCODE rc;
int res;
BACKSQL_ROW_NTS row;
int i;
int j;
int n_candidates = bsi->bsi_n_candidates;
/*
* + 1 because we need room for '%';
* + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
* this makes a subtree
* search for a DN BACKSQL_MAX_DN_LEN long legal
* if it returns that DN only
*/
char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
bsi->bsi_status = LDAP_SUCCESS;
Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
BACKSQL_OC_NAME( oc ), 0, 0 );
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
2005-02-19 20:25:55 +08:00
/* check for abandon */
if ( op->o_abandon ) {
bsi->bsi_status = SLAPD_ABANDON;
return BACKSQL_AVL_STOP;
}
if ( bsi->bsi_n_candidates == -1 ) {
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"unchecked limit has been overcome\n", 0, 0, 0 );
/* should never get here */
assert( 0 );
bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
return BACKSQL_AVL_STOP;
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
}
bsi->bsi_oc = oc;
2003-04-17 06:22:57 +08:00
res = backsql_srch_query( bsi, &query );
if ( res ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"error while constructing query for objectclass \"%s\"\n",
oc->bom_oc->soc_cname.bv_val, 0, 0 );
2003-04-17 06:22:57 +08:00
/*
* FIXME: need to separate errors from legally
* impossible filters
*/
switch ( bsi->bsi_status ) {
case LDAP_SUCCESS:
case LDAP_UNDEFINED_TYPE:
case LDAP_NO_SUCH_OBJECT:
/* we are conservative... */
default:
bsi->bsi_status = LDAP_SUCCESS;
/* try next */
return BACKSQL_AVL_CONTINUE;
case LDAP_ADMINLIMIT_EXCEEDED:
case LDAP_OTHER:
/* don't try any more */
return BACKSQL_AVL_STOP;
}
2003-04-17 06:22:57 +08:00
}
if ( BER_BVISNULL( &query ) ) {
2003-04-17 06:22:57 +08:00
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"could not construct query for objectclass \"%s\"\n",
oc->bom_oc->soc_cname.bv_val, 0, 0 );
bsi->bsi_status = LDAP_SUCCESS;
return BACKSQL_AVL_CONTINUE;
}
Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
query.bv_val, 0, 0 );
rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
free( query.bv_val );
BER_BVZERO( &query );
if ( rc != SQL_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"error preparing query\n", 0, 0, 0 );
2004-10-02 20:05:42 +08:00
backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
bsi->bsi_status = LDAP_OTHER;
return BACKSQL_AVL_CONTINUE;
}
Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
&bsi->bsi_oc->bom_id );
if ( rc != SQL_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"error binding objectclass id parameter\n", 0, 0, 0 );
bsi->bsi_status = LDAP_OTHER;
return BACKSQL_AVL_CONTINUE;
}
switch ( bsi->bsi_scope ) {
case LDAP_SCOPE_BASE:
case BACKSQL_SCOPE_BASE_LIKE:
/*
* We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
* however this should be handled earlier
*/
if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
bsi->bsi_status = LDAP_OTHER;
return BACKSQL_AVL_CONTINUE;
}
AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
bsi->bsi_base_ndn->bv_len + 1 );
/* uppercase DN only if the stored DN can be uppercased
* for comparison */
if ( BACKSQL_CANUPPERCASE( bi ) ) {
ldap_pvt_str2upper( tmp_base_ndn );
}
Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
tmp_base_ndn, 0, 0 );
rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
tmp_base_ndn, BACKSQL_MAX_DN_LEN );
if ( rc != SQL_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"error binding base_ndn parameter\n", 0, 0, 0 );
2004-10-02 20:05:42 +08:00
backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
sth, rc );
bsi->bsi_status = LDAP_OTHER;
return BACKSQL_AVL_CONTINUE;
}
break;
A big bunch of improvements, contributed by Sam Drake and Raj Damani. Summary of changes is cited below. The patch still needs some cosmetic changes to be made, but is ready for testing. -----Original Message----- From: Sam Drake [mailto:drake@timesten.com] Sent: Saturday, April 07, 2001 10:40 PM To: 'mitya@seismic.ru' Cc: openldap-devel@OpenLDAP.org Subject: RE: Slapd frontend performance issues FYI, here is a short description of the changes I made. I'll package up the changes asap, but it may take a couple of days. The performance numbers quoted in this report were seen at my location with a 100,000 object database ... the slower numbers I mentioned earlier were reported by a customer with a 1,000,000 object database. I also can't explain the very poor performance I saw with OpenLDAP and LDBM with a 100,000 object database. ...Sam Drake / TimesTen Performance Software ---------- Work Performed OpenLDAP 2.0.9, including back-sql, was built successfully on Solaris 8 using gcc. The LDAP server itself, slapd, passed all tests bundled with OpenLDAP. OpenLDAP was built using Sleepycat LDBM release 3.1.17 as the "native" storage manager. The experimental back-sql facility in slapd was also built successfully. It was built using Oracle release 8.1.7 and the Oracle ODBC driver and ODBC Driver Manager from Merant. Rudimentary testing was performed with the data and examples provided with back-sql, and back-sql was found to be functional. Slapd and back-sql were then tested with TimesTen, using TimesTen 4.1.1. Back-sql was not immediately functional with TimesTen due to a number of SQL limitations in the TimesTen product. Functional issues encountered were: 1. Back-sql issued SELECT statements including the construct, "UPPER(?)". While TimesTen supports UPPER, it does not support the use of parameters as input to builtin functions. Back-sql was modified to convert the parameter to upper case prior to giving it to the underlying database ... a change that is appropriate for all databases. 2. Back-sql issued SELECT statements using the SQL CONCAT function. TimesTen does not support this function. Back-sql was modified to concatentate the necessary strings itself (in "C" code) prior to passing the parameters to SQL. This change is also appropriate for all databases, not just TimesTen. Once these two issues were resolved, back-sql could successfully process LDAP searches using the sample data and examples provided with back-sql. While performance was not measured at this point, numerous serious performance problems were observed with the back-sql code and the generated SQL. In particular: 1. In the process of implementing an LDAP search, back-sql will generate and execute a SQL query for all object classes stored in back-sql. During the source of generating each SQL query, it is common for back-sql to determine that a particular object class can not possibly have any members satisfying the search. For example, this can occur if the query searches an attribute of the LDAP object that does not exist in the SQL schema. In this case, back-sql would generate and issue the SQL query anyway, including a clause such as "WHERE 1=0" in the generated SELECT. The overhead of parsing, optimizing and executing the query is non-trivial, and the answer (the empty set) is known in advance. Solution: Back-sql was modified to stop executing a SQL query when it can be predetermined that the query will return no rows. 2. Searches in LDAP are fundamentally case-insensitive ("abc" is equal to "aBc"). However, in SQL this is not normally the case. Back-sql thus generated SQL SELECT statements including clauses of the form, "WHERE UPPER(attribute) = 'JOE'". Even if an index is defined on the attribute in the relational database, the index can not be used to satisfy the query, as the index is case sensitive. The relational database then is forced to scan all rows in the table in order to satisfy the query ... an expensive and non-scalable proposition. Solution: Back-sql was modified to allow the schema designer to add additional "upper cased" columns to the SQL schema. These columns, if present, contain an upper cased version of the "standard" field, and will be used preferentially for searching. Such columns can be provided for all searchable columns, some columns, or no columns. An application using database "triggers" or similar mechanisms can automatically maintain these upper cased columns when the standard column is changed. 3. In order to implement the hierarchical nature of LDAP object hierarchies, OpenLDAP uses suffix searches in SQL. For example, to find all objects in the subtree "o=TimesTen,c=us", a SQL SELECT statement of the form, "WHERE UPPER(dn) LIKE '%O=TIMESTEN,C=US'" would be employed. Aside from the UPPER issue discussed above, a second performance problem in this query is the use of suffix search. In TimesTen (and most relational databases), indexes can be used to optimize exact-match searches and prefix searches. However, suffix searches must be performed by scanning every row in the table ... an expensive and non-scalable proposition. Solution: Back-sql was modified to optionally add a new "dn_ru" column to the ldap_entries table. This additional column, if present, contains a byte-reversed and upper cased version of the DN. This allows back-sql to generate indexable prefix searches. This column is also easily maintained automatically through the use of triggers. Results A simple database schema was generated holding the LDAP objects and attributes specified by our customer. An application was written to generate test databases. Both TimesTen and Oracle 8.1.7 were populated with 100,000 entry databases. Load Times Using "slapadd" followed by "slapindex", loading and indexing 100,000 entries in an LDBM database ran for 19 minutes 10 seconds. Using a C++ application that used ODBC, loading 100,000 entries into a disk based RDBMS took 17 minutes 53 seconds. Using a C++ application that used ODBC, loading 100,000 entries into TimesTen took 1 minute 40 seconds. Search Times The command, "timex timesearch.sh '(cn=fname210100*)'" was used to test search times. This command issues the same LDAP search 4000 times over a single LDAP connection. Both the client and server (slapd) were run on the same machine. With TimesTen as the database, 4000 queries took 14.93 seconds, for a rate of 267.9 per second. With a disk based RDBMS as the database, 4000 queries took 77.79 seconds, for a rate of 51.42 per second. With LDBM as the database, 1 query takes 76 seconds, or 0.076 per second. Something is clearly broken.
2001-08-03 01:28:59 +08:00
#ifdef LDAP_SCOPE_SUBORDINATE
case LDAP_SCOPE_SUBORDINATE:
#endif /* LDAP_SCOPE_SUBORDINATE */
case LDAP_SCOPE_SUBTREE:
{
/* if short-cutting the search base,
* don't bind any parameter */
if ( bsi->bsi_use_subtree_shortcut ) {
break;
}
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
/*
* We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
* however this should be handled earlier
*/
if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
bsi->bsi_status = LDAP_OTHER;
return BACKSQL_AVL_CONTINUE;
}
/*
* Sets the parameters for the SQL built earlier
* NOTE that all the databases could actually use
* the TimesTen version, which would be cleaner
* and would also eliminate the need for the
* subtree_cond line in the configuration file.
* For now, I'm leaving it the way it is,
* so non-TimesTen databases use the original code.
* But at some point this should get cleaned up.
*
* If "dn" is being used, do a suffix search.
* If "dn_ru" is being used, do a prefix search.
*/
if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
tmp_base_ndn[ 0 ] = '\0';
for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
j >= 0; i++, j--) {
tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
}
#ifdef LDAP_SCOPE_SUBORDINATE
if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
tmp_base_ndn[ i++ ] = ',';
}
#endif /* LDAP_SCOPE_SUBORDINATE */
tmp_base_ndn[ i ] = '%';
tmp_base_ndn[ i + 1 ] = '\0';
} else {
i = 0;
tmp_base_ndn[ i++ ] = '%';
#ifdef LDAP_SCOPE_SUBORDINATE
if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
tmp_base_ndn[ i++ ] = ',';
}
#endif /* LDAP_SCOPE_SUBORDINATE */
AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
bsi->bsi_base_ndn->bv_len + 1 );
}
/* uppercase DN only if the stored DN can be uppercased
* for comparison */
if ( BACKSQL_CANUPPERCASE( bi ) ) {
ldap_pvt_str2upper( tmp_base_ndn );
}
#ifdef LDAP_SCOPE_SUBORDINATE
if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
tmp_base_ndn, 0, 0 );
} else
#endif /* LDAP_SCOPE_SUBORDINATE */
{
Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
tmp_base_ndn, 0, 0 );
}
rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
tmp_base_ndn, BACKSQL_MAX_DN_LEN );
if ( rc != SQL_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"error binding base_ndn parameter (2)\n",
0, 0, 0 );
2004-10-02 20:05:42 +08:00
backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
sth, rc );
bsi->bsi_status = LDAP_OTHER;
return BACKSQL_AVL_CONTINUE;
}
break;
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
}
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 */
rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
&bsi->bsi_base_id.eid_id );
if ( rc != SQL_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"error binding base id parameter\n", 0, 0, 0 );
bsi->bsi_status = LDAP_OTHER;
return BACKSQL_AVL_CONTINUE;
}
break;
}
rc = SQLExecute( sth );
if ( !BACKSQL_SUCCESS( rc ) ) {
Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
"error executing query\n", 0, 0, 0 );
2004-10-02 20:05:42 +08:00
backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
SQLFreeStmt( sth, SQL_DROP );
bsi->bsi_status = LDAP_OTHER;
return BACKSQL_AVL_CONTINUE;
}
backsql_BindRowAsStrings( sth, &row );
rc = SQLFetch( sth );
for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
struct berval dn, pdn, ndn;
backsql_entryID *c_id = NULL;
int ret;
ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
continue;
}
2005-01-16 02:43:34 +08:00
ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
if ( dn.bv_val != row.cols[ 3 ] ) {
free( dn.bv_val );
}
if ( ret != LDAP_SUCCESS ) {
continue;
}
if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
2005-01-16 02:43:34 +08:00
op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
continue;
}
c_id = (backsql_entryID *)ch_calloc( 1,
sizeof( backsql_entryID ) );
#ifdef BACKSQL_ARBITRARY_KEY
2005-01-16 02:43:34 +08:00
ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
op->o_tmpmemctx );
ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
op->o_tmpmemctx );
#else /* ! BACKSQL_ARBITRARY_KEY */
c_id->eid_id = strtol( row.cols[ 0 ], NULL, 0 );
c_id->eid_keyval = strtol( row.cols[ 1 ], NULL, 0 );
#endif /* ! BACKSQL_ARBITRARY_KEY */
c_id->eid_oc_id = bsi->bsi_oc->bom_id;
c_id->eid_dn = pdn;
c_id->eid_ndn = ndn;
/* append at end of list ... */
c_id->eid_next = NULL;
*bsi->bsi_id_listtail = c_id;
bsi->bsi_id_listtail = &c_id->eid_next;
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
#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,
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 */
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
/* count candidates, for unchecked limit */
bsi->bsi_n_candidates--;
if ( bsi->bsi_n_candidates == -1 ) {
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
break;
}
}
backsql_FreeRow( &row );
SQLFreeStmt( sth, SQL_DROP );
Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
n_candidates - bsi->bsi_n_candidates, 0, 0 );
return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
}
int
2003-04-03 06:58:02 +08:00
backsql_search( Operation *op, SlapReply *rs )
{
2003-04-03 06:58:02 +08:00
backsql_info *bi = (backsql_info *)op->o_bd->be_private;
SQLHDBC dbh = SQL_NULL_HDBC;
int sres;
2005-01-16 02:43:34 +08:00
Entry user_entry = { 0 },
base_entry = { 0 };
int manageDSAit = get_manageDSAit( op );
time_t stoptime = 0;
backsql_srch_info bsi = { 0 };
backsql_entryID *eid = NULL;
struct berval nbase = BER_BVNULL;
2003-04-03 06:58:02 +08:00
Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
"base=\"%s\", filter=\"%s\", scope=%d,",
2003-04-03 06:58:02 +08:00
op->o_req_ndn.bv_val,
op->ors_filterstr.bv_val ? op->ors_filterstr.bv_val : "(no filter)",
2004-03-09 02:12:45 +08:00
op->ors_scope );
Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
"attributes to load: %s\n",
2004-03-09 02:12:45 +08:00
op->ors_deref,
op->ors_attrsonly,
op->ors_attrs == NULL ? "all" : "custom list" );
2003-04-03 06:58:02 +08:00
if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
"search base length (%ld) exceeds max length (%d)\n",
2003-04-03 06:58:02 +08:00
op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
/*
* FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
* since it is impossible that such a long DN exists
* in the backend
*/
2003-04-03 06:58:02 +08:00
rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
send_ldap_result( op, rs );
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
return 1;
}
2003-04-03 08:35:16 +08:00
sres = backsql_get_db_conn( op, &dbh );
if ( sres != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
"could not get connection handle - exiting\n",
0, 0, 0 );
2003-04-03 06:58:02 +08:00
rs->sr_err = sres;
rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
send_ldap_result( op, rs );
return 1;
}
/* compute it anyway; root does not use it */
2004-03-09 02:12:45 +08:00
stoptime = op->o_time + op->ors_tlimit;
/* init search */
2005-01-16 02:43:34 +08:00
bsi.bsi_e = &base_entry;
rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
2004-03-09 02:12:45 +08:00
op->ors_scope,
op->ors_slimit, op->ors_tlimit,
stoptime, op->ors_filter,
dbh, op, rs, op->ors_attrs,
2005-01-16 02:43:34 +08:00
( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
switch ( rs->sr_err ) {
case LDAP_SUCCESS:
break;
case LDAP_REFERRAL:
2005-01-20 08:17:52 +08:00
if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
{
rs->sr_err = LDAP_SUCCESS;
rs->sr_text = NULL;
rs->sr_matched = NULL;
if ( rs->sr_ref ) {
ber_bvarray_free( rs->sr_ref );
rs->sr_ref = NULL;
}
break;
}
/* fall thru */
default:
2005-01-16 02:43:34 +08:00
#ifdef SLAP_ACL_HONOR_DISCLOSE
if ( !BER_BVISNULL( &base_entry.e_nname )
&& ! access_allowed( op, &base_entry,
slap_schema.si_ad_entry, NULL,
ACL_DISCLOSE, NULL ) )
{
rs->sr_err = LDAP_NO_SUCH_OBJECT;
if ( rs->sr_ref ) {
ber_bvarray_free( rs->sr_ref );
rs->sr_ref = NULL;
2005-01-16 02:43:34 +08:00
}
rs->sr_matched = NULL;
rs->sr_text = NULL;
}
#endif /* SLAP_ACL_HONOR_DISCLOSE */
send_ldap_result( op, rs );
goto done;
2005-01-16 02:43:34 +08:00
}
#ifdef SLAP_ACL_HONOR_DISCLOSE
/* NOTE: __NEW__ "search" access is required
* on searchBase object */
{
2005-01-16 02:43:34 +08:00
slap_mask_t mask;
if ( get_assert( op ) &&
( test_filter( op, &base_entry, get_assertion( op ) )
!= LDAP_COMPARE_TRUE ) )
{
rs->sr_err = LDAP_ASSERTION_FAILED;
}
2005-01-16 02:43:34 +08:00
if ( ! access_allowed_mask( op, &base_entry,
slap_schema.si_ad_entry,
NULL, ACL_SEARCH, NULL, &mask ) )
{
if ( rs->sr_err == LDAP_SUCCESS ) {
rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
}
}
if ( rs->sr_err != LDAP_SUCCESS ) {
2005-01-16 02:43:34 +08:00
if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
rs->sr_err = LDAP_NO_SUCH_OBJECT;
rs->sr_text = NULL;
}
send_ldap_result( op, rs );
goto done;
}
}
2005-01-16 02:43:34 +08:00
#endif /* SLAP_ACL_HONOR_DISCLOSE */
bsi.bsi_e = NULL;
bsi.bsi_n_candidates =
( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
2004-03-09 02:12:45 +08:00
( op->ors_limit->lms_s_unchecked == -1 ? -2 :
( op->ors_limit->lms_s_unchecked ) ) );
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 );
2005-02-19 20:25:55 +08:00
/* check for abandon */
if ( op->o_abandon ) {
rs->sr_err = SLAPD_ABANDON;
goto send_results;
}
}
if ( op->ors_limit != NULL /* isroot == FALSE */
2004-03-09 02:12:45 +08:00
&& op->ors_limit->lms_s_unchecked != -1
&& bsi.bsi_n_candidates == -1 )
2004-03-09 02:12:45 +08:00
{
rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
send_ldap_result( op, rs );
goto done;
}
/*
* now we load candidate entries (only those attributes
* mentioned in attrs and filter), test it against full filter
* and then send to client; don't free entry_id if baseObject...
*/
for ( eid = bsi.bsi_id_list;
eid != NULL;
2005-01-16 02:43:34 +08:00
eid = backsql_free_entryID( op,
eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
{
int rc;
Attribute *a_hasSubordinate = NULL,
*a_entryUUID = NULL,
*a_entryCSN = NULL,
2004-11-29 17:36:22 +08:00
**ap = NULL;
Entry *e = NULL;
/* check for abandon */
if ( op->o_abandon ) {
2005-02-19 20:25:55 +08:00
rs->sr_err = SLAPD_ABANDON;
goto send_results;
}
/* check time limit */
if ( op->ors_tlimit != SLAP_NO_LIMIT
&& slap_get_time() > stoptime )
{
2003-04-03 06:58:02 +08:00
rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
rs->sr_ctrls = NULL;
rs->sr_ref = rs->sr_v2ref;
2005-02-19 19:07:02 +08:00
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 */
/* check scope */
switch ( op->ors_scope ) {
case LDAP_SCOPE_BASE:
case BACKSQL_SCOPE_BASE_LIKE:
2004-11-29 17:36:22 +08:00
if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
goto next_entry2;
}
break;
case LDAP_SCOPE_ONE:
{
2004-11-29 17:36:22 +08:00
struct berval rdn = eid->eid_ndn;
rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
if ( !dnIsOneLevelRDN( &rdn ) ) {
2004-11-29 17:36:22 +08:00
goto next_entry2;
}
/* fall thru */
}
#ifdef LDAP_SCOPE_SUBORDINATE
case LDAP_SCOPE_SUBORDINATE:
/* discard the baseObject entry */
2004-11-29 17:36:22 +08:00
if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
goto next_entry2;
}
/* FALLTHRU */
#endif /* LDAP_SCOPE_SUBORDINATE */
case LDAP_SCOPE_SUBTREE:
/* FIXME: this should never fail... */
2004-11-29 17:36:22 +08:00
if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2005-01-16 02:43:34 +08:00
assert( 0 );
2004-11-29 17:36:22 +08:00
goto next_entry2;
}
break;
}
2004-11-29 17:36:22 +08:00
if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2005-01-16 02:43:34 +08:00
/* don't recollect baseObject... */
2004-11-29 17:36:22 +08:00
e = bi->sql_baseObject;
2005-01-16 02:43:34 +08:00
} else if ( eid == &bsi.bsi_base_id ) {
/* don't recollect searchBase object... */
e = &base_entry;
2004-11-29 17:36:22 +08:00
} 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;
}
2003-04-03 06:58:02 +08:00
if ( !manageDSAit &&
2004-03-09 02:12:45 +08:00
op->ors_scope != LDAP_SCOPE_BASE &&
op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
is_entry_referral( e ) )
{
2003-04-03 06:58:02 +08:00
BerVarray refs;
refs = get_entry_referrals( op, e );
if ( !refs ) {
backsql_srch_info bsi2 = { 0 };
Entry user_entry2 = { 0 };
/* retry with the full entry... */
2005-01-16 02:43:34 +08:00
bsi2.bsi_e = &user_entry2;
rc = backsql_init_search( &bsi2,
&e->e_nname,
LDAP_SCOPE_BASE,
SLAP_NO_LIMIT, SLAP_NO_LIMIT,
(time_t)(-1), NULL,
2005-01-16 02:43:34 +08:00
dbh, op, rs, NULL,
BACKSQL_ISF_GET_ENTRY );
if ( rc == LDAP_SUCCESS ) {
if ( is_entry_referral( &user_entry2 ) )
{
refs = get_entry_referrals( op,
&user_entry2 );
2005-01-16 02:43:34 +08:00
} else {
rs->sr_err = LDAP_OTHER;
}
backsql_entry_clean( op, &user_entry2 );
}
if ( bsi2.bsi_attrs != NULL ) {
op->o_tmpfree( bsi2.bsi_attrs,
op->o_tmpmemctx );
}
}
2003-04-03 06:58:02 +08:00
if ( refs ) {
rs->sr_ref = referral_rewrite( refs,
&e->e_name,
&op->o_req_dn,
2004-03-09 02:12:45 +08:00
op->ors_scope );
2003-04-03 06:58:02 +08:00
ber_bvarray_free( refs );
}
2005-01-16 02:43:34 +08:00
if ( rs->sr_ref ) {
rs->sr_err = LDAP_REFERRAL;
} else {
rs->sr_text = "bad referral object";
2003-04-03 06:58:02 +08:00
}
2004-11-29 17:36:22 +08:00
rs->sr_entry = e;
rs->sr_matched = user_entry.e_name.bv_val;
2003-04-08 07:21:55 +08:00
send_search_reference( op, rs );
2003-04-03 06:58:02 +08:00
ber_bvarray_free( rs->sr_ref );
rs->sr_ref = NULL;
rs->sr_matched = NULL;
2004-11-29 17:36:22 +08:00
rs->sr_entry = NULL;
goto next_entry;
}
/*
* We use this flag since we need to parse the filter
* anyway; we should have used the frontend API function
* filter_has_subordinates()
*/
if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
rc = backsql_has_children( bi, dbh, &e->e_nname );
switch ( rc ) {
case LDAP_COMPARE_TRUE:
case LDAP_COMPARE_FALSE:
a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
if ( a_hasSubordinate != NULL ) {
2004-11-29 17:36:22 +08:00
for ( ap = &user_entry.e_attrs;
*ap;
ap = &(*ap)->a_next );
2004-11-29 17:36:22 +08:00
*ap = a_hasSubordinate;
}
rc = 0;
break;
default:
Debug(LDAP_DEBUG_TRACE,
"backsql_search(): "
"has_children failed( %d)\n",
rc, 0, 0 );
rc = 1;
goto next_entry;
}
}
if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
a_entryUUID = backsql_operational_entryUUID( bi, eid );
if ( a_entryUUID != NULL ) {
2004-11-29 17:36:22 +08:00
if ( ap == NULL ) {
ap = &user_entry.e_attrs;
}
for ( ; *ap; ap = &(*ap)->a_next );
2004-11-29 17:36:22 +08:00
*ap = a_entryUUID;
}
}
#ifdef BACKSQL_SYNCPROV
if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
a_entryCSN = backsql_operational_entryCSN( op );
2004-11-29 17:36:22 +08:00
if ( a_entryCSN != NULL ) {
if ( ap == NULL ) {
ap = &user_entry.e_attrs;
}
for ( ; *ap; ap = &(*ap)->a_next );
2004-11-29 17:36:22 +08:00
*ap = a_entryCSN;
}
}
#endif /* BACKSQL_SYNCPROV */
if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
{
rs->sr_attrs = op->ors_attrs;
rs->sr_operational_attrs = NULL;
rs->sr_entry = e;
if ( e == &user_entry ) {
rs->sr_flags = REP_ENTRY_MODIFIABLE;
}
/* FIXME: need the whole entry (ITS#3480) */
sres = send_search_entry( op, rs );
rs->sr_entry = NULL;
rs->sr_attrs = NULL;
rs->sr_operational_attrs = NULL;
2005-01-16 02:43:34 +08:00
if ( sres == -1 ) {
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
/*
* FIXME: send_search_entry failed;
* better stop
*/
Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
"connection lost\n", 0, 0, 0 );
goto end_of_search;
}
}
next_entry:;
2005-01-16 02:43:34 +08:00
if ( e == &user_entry ) {
backsql_entry_clean( op, &user_entry );
2005-01-16 02:43:34 +08:00
}
2004-11-29 17:36:22 +08:00
next_entry2:;
if ( op->ors_slimit != SLAP_NO_LIMIT
&& rs->sr_nentries >= op->ors_slimit )
{
2003-04-03 06:58:02 +08:00
rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
2005-02-19 19:07:02 +08:00
goto send_results;
}
}
Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?)
2002-08-23 16:54:08 +08:00
end_of_search:;
2003-04-03 06:58:02 +08:00
if ( rs->sr_nentries > 0 ) {
rs->sr_ref = rs->sr_v2ref;
rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
: LDAP_REFERRAL;
} else {
rs->sr_err = bsi.bsi_status;
}
2005-02-19 19:07:02 +08:00
send_results:;
2005-02-19 20:25:55 +08:00
if ( rs->sr_err != SLAPD_ABANDON ) {
send_ldap_result( op, rs );
}
2003-04-03 06:58:02 +08:00
backsql_entry_clean( op, &base_entry );
2005-02-19 19:07:02 +08:00
/* in case we got here accidentally */
backsql_entry_clean( op, &user_entry );
2005-02-19 19:07:02 +08:00
2003-04-03 06:58:02 +08:00
if ( rs->sr_v2ref ) {
ber_bvarray_free( rs->sr_v2ref );
rs->sr_v2ref = NULL;
}
#ifdef BACKSQL_SYNCPROV
if ( op->o_sync ) {
Operation op2 = *op;
SlapReply rs2 = { 0 };
Entry e = { 0 };
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_callback = &cb;
e.e_name = op->o_bd->be_suffix[0];
e.e_nname = op->o_bd->be_nsuffix[0];
cb.sc_response = slap_null_cb;
op2.o_bd->be_add( &op2, &rs2 );
}
#endif /* BACKSQL_SYNCPROV */
done:;
2005-01-16 02:43:34 +08:00
(void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
if ( bsi.bsi_attrs != NULL ) {
op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
}
if ( !BER_BVISNULL( &nbase )
&& nbase.bv_val != op->o_req_ndn.bv_val )
{
ch_free( nbase.bv_val );
}
/* restore scope ... FIXME: this should be done before ANY
* frontend call that uses op */
if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
op->ors_scope = LDAP_SCOPE_BASE;
}
Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2005-02-19 20:25:55 +08:00
return rs->sr_err;
}
/* return LDAP_SUCCESS IFF we can retrieve the specified entry.
*/
int
backsql_entry_get(
Operation *op,
struct berval *ndn,
ObjectClass *oc,
AttributeDescription *at,
int rw,
Entry **ent )
{
backsql_srch_info bsi = { 0 };
SQLHDBC dbh = SQL_NULL_HDBC;
int rc;
SlapReply rs = { 0 };
AttributeName anlist[ 2 ];
2005-01-16 02:43:34 +08:00
*ent = NULL;
rc = backsql_get_db_conn( op, &dbh );
if ( !dbh ) {
return LDAP_OTHER;
}
if ( at ) {
anlist[ 0 ].an_name = at->ad_cname;
anlist[ 0 ].an_desc = at;
BER_BVZERO( &anlist[ 1 ].an_name );
}
2005-01-16 02:43:34 +08:00
bsi.bsi_e = ch_malloc( sizeof( Entry ) );
rc = backsql_init_search( &bsi,
ndn,
LDAP_SCOPE_BASE,
SLAP_NO_LIMIT, SLAP_NO_LIMIT,
(time_t)(-1), NULL,
dbh, op, &rs, at ? anlist : NULL,
2005-01-16 02:43:34 +08:00
BACKSQL_ISF_GET_ENTRY );
if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
(void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
}
if ( rc == LDAP_SUCCESS ) {
#if 0 /* not supported at present */
/* find attribute values */
if ( is_entry_alias( bsi.bsi_e ) ) {
Debug( LDAP_DEBUG_ACL,
"<= backsql_entry_get: entry is an alias\n",
0, 0, 0 );
rc = LDAP_ALIAS_PROBLEM;
goto return_results;
}
#endif
if ( is_entry_referral( bsi.bsi_e ) ) {
Debug( LDAP_DEBUG_ACL,
"<= backsql_entry_get: entry is a referral\n",
0, 0, 0 );
rc = LDAP_REFERRAL;
goto return_results;
}
if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
Debug( LDAP_DEBUG_ACL,
"<= backsql_entry_get: "
"failed to find objectClass\n",
0, 0, 0 );
rc = LDAP_NO_SUCH_ATTRIBUTE;
goto return_results;
}
*ent = bsi.bsi_e;
}
return_results:;
if ( bsi.bsi_attrs != NULL ) {
op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
}
if ( rc != LDAP_SUCCESS ) {
if ( bsi.bsi_e ) {
entry_free( bsi.bsi_e );
}
}
return rc;
}
void
backsql_entry_clean(
Operation *op,
Entry *e )
{
void *ctx;
ctx = ldap_pvt_thread_pool_context();
2005-06-02 03:36:58 +08:00
if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
if ( !BER_BVISNULL( &e->e_name ) ) {
op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
BER_BVZERO( &e->e_name );
}
if ( !BER_BVISNULL( &e->e_nname ) ) {
op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
BER_BVZERO( &e->e_nname );
}
}
entry_clean( e );
}
int
backsql_entry_release(
Operation *op,
Entry *e,
int rw )
{
backsql_entry_clean( op, e );
ch_free( e );
return 0;
}