From f11c6b27e7f09dc6ea476e892dbaa577cfcac843 Mon Sep 17 00:00:00 2001
From: Pierangelo Masarati <ando@openldap.org>
Date: Fri, 23 Aug 2002 08:54:08 +0000
Subject: [PATCH] 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 ?)
---
 doc/man/man5/slapd-sql.5                      |  70 ++-
 servers/slapd/back-sql/back-sql.h             | 117 ++++-
 servers/slapd/back-sql/config.c               | 161 +++++-
 servers/slapd/back-sql/entry-id.c             | 143 ++++-
 servers/slapd/back-sql/entry-id.h             |   2 +
 servers/slapd/back-sql/external.h             |   2 +
 servers/slapd/back-sql/init.c                 | 237 +++++++--
 servers/slapd/back-sql/modify.c               | 494 +++++++++++++++---
 servers/slapd/back-sql/other.c                |  66 +++
 .../rdbms_depend/ibmdb2/backsql_create.sql    |   1 +
 .../back-sql/rdbms_depend/ibmdb2/slapd.conf   |   3 +
 .../rdbms_depend/ibmdb2/testdb_create.sql     |   3 +-
 .../rdbms_depend/ibmdb2/testdb_data.sql       |   6 +-
 .../rdbms_depend/ibmdb2/testdb_metadata.sql   |  21 +-
 .../back-sql/rdbms_depend/pgsql/slapd.conf    |   2 +-
 servers/slapd/back-sql/schema-map.c           | 246 +++++----
 servers/slapd/back-sql/schema-map.h           |  28 +-
 servers/slapd/back-sql/search.c               | 456 +++++++++-------
 servers/slapd/back-sql/sql-wrap.c             |  46 +-
 servers/slapd/back-sql/util.c                 | 309 +++++++++--
 servers/slapd/back-sql/util.h                 |  33 +-
 21 files changed, 1886 insertions(+), 560 deletions(-)

diff --git a/doc/man/man5/slapd-sql.5 b/doc/man/man5/slapd-sql.5
index 6e52f64acb..c0b55fc623 100644
--- a/doc/man/man5/slapd-sql.5
+++ b/doc/man/man5/slapd-sql.5
@@ -63,7 +63,9 @@ These three options are generally unneeded, because this information is already
 taken from the datasource.
 Use them if you need to override datasource settings.
 Also, several RDBMS' drivers tend to require explicit passing of user/password,
-even if those are given in datasource.
+even if those are given in datasource (Note:
+.B dbhost
+is currently ignored).
 .RE
 .TP
 .B subtree_cond <SQL expression>
@@ -92,18 +94,74 @@ adding and deleting entries to ldap_entries, etc.
 All these and subtree_cond should have the given default values.
 For the current value it is recommended to look at the sources,
 or in the log output when slapd starts with "-d 5" or greater.
+Note that the parameter number and order must not be changed.
 .TP
 .B upper_func <SQL function name>
 Specifies the name of a function that converts a given value to uppercase.
 This is used for CIS matching when the RDBMS is case sensitive.
 .TP
+.B upper_needs_cast { yes | no }
+Set this directive to 
+.B yes
+if 
+.B upper_func
+needs an explicit cast when applied to literal strings.  The form
+.B cast (<arg> as varchar(<max DN length>))
+is used, where
+.B <max DN length>
+is builtin.
+This is
+.B experimental 
+and may change in future releases.
+.TP
+.B concat_pattern <pattern>
+This statement defines the
+.B pattern 
+to be used to concatenate strings.  The
+.B pattern
+MUST contain two question marks, '?', that will be replaced 
+by the two strings that must be concatenated.  The default value is
+.BR "CONCAT(?,?)";
+a form that is known to be highly portable is 
+.BR "?||?",
+but an explicit cast may be required when operating on literal strings:
+.BR "cast(?||? as varchar(<length>))".
+On some RDBMSes the form
+.B "?+?"
+is known to work.
+Carefully check the documentation of your RDBMS or stay with the examples
+for supported ones.
+This is
+.B experimental 
+and may change in future releases.
+.TP
 .B strcast_func <SQL function name>
 Specifies the name of a function that converts a given value to a string
-for appropriate ordering.  This is used when selecting distinct data.
+for appropriate ordering.  This is used in "SELECT DISTINCT" statements
+for strongly typed RDBMSes with little implicit casting (like PostgreSQL),
+when a literal string is specified.
+This is
+.B experimental 
+and may change in future releases.
 .TP
 .B has_ldapinfo_dn_ru { yes | no }
-Explicitly inform the backend whether the SQL schema has dn_ru or not.
-Overrides automatic check (required by PostgreSQL).
+Explicitly inform the backend whether the SQL schema has dn_ru column
+(dn in reverse uppercased form) or not.
+Overrides automatic check (required by PostgreSQL/unixODBC).
+This is
+.B experimental 
+and may change in future releases.
+
+.TP
+.B fail_if_no_mapping { yes | no }
+When set to
+.B yes
+it forces write operations to fail if no appropriate mapping between LDAP
+attributes and SQL data is available.  The default behavior is to ignore
+those changes that cannot be mapped correctly.
+This is
+.B experimental 
+and may change in future releases.
 
 .SH METAINFORMATION USED
 .LP
@@ -217,7 +275,7 @@ Keytbl and keycol thus contain "persons" (name of the table), and "id"
   ldap_attr_mappings (some columns are not listed for clarity)
   -----------
   id=1
-  oc_id=1
+  oc_map_id=1
   name="cn"
   sel_expr="CONCAT(persons.first_name,' ',persons.last_name)"
   from_tbls="persons"
@@ -356,7 +414,7 @@ information on this matter - they are self-explanatory for those familiar
 with concept expressed above.
 .LP
 .SH common techniques (referrals, multiclassing etc.)
-First of all, lets remember that among other major differences to the
+First of all, let's remember that among other major differences to the
 complete LDAP data model, the concept above does not directly support
 such things as multiple objectclasses per entry, and referrals.
 Fortunately, they are easy to adopt in this scheme.
diff --git a/servers/slapd/back-sql/back-sql.h b/servers/slapd/back-sql/back-sql.h
index bd349175a4..a456351479 100644
--- a/servers/slapd/back-sql/back-sql.h
+++ b/servers/slapd/back-sql/back-sql.h
@@ -1,6 +1,3 @@
-#ifndef __BACKSQL_H__
-#define __BACKSQL_H__
-
 /*
  *	 Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
  *
@@ -9,6 +6,66 @@
  *	 license is available at http://www.OpenLDAP.org/license.html or
  *	 in file LICENSE in the top-level directory of the distribution.
  */
+/*
+ *	 Copyright 2002, Pierangelo Masarati <ando@OpenLDAP.org>.
+ *	 All rights reserved.
+ *
+ *	 This is a modified version of back-sql; the same conditions
+ *	 of the above reported Copyright statement, and sigificantly
+ *	 the OpenLDAP Public License apply.  Credits go to Dmitry 
+ *	 Kovalev for the initial development of the backend.
+ *
+ *	 This copyright statement cannot be altered.
+ */
+/*
+ *	 The following changes have been addressed:
+ *	 
+ * 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 ?)
+ *   - check how to allow multiple operations with one statement, to remove
+ *     BACKSQL_REALLOC_STMT from modify.c (a more recent unixODBC lib?)
+ */
+
+#ifndef __BACKSQL_H__
+#define __BACKSQL_H__
 
 #include "external.h"
 #include "sql-types.h"
@@ -18,6 +75,12 @@
  */
 #define BACKSQL_MAX_DN_LEN	255
 
+/*
+ * define to enable very extensive trace logging (debug only)
+ */
+#undef BACKSQL_TRACE
+
+
 typedef struct {
 	char		*dbhost;
 	int		dbport;
@@ -26,27 +89,51 @@ typedef struct {
 	char		*dbname;
  	/*
 	 * SQL condition for subtree searches differs in syntax:
-	 * "LIKE CONCAT('%',?)" or "LIKE '%'+?" or smth else 
+	 * "LIKE CONCAT('%',?)" or "LIKE '%'+?" or "LIKE '%'||?"
+	 * or smth else 
 	 */
-	char		*subtree_cond;
-	char		*oc_query,*at_query;
+	struct berval	subtree_cond;
+	struct berval	children_cond;
+	char		*oc_query, *at_query;
 	char		*insentry_query,*delentry_query;
 	char		*id_query;
-	char		*upper_func;
-	char		*strcast_func;
+	char		*has_children_query;
+	struct berval	upper_func;
+	struct berval	upper_func_open;
+	struct berval	upper_func_close;
+	BerVarray	concat_func;
+
+	unsigned int	bsql_flags;
+#define	BSQLF_SCHEMA_LOADED		0x0001
+#define	BSQLF_UPPER_NEEDS_CAST		0x0002
+#define	BSQLF_CREATE_NEEDS_SELECT	0x0004
+#define	BSQLF_FAIL_IF_NO_MAPPING	0x0008
+#define BSQLF_HAS_LDAPINFO_DN_RU	0x0010
+#define BSQLF_DONTCHECK_LDAPINFO_DN_RU	0x0020
+#define BSQLF_USE_REVERSE_DN		0x0040
+
+#define	BACKSQL_SCHEMA_LOADED(si) \
+	((si)->bsql_flags & BSQLF_SCHEMA_LOADED)
+#define BACKSQL_UPPER_NEEDS_CAST(si) \
+	((si)->bsql_flags & BSQLF_UPPER_NEEDS_CAST)
+#define BACKSQL_CREATE_NEEDS_SELECT(si) \
+	((si)->bsql_flags & BSQLF_CREATE_NEEDS_SELECT)
+#define BACKSQL_FAIL_IF_NO_MAPPING(si) \
+	((si)->bsql_flags & BSQLF_FAIL_IF_NO_MAPPING)
+#define BACKSQL_HAS_LDAPINFO_DN_RU(si) \
+	((si)->bsql_flags & BSQLF_HAS_LDAPINFO_DN_RU)
+#define BACKSQL_DONTCHECK_LDAPINFO_DN_RU(si) \
+	((si)->bsql_flags & BSQLF_DONTCHECK_LDAPINFO_DN_RU)
+#define BACKSQL_USE_REVERSE_DN(si) \
+	((si)->bsql_flags & BSQLF_USE_REVERSE_DN)
+	
+	struct berval	strcast_func;
 	Avlnode		*db_conns;
 	Avlnode		*oc_by_oc;
 	Avlnode		*oc_by_id;
-	int		schema_loaded;
 	ldap_pvt_thread_mutex_t		dbconn_mutex;
 	ldap_pvt_thread_mutex_t		schema_mutex;
  	SQLHENV		db_env;
- 	int		isTimesTen;
-
-	/* 
-	 * Does ldapinfo.dn_ru exist in schema?
-	 */
-	int		has_ldapinfo_dn_ru;  
 } backsql_info;
 
 #define BACKSQL_SUCCESS( rc ) \
diff --git a/servers/slapd/back-sql/config.c b/servers/slapd/back-sql/config.c
index 7403cc1d6e..3472496ae6 100644
--- a/servers/slapd/back-sql/config.c
+++ b/servers/slapd/back-sql/config.c
@@ -17,6 +17,7 @@
 #include "slap.h"
 #include "back-sql.h"
 #include "sql-wrap.h"
+#include "util.h"
 
 int
 backsql_db_config(
@@ -35,7 +36,7 @@ backsql_db_config(
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE, 
 				"<==backsql_db_config (%s line %d): "
-				"missing hostname in dbhost directive\n",
+				"missing hostname in \"dbhost\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 	    	}
@@ -48,7 +49,7 @@ backsql_db_config(
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE, 
 				"<==backsql_db_config (%s line %d): "
-				"missing username in dbuser directive\n",
+				"missing username in \"dbuser\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
@@ -60,7 +61,7 @@ backsql_db_config(
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE, 
 				"<==backsql_db_config (%s line %d): "
-				"missing password in dbpasswd directive\n",
+				"missing password in \"dbpasswd\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
@@ -72,33 +73,53 @@ backsql_db_config(
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE, 
 				"<==backsql_db_config (%s line %d): "
-				"missing database name in dbname directive\n",
-				fname, lineno, 0 );
+				"missing database name in \"dbname\" "
+				"directive\n", fname, lineno, 0 );
 			return 1;
 		}
 		si->dbname = ch_strdup( argv[ 1 ] );
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): dbname=%s\n",
 			si->dbname, 0, 0 );
 
+	} else if ( !strcasecmp( argv[ 0 ], "concat_pattern" ) ) {
+		if ( argc < 2 ) {
+			Debug( LDAP_DEBUG_TRACE, 
+				"<==backsql_db_config (%s line %d): "
+				"missing pattern"
+				"in \"concat_pattern\" directive\n",
+				fname, lineno, 0 );
+			return 1;
+		}
+		if ( backsql_split_pattern( argv[ 1 ], &si->concat_func, 2 ) ) {
+			Debug( LDAP_DEBUG_TRACE, 
+				"<==backsql_db_config (%s line %d): "
+				"unable to parse pattern \"%s\"\n"
+				"in \"concat_pattern\" directive\n",
+				fname, lineno, argv[ 1 ] );
+			return 1;
+		}
+		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+			"concat_pattern=\"%s\"\n", argv[ 1 ], 0, 0 );
+
 	} else if ( !strcasecmp( argv[ 0 ], "subtree_cond" ) ) {
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE, 
 				"<==backsql_db_config (%s line %d): "
 				"missing SQL condition "
-				"in subtree_cond directive\n",
+				"in \"subtree_cond\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
-		si->subtree_cond = ch_strdup( argv[ 1 ] );
+		ber_str2bv( argv[ 1 ], 0, 1, &si->subtree_cond );
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
-			"subtree_cond=%s\n", si->subtree_cond, 0, 0 );
+			"subtree_cond=%s\n", si->subtree_cond.bv_val, 0, 0 );
 
 	} else if ( !strcasecmp( argv[ 0 ], "oc_query" ) ) {
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE, 
 				"<==backsql_db_config (%s line %d): "
 				"missing SQL statement "
-				"in oc_query directive\n",
+				"in \"oc_query\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
@@ -111,7 +132,7 @@ backsql_db_config(
 			Debug( LDAP_DEBUG_TRACE,
 				"<==backsql_db_config (%s line %d): "
 				"missing SQL statement "
-				"in at_query directive\n",
+				"in \"at_query\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
@@ -124,7 +145,7 @@ backsql_db_config(
 			Debug( LDAP_DEBUG_TRACE, 
 				"<==backsql_db_config (%s line %d): "
 				"missing SQL statement "
-				"in insentry_query directive\n",
+				"in \"insentry_query\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
@@ -132,38 +153,97 @@ backsql_db_config(
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
 			"insentry_query=%s\n", si->insentry_query, 0, 0 );
 
+	} else if ( !strcasecmp( argv[ 0 ], "create_needs_select" ) ) {
+		if ( argc < 2 ) {
+			Debug( LDAP_DEBUG_TRACE,
+				"<==backsql_db_config (%s line %d): "
+				"missing { yes | no }"
+				"in \"create_needs_select\" directive\n",
+				fname, lineno, 0 );
+			return 1;
+		}
+
+		if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+			si->bsql_flags |= BSQLF_CREATE_NEEDS_SELECT;
+
+		} else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+			si->bsql_flags &= ~BSQLF_CREATE_NEEDS_SELECT;
+
+		} else {
+			Debug( LDAP_DEBUG_TRACE,
+				"<==backsql_db_config (%s line %d): "
+				"\"create_needs_select\" directive arg "
+				"must be \"yes\" or \"no\"\n",
+				fname, lineno, 0 );
+			return 1;
+
+		}
+		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+			"create_needs_select =%s\n", 
+			BACKSQL_CREATE_NEEDS_SELECT( si ) ? "yes" : "no",
+			0, 0 );
+
 	} else if ( !strcasecmp( argv[ 0 ], "upper_func" ) ) {
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE,
 				"<==backsql_db_config (%s line %d): "
 				"missing function name "
-				"in upper_func directive\n",
+				"in \"upper_func\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
-		si->upper_func = ch_strdup( argv[ 1 ] );
+		ber_str2bv( argv[ 1 ], 0, 1, &si->upper_func );
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
-			"upper_func=%s\n", si->upper_func, 0, 0 );
+			"upper_func=%s\n", si->upper_func.bv_val, 0, 0 );
+
+	} else if ( !strcasecmp( argv[ 0 ], "upper_needs_cast" ) ) {
+		if ( argc < 2 ) {
+			Debug( LDAP_DEBUG_TRACE,
+				"<==backsql_db_config (%s line %d): "
+				"missing { yes | no }"
+				"in \"upper_needs_cast\" directive\n",
+				fname, lineno, 0 );
+			return 1;
+		}
+
+		if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+			si->bsql_flags |= BSQLF_UPPER_NEEDS_CAST;
+
+		} else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+			si->bsql_flags &= ~BSQLF_UPPER_NEEDS_CAST;
+
+		} else {
+			Debug( LDAP_DEBUG_TRACE,
+				"<==backsql_db_config (%s line %d): "
+				"\"upper_needs_cast\" directive arg "
+				"must be \"yes\" or \"no\"\n",
+				fname, lineno, 0 );
+			return 1;
+
+		}
+		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+			"upper_needs_cast =%s\n", 
+			BACKSQL_UPPER_NEEDS_CAST( si ) ? "yes" : "no", 0, 0 );
 
 	} else if ( !strcasecmp( argv[ 0 ], "strcast_func" ) ) {
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE,
 				"<==backsql_db_config (%s line %d): "
 				"missing function name "
-				"in strcast_func directive\n",
+				"in \"strcast_func\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
-		si->strcast_func = ch_strdup( argv[ 1 ] );
+		ber_str2bv( argv[ 1 ], 0, 1, &si->strcast_func );
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
-			"strcast_func=%s\n", si->strcast_func, 0, 0 );
+			"strcast_func=%s\n", si->strcast_func.bv_val, 0, 0 );
 
 	} else if ( !strcasecmp( argv[ 0 ], "delentry_query" ) ) {
 		if ( argc < 2 ) {
 			Debug( LDAP_DEBUG_TRACE,
 				"<==backsql_db_config (%s line %d): "
 				"missing SQL statement "
-				"in delentry_query directive\n",
+				"in \"delentry_query\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
@@ -176,19 +256,23 @@ backsql_db_config(
 			Debug( LDAP_DEBUG_TRACE,
 				"<==backsql_db_config (%s line %d): "
 				"missing { yes | no }"
-				"in has_ldapinfo_dn_ru directive\n",
+				"in \"has_ldapinfo_dn_ru\" directive\n",
 				fname, lineno, 0 );
 			return 1;
 		}
 
 		if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
-			si->has_ldapinfo_dn_ru = 1;
+			si->bsql_flags |= BSQLF_HAS_LDAPINFO_DN_RU;
+			si->bsql_flags |= BSQLF_DONTCHECK_LDAPINFO_DN_RU;
+
 		} else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
-			si->has_ldapinfo_dn_ru = 0;
+			si->bsql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU;
+			si->bsql_flags |= BSQLF_DONTCHECK_LDAPINFO_DN_RU;
+
 		} else {
 			Debug( LDAP_DEBUG_TRACE,
 				"<==backsql_db_config (%s line %d): "
-				"has_ldapinfo_dn_ru directive arg "
+				"\"has_ldapinfo_dn_ru\" directive arg "
 				"must be \"yes\" or \"no\"\n",
 				fname, lineno, 0 );
 			return 1;
@@ -196,11 +280,40 @@ backsql_db_config(
 		}
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
 			"has_ldapinfo_dn_ru=%s\n", 
-			si->has_ldapinfo_dn_ru == 0 ? "no" : "yes", 0, 0 );
+			BACKSQL_HAS_LDAPINFO_DN_RU( si ) ? "yes" : "no", 0, 0 );
+
+	} else if ( !strcasecmp( argv[ 0 ], "fail_if_no_mapping") ) {
+		if ( argc < 2 ) {
+			Debug( LDAP_DEBUG_TRACE,
+				"<==backsql_db_config (%s line %d): "
+				"missing { yes | no }"
+				"in \"fail_if_no_mapping\" directive\n",
+				fname, lineno, 0 );
+			return 1;
+		}
+
+		if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+			si->bsql_flags |= BSQLF_FAIL_IF_NO_MAPPING;
+
+		} else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+			si->bsql_flags &= ~BSQLF_FAIL_IF_NO_MAPPING;
+
+		} else {
+			Debug( LDAP_DEBUG_TRACE,
+				"<==backsql_db_config (%s line %d): "
+				"\"fail_if_no_mapping\" directive arg "
+				"must be \"yes\" or \"no\"\n",
+				fname, lineno, 0 );
+			return 1;
+
+		}
+		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+			"fail_if_no_mapping=%s\n", 
+			BACKSQL_FAIL_IF_NO_MAPPING( si ) ? "yes" : "no", 0, 0 );
 
 	} else {
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config (%s line %d): "
-			"unknown directive '%s' (ignored)\n",
+			"unknown directive \"%s\" (ignored)\n",
 			fname, lineno, argv[ 0 ] );
 	}
 
diff --git a/servers/slapd/back-sql/entry-id.c b/servers/slapd/back-sql/entry-id.c
index 552d23946e..90564463ac 100644
--- a/servers/slapd/back-sql/entry-id.c
+++ b/servers/slapd/back-sql/entry-id.c
@@ -42,11 +42,6 @@ backsql_free_entryID( backsql_entryID *id, int freeit )
 	return next;
 }
 
-/*
- * FIXME: need to change API to pass backsql_entryID **id 
- * and return an error code, to distinguish LDAP_OTHER from
- * LDAP_NO_SUCH_OBJECT
- */
 int
 backsql_dn2id(
 	backsql_info		*bi,
@@ -56,9 +51,6 @@ backsql_dn2id(
 {
 	SQLHSTMT		sth; 
 	BACKSQL_ROW_NTS		row;
-#if 0
- 	SQLINTEGER		nrows = 0;
-#endif
 	RETCODE 		rc;
 	int			res;
 
@@ -93,7 +85,7 @@ backsql_dn2id(
 		return LDAP_OTHER;
 	}
 
-	if ( bi->has_ldapinfo_dn_ru ) {
+	if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
 		/*
 		 * Prepare an upper cased, byte reversed version 
 		 * that can be searched using indexes
@@ -109,7 +101,7 @@ backsql_dn2id(
 				upperdn, 0, 0 );
 		toBind = upperdn;
 	} else {
-		if ( bi->isTimesTen ) {
+		if ( BACKSQL_USE_REVERSE_DN( bi ) ) {
 			AC_MEMCPY( upperdn, dn->bv_val, dn->bv_len + 1 );
 			ldap_pvt_str2upper( upperdn );
 			Debug( LDAP_DEBUG_TRACE,
@@ -126,7 +118,8 @@ backsql_dn2id(
 	if ( rc != SQL_SUCCESS) {
 		/* end TimesTen */ 
 		Debug( LDAP_DEBUG_TRACE, "backsql_dn2id(): "
-			"error binding dn=\"%s\" parameter:\n", toBind, 0, 0 );
+			"error binding dn=\"%s\" parameter:\n", 
+			toBind, 0, 0 );
 		backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
 		SQLFreeStmt( sth, SQL_DROP );
 		return LDAP_OTHER;
@@ -145,9 +138,9 @@ backsql_dn2id(
 	backsql_BindRowAsStrings( sth, &row );
 	rc = SQLFetch( sth );
 	if ( BACKSQL_SUCCESS( rc ) ) {
-		id->id = atoi( row.cols[ 0 ] );
-		id->keyval = atoi( row.cols[ 1 ] );
-		id->oc_id = atoi( row.cols[ 2 ] );
+		id->id = strtol( row.cols[ 0 ], NULL, 0 );
+		id->keyval = strtol( row.cols[ 1 ], NULL, 0 );
+		id->oc_id = strtol( row.cols[ 2 ], NULL, 0 );
 		ber_dupbv( &id->dn, dn );
 		id->next = NULL;
 
@@ -169,6 +162,86 @@ backsql_dn2id(
 	return res;
 }
 
+int
+backsql_has_children(
+	backsql_info		*bi,
+	SQLHDBC			dbh,
+	struct berval		*dn )
+{
+	SQLHSTMT		sth; 
+	BACKSQL_ROW_NTS		row;
+	RETCODE 		rc;
+	int			res;
+
+	Debug( LDAP_DEBUG_TRACE, "==>backsql_has_children(): dn='%s'\n", 
+			dn->bv_val, 0, 0 );
+
+	if ( dn->bv_len > BACKSQL_MAX_DN_LEN ) {
+		Debug( LDAP_DEBUG_TRACE, 
+			"backsql_has_children(): DN \"%s\" (%ld bytes) "
+			"exceeds max DN length (%d):\n",
+			dn->bv_val, dn->bv_len, BACKSQL_MAX_DN_LEN );
+		return LDAP_OTHER;
+	}
+	
+	/* begin TimesTen */
+	Debug(LDAP_DEBUG_TRACE, "children id query '%s'\n", 
+			bi->has_children_query, 0, 0);
+	assert( bi->has_children_query );
+ 	rc = backsql_Prepare( dbh, &sth, bi->has_children_query, 0 );
+	if ( rc != SQL_SUCCESS ) {
+		Debug( LDAP_DEBUG_TRACE, 
+			"backsql_has_children(): error preparing SQL:\n%s", 
+			bi->has_children_query, 0, 0);
+		backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
+		SQLFreeStmt( sth, SQL_DROP );
+		return LDAP_OTHER;
+	}
+
+	rc = backsql_BindParamStr( sth, 1, dn->bv_val, BACKSQL_MAX_DN_LEN );
+	if ( rc != SQL_SUCCESS) {
+		/* end TimesTen */ 
+		Debug( LDAP_DEBUG_TRACE, "backsql_has_children(): "
+			"error binding dn=\"%s\" parameter:\n", 
+			dn->bv_val, 0, 0 );
+		backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
+		SQLFreeStmt( sth, SQL_DROP );
+		return LDAP_OTHER;
+	}
+
+	rc = SQLExecute( sth );
+	if ( rc != SQL_SUCCESS ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_has_children(): "
+			"error executing query (\"%s\", \"%s\"):\n", 
+			bi->has_children_query, dn->bv_val, 0 );
+		backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
+		SQLFreeStmt( sth, SQL_DROP );
+		return LDAP_OTHER;
+	}
+
+	backsql_BindRowAsStrings( sth, &row );
+	
+	rc = SQLFetch( sth );
+	if ( BACKSQL_SUCCESS( rc ) ) {
+		if ( strtol( row.cols[ 0 ], NULL, 0 ) > 0 ) {
+			res = LDAP_COMPARE_TRUE;
+		} else {
+			res = LDAP_COMPARE_FALSE;
+		}
+
+	} else {
+		res = LDAP_OTHER;
+	}
+	backsql_FreeRow( &row );
+
+	SQLFreeStmt( sth, SQL_DROP );
+
+	Debug( LDAP_DEBUG_TRACE, "<==backsql_has_children(): %s\n",
+			res == LDAP_COMPARE_TRUE ? "yes" : "no", 0, 0 );
+
+	return res;
+}
+
 int
 backsql_get_attr_vals( backsql_at_map_rec *at, backsql_srch_info *bsi )
 {
@@ -182,7 +255,9 @@ backsql_get_attr_vals( backsql_at_map_rec *at, backsql_srch_info *bsi )
  
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_get_attr_vals(): "
 		"oc='%s' attr='%s' keyval=%ld\n",
-		bsi->oc->name.bv_val, at->name.bv_val, bsi->c_eid->keyval );
+		// bsi->oc->name.bv_val, at->name.bv_val, 
+		bsi->oc->oc->soc_names[0], at->ad->ad_cname.bv_val, 
+		bsi->c_eid->keyval );
 
 	rc = backsql_Prepare( bsi->dbh, &sth, at->query, 0 );
 	if ( rc != SQL_SUCCESS ) {
@@ -230,14 +305,14 @@ backsql_get_attr_vals( backsql_at_map_rec *at, backsql_srch_info *bsi )
        				backsql_entry_addattr( bsi->e, 
 						&row.col_names[ i ], &bv );
 
-#if 0
+#ifdef BACKSQL_TRACE
 				Debug( LDAP_DEBUG_TRACE, "prec=%d\n",
 					(int)row.col_prec[ i ], 0, 0 );
 			} else {
       				Debug( LDAP_DEBUG_TRACE, "NULL value "
 					"in this row for attribute '%s'\n",
 					row.col_names[ i ].bv_val, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 			}
 		}
 	}
@@ -255,6 +330,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid )
 	int			i;
 	backsql_at_map_rec	*at;
 	int			rc;
+	AttributeDescription	*ad_oc = slap_schema.si_ad_objectClass;
 
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_id2entry()\n", 0, 0, 0 );
 
@@ -279,7 +355,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid )
 		for ( i = 0; bsi->attrs[ i ].an_name.bv_val; i++ ) {
 			AttributeName *attr = &bsi->attrs[ i ];
 
-			if ( attr->an_desc == slap_schema.si_ad_objectClass
+			if ( attr->an_desc == ad_oc
 #if 0	/* FIXME: what is 0.10 ? */
 					|| !BACKSQL_NCMP( &attr->an_name, &bv_n_0_10 ) 
 #endif
@@ -303,6 +379,7 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid )
 					bsi->oc->name.bv_val, 0 );
 			}
 		}
+
 	} else {
 		Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(): "
 			"retrieving all attributes\n", 0, 0, 0 );
@@ -310,7 +387,35 @@ backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid )
 				bsi, 0, AVL_INORDER );
 	}
 
-	backsql_entry_addattr( bsi->e, &bv_n_objectclass, &bsi->oc->name );
+	if ( attr_merge_one( bsi->e, ad_oc, &bsi->oc->name ) ) {
+		entry_free( e );
+		return NULL;
+	}
+
+	if ( global_schemacheck ) {
+		const char	*text = NULL;
+		char		textbuf[ 1024 ];
+		size_t		textlen = sizeof( textbuf );
+		struct berval	bv[ 2 ] = { bsi->oc->name, { 0, NULL } };
+		struct berval	soc;
+		AttributeDescription	*ad_soc
+			= slap_schema.si_ad_structuralObjectClass;
+
+		int rc = structural_class( bv, &soc, NULL, 
+				&text, textbuf, textlen );
+		if ( rc != LDAP_SUCCESS ) {
+			entry_free( e );
+			return NULL;
+		}
+
+		if ( bsi->attr_flags | BSQL_SF_ALL_OPER 
+				|| an_find( bsi->attrs, &AllOper ) ) {
+			if ( attr_merge_one( bsi->e, ad_soc, &soc ) ) {
+				entry_free( e );
+				return NULL;
+			}
+		}
+	}
 
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_id2entry()\n", 0, 0, 0 );
 
diff --git a/servers/slapd/back-sql/entry-id.h b/servers/slapd/back-sql/entry-id.h
index bc52fa8669..c29470b4e8 100644
--- a/servers/slapd/back-sql/entry-id.h
+++ b/servers/slapd/back-sql/entry-id.h
@@ -21,6 +21,8 @@ typedef struct backsql_entryID {
 
 int backsql_dn2id( backsql_info *bi, backsql_entryID *id,
 		SQLHDBC dbh, struct berval *dn );
+int backsql_has_children( backsql_info *bi, SQLHDBC dbh, struct berval *dn );
+
 /* returns next */
 backsql_entryID *backsql_free_entryID( backsql_entryID *id, int freeit );
 
diff --git a/servers/slapd/back-sql/external.h b/servers/slapd/back-sql/external.h
index f016dfabb2..a45ac6f002 100644
--- a/servers/slapd/back-sql/external.h
+++ b/servers/slapd/back-sql/external.h
@@ -34,6 +34,8 @@ extern BI_op_add	backsql_add;
 extern BI_op_delete	backsql_delete;
 extern BI_op_abandon	backsql_abandon;
 
+extern BI_operational	backsql_operational;
+
 extern BI_connection_destroy	backsql_connection_destroy;
 
 LDAP_END_DECL
diff --git a/servers/slapd/back-sql/init.c b/servers/slapd/back-sql/init.c
index 79d8dec530..3d4e30242f 100644
--- a/servers/slapd/back-sql/init.c
+++ b/servers/slapd/back-sql/init.c
@@ -74,6 +74,7 @@ sql_back_initialize(
 	bi->bi_acl_group = 0;
 	bi->bi_acl_attribute = 0;
 	bi->bi_chk_referrals = 0;
+	bi->bi_operational = backsql_operational;
  
 	bi->bi_connection_init = 0;
 	bi->bi_connection_destroy = backsql_connection_destroy;
@@ -100,10 +101,10 @@ backsql_db_init(
  
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_db_init()\n", 0, 0, 0 );
 	si = (backsql_info *)ch_calloc( 1, sizeof( backsql_info ) );
+	memset( si, '\0', sizeof( backsql_info ) );
 	ldap_pvt_thread_mutex_init( &si->dbconn_mutex );
 	ldap_pvt_thread_mutex_init( &si->schema_mutex );
 	backsql_init_db_env( si );
-	si->has_ldapinfo_dn_ru = -1;
 
 	bd->be_private = si;
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_db_init()\n", 0, 0, 0 );
@@ -120,11 +121,11 @@ backsql_db_destroy(
 	ldap_pvt_thread_mutex_lock( &si->dbconn_mutex );
 	backsql_free_db_env( si );
 	ldap_pvt_thread_mutex_unlock( &si->dbconn_mutex );
+	ldap_pvt_thread_mutex_destroy( &si->dbconn_mutex );
 	ldap_pvt_thread_mutex_lock( &si->schema_mutex );
 	backsql_destroy_schema_map( si );
 	ldap_pvt_thread_mutex_unlock( &si->schema_mutex );
 	ldap_pvt_thread_mutex_destroy( &si->schema_mutex );
-	ldap_pvt_thread_mutex_destroy( &si->dbconn_mutex );
 	free( si->dbname );
 	free( si->dbuser );
 	if ( si->dbpasswd ) {
@@ -133,11 +134,13 @@ backsql_db_destroy(
 	if ( si->dbhost ) {
 		free( si->dbhost );
 	}
-	if ( si->upper_func ) {
-		free( si->upper_func );
+	if ( si->upper_func.bv_val ) {
+		free( si->upper_func.bv_val );
+		free( si->upper_func_open.bv_val );
+		free( si->upper_func_close.bv_val );
 	}
 	
-	free( si->subtree_cond );
+	free( si->subtree_cond.bv_val );
 	free( si->oc_query );
 	free( si->at_query );
 	free( si->insentry_query );
@@ -155,7 +158,7 @@ backsql_db_open(
 	backsql_info 	*si = (backsql_info*)bd->be_private;
 	Connection 	tmp;
 	SQLHDBC 	dbh;
-	int 		idq_len;
+	ber_len_t	idq_len;
 	struct berval	bv;
 
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_db_open(): "
@@ -163,52 +166,194 @@ backsql_db_open(
 	if ( si->dbname == NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"datasource name not specified "
-			"(use dbname directive in slapd.conf)\n", 0, 0, 0 );
+			"(use \"dbname\" directive in slapd.conf)\n", 0, 0, 0 );
 		return 1;
 	}
+
+	if ( si->concat_func == NULL ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+			"concat func not specified (use \"concat_pattern\" "
+			"directive in slapd.conf)\n", 0, 0, 0 );
+
+		if ( backsql_split_pattern( backsql_def_concat_func, 
+				&si->concat_func, 2 ) ) {
+			Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+				"unable to parse pattern '%s'",
+				backsql_def_concat_func, 0, 0 );
+			return 1;
+		}
+	}
+
+	/*
+	 * Prepare cast string as required
+	 */
+	if ( si->upper_func.bv_val ) {
+		char buf[1024];
+
+		if ( BACKSQL_UPPER_NEEDS_CAST( si ) ) {
+			snprintf( buf, sizeof( buf ), 
+				"%s(cast (" /* ? as varchar(%d))) */ , 
+				si->upper_func.bv_val );
+			ber_str2bv( buf, 0, 1, &si->upper_func_open );
+
+			snprintf( buf, sizeof( buf ),
+				/* (cast(? */ " as varchar(%d)))",
+				BACKSQL_MAX_DN_LEN );
+			ber_str2bv( buf, 0, 1, &si->upper_func_close );
+
+		} else {
+			snprintf( buf, sizeof( buf ), "%s(" /* ?) */ ,
+					si->upper_func.bv_val );
+			ber_str2bv( buf, 0, 1, &si->upper_func_open );
+
+			ber_str2bv( /* (? */ ")", 0, 1, &si->upper_func_close );
+		}
+	}
 	
 	if ( si->dbuser == NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"user name not specified "
-			"(use dbuser directive in slapd.conf)\n", 0, 0, 0 );
+			"(use \"dbuser\" directive in slapd.conf)\n", 0, 0, 0 );
 		return 1;
 	}
 	
-	if ( si->subtree_cond == NULL ) {
+	if ( si->subtree_cond.bv_val == NULL ) {
+		/*
+		 * Prepare concat function for subtree search condition
+		 */
+		struct berval	concat;
+		ber_len_t	len = 0;
+		struct berval	values[] = {
+			{ sizeof( "'%'" ) - 1,	"'%'" },
+			{ sizeof( "?" ) - 1,	"?" },
+			{ 0,			NULL }
+		};
+
+		if ( backsql_prepare_pattern( si->concat_func, values, 
+				&concat ) ) {
+			Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+				"unable to prepare CONCAT pattern", 0, 0, 0 );
+			return 1;
+		}
+			
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"subtree search SQL condition not specified "
-			"(use subtree_cond directive in slapd.conf)\n", 
+			"(use \"subtree_cond\" directive in slapd.conf)\n", 
+			0, 0, 0);
+
+		si->subtree_cond.bv_val = NULL;
+		si->subtree_cond.bv_len = 0;
+
+		if ( si->upper_func.bv_val ) {
+
+			/*
+			 * UPPER(ldap_entries.dn) LIKE UPPER(CONCAT('%',?))
+			 */
+
+			backsql_strfcat( &si->subtree_cond, &len, "blbbb",
+					&si->upper_func,
+					(ber_len_t)sizeof( "(ldap_entries.dn) LIKE " ) - 1,
+						"(ldap_entries.dn) LIKE ",
+					&si->upper_func_open,
+					&concat,
+					&si->upper_func_close );
+
+		} else {
+
+			/*
+			 * ldap_entries.dn LIKE CONCAT('%',?)
+			 */
+
+			backsql_strfcat( &si->subtree_cond, &len, "lb",
+					(ber_len_t)sizeof( "ldap_entries.dn LIKE " ) - 1,
+						"ldap_entries.dn LIKE ",
+					&concat );
+		}
+			
+		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+			"setting '%s' as default\n",
+			si->subtree_cond.bv_val, 0, 0 );
+	}
+
+	if ( si->children_cond.bv_val == NULL ) {
+		/*
+		 * Prepare concat function for children search condition
+		 */
+		struct berval	concat;
+		ber_len_t	len = 0;
+		struct berval	values[] = {
+			{ sizeof( "'%,'" ) - 1,	"'%,'" },
+			{ sizeof( "?" ) - 1,	"?" },
+			{ 0,			NULL }
+		};
+
+		if ( backsql_prepare_pattern( si->concat_func, values, 
+				&concat ) ) {
+			Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+				"unable to prepare CONCAT pattern", 0, 0, 0 );
+			return 1;
+		}
+			
+		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+			"children search SQL condition not specified "
+			"(use \"children_cond\" directive in slapd.conf)\n", 
 			0, 0, 0);
-		if ( si->upper_func ) {
-			struct berval	bv = { 0, NULL };
-			int		len = 0;
 
-			backsql_strcat( &bv, &len, si->upper_func,
-					backsql_def_upper_subtree_cond, NULL );
-			si->subtree_cond = bv.bv_val;
+		si->children_cond.bv_val = NULL;
+		si->children_cond.bv_len = 0;
+
+		if ( si->upper_func.bv_val ) {
+
+			/*
+			 * UPPER(ldap_entries.dn) LIKE UPPER(CONCAT('%,',?))
+			 */
+
+			backsql_strfcat( &si->children_cond, &len, "blbbb",
+					&si->upper_func,
+					(ber_len_t)sizeof( "(ldap_entries.dn) LIKE " ) - 1,
+						"(ldap_entries.dn) LIKE ",
+					&si->upper_func_open,
+					&concat,
+					&si->upper_func_close );
+
 		} else {
-			si->subtree_cond = ch_strdup( backsql_def_subtree_cond );
+
+			/*
+			 * ldap_entries.dn LIKE CONCAT('%,',?)
+			 */
+
+			backsql_strfcat( &si->children_cond, &len, "lb",
+					(ber_len_t)sizeof( "ldap_entries.dn LIKE " ) - 1,
+						"ldap_entries.dn LIKE ",
+					&concat );
 		}
 			
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"setting '%s' as default\n",
-			si->subtree_cond, 0, 0 );
+			si->children_cond.bv_val, 0, 0 );
 	}
 
 	if ( si->oc_query == NULL ) {
+		if ( BACKSQL_CREATE_NEEDS_SELECT( si ) ) {
+			si->oc_query =
+				ch_strdup( backsql_def_needs_select_oc_query );
+
+		} else {
+			si->oc_query = ch_strdup( backsql_def_oc_query );
+		}
+
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"objectclass mapping SQL statement not specified "
-			"(use oc_query directive in slapd.conf)\n", 0, 0, 0 );
+			"(use \"oc_query\" directive in slapd.conf)\n", 
+			0, 0, 0 );
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
-			"setting '%s' by default\n", 
-			backsql_def_oc_query, 0, 0 );
-		si->oc_query = ch_strdup( backsql_def_oc_query );
+			"setting '%s' by default\n", si->oc_query, 0, 0 );
 	}
 	
 	if ( si->at_query == NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"attribute mapping SQL statement not specified "
-			"(use at_query directive in slapd.conf)\n",
+			"(use \"at_query\" directive in slapd.conf)\n",
 			0, 0, 0 );
 		Debug(LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"setting '%s' by default\n",
@@ -219,7 +364,7 @@ backsql_db_open(
 	if ( si->insentry_query == NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"entry insertion SQL statement not specified "
-			"(use insentry_query directive in slapd.conf)\n",
+			"(use \"insentry_query\" directive in slapd.conf)\n",
 			0, 0, 0 );
 		Debug(LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"setting '%s' by default\n",
@@ -230,14 +375,15 @@ backsql_db_open(
 	if ( si->delentry_query == NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"entry deletion SQL statement not specified "
-			"(use delentry_query directive in slapd.conf)\n",
+			"(use \"delentry_query\" directive in slapd.conf)\n",
 			0, 0, 0 );
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"setting '%s' by default\n",
 			backsql_def_delentry_query, 0, 0 );
 		si->delentry_query = ch_strdup( backsql_def_delentry_query );
 	}
-	
+
+
 	tmp.c_connid =- 1;
 	if ( backsql_get_db_conn( bd, &tmp, &dbh ) != LDAP_SUCCESS ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
@@ -245,36 +391,55 @@ backsql_db_open(
 		return 1;
 	}
 
+	/*
+	 * Prepare ID selection query
+	 */
 	si->id_query = NULL;
 	idq_len = 0;
 
 	bv.bv_val = NULL;
 	bv.bv_len = 0;
-	if ( si->upper_func == NULL ) {
+	if ( si->upper_func.bv_val == NULL ) {
 		backsql_strcat( &bv, &idq_len, backsql_id_query, 
 				"dn=?", NULL );
 	} else {
-		if ( si->has_ldapinfo_dn_ru ) {
+		if ( BACKSQL_HAS_LDAPINFO_DN_RU( si ) ) {
 			backsql_strcat( &bv, &idq_len, backsql_id_query,
 					"dn_ru=?", NULL );
 		} else {
-			if ( si->isTimesTen ) {
-				backsql_strcat( &bv, &idq_len, 
+			if ( BACKSQL_USE_REVERSE_DN( si ) ) {
+				backsql_strfcat( &bv, &idq_len, "sbl",
 						backsql_id_query,
-						si->upper_func, "(dn)=?",
-						NULL );
+						&si->upper_func, 
+						(ber_len_t)sizeof( "(dn)=?" ) - 1, "(dn)=?" );
 			} else {
-				backsql_strcat( &bv, &idq_len, 
+				backsql_strfcat( &bv, &idq_len, "sblbcb",
 						backsql_id_query,
-						si->upper_func, "(dn)=",
-						si->upper_func, "(?)", NULL );
+						&si->upper_func, 
+						(ber_len_t)sizeof( "(dn)=" ) - 1, "(dn)=",
+						&si->upper_func_open, 
+						'?', 
+						&si->upper_func_close );
 			}
 		}
 	}
 	si->id_query = bv.bv_val;
+
+       	/*
+	 * Prepare children ID selection query
+	 */
+	si->has_children_query = NULL;
+	idq_len = 0;
+
+	bv.bv_val = NULL;
+	bv.bv_len = 0;
+	backsql_strfcat( &bv, &idq_len, "sb", 
+			"select count(*) from ldap_entries where ",
+			&si->children_cond );
+	si->has_children_query = bv.bv_val;
  
 	backsql_free_db_conn( bd, &tmp );
-	if ( !si->schema_loaded ) {
+	if ( !BACKSQL_SCHEMA_LOADED( si ) ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
 			"test failed, schema map not loaded - exiting\n",
 			0, 0, 0 );
diff --git a/servers/slapd/back-sql/modify.c b/servers/slapd/back-sql/modify.c
index 2757d33e4c..6966c2c6fd 100644
--- a/servers/slapd/back-sql/modify.c
+++ b/servers/slapd/back-sql/modify.c
@@ -23,29 +23,50 @@
 #include "util.h"
 
 /*
- * PostgreSQL doesn't work without :(
+ * PostgreSQL 7.0 doesn't work without :(
  */
 #define	BACKSQL_REALLOC_STMT
 
+/*
+ * Skip:
+ * - the first occurrence of objectClass, which is used
+ *   to determine how to bulid the SQL entry (FIXME ?!?)
+ * - operational attributes
+ *   empty attributes (FIXME ?!?)
+ */
+#define	backsql_attr_skip(ad,vals) \
+	( \
+		( (ad) == slap_schema.si_ad_objectClass \
+				&& (vals)[ 1 ].bv_val == NULL ) \
+		|| is_at_operational( (ad)->ad_type ) \
+		|| ( (vals)[ 0 ].bv_val == NULL ) \
+	)
+
 static int
 backsql_modify_internal(
 	backsql_info		*bi,
 	SQLHDBC			dbh, 
 	backsql_oc_map_rec	*oc,
 	backsql_entryID		*e_id,
-	Modifications		*modlist )
+	Modifications		*modlist,
+	const char		**text )
 {
 	RETCODE		rc;
 	SQLHSTMT	sth;
 	Modifications	*ml;
+	int		res = LDAP_SUCCESS;
 
-	Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+	Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): "
 		"traversing modifications list\n", 0, 0, 0 );
+
+	*text = NULL;
+
 #ifndef BACKSQL_REALLOC_STMT
 	SQLAllocStmt( dbh, &sth );
 #endif /* BACKSQL_REALLOC_STMT */
+
 	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
-		AttributeDescription *ad;
+		AttributeDescription	*ad;
 		backsql_at_map_rec	*at = NULL;
 		struct berval		*at_val;
 		Modification		*c_mod;
@@ -60,16 +81,30 @@ backsql_modify_internal(
 #endif /* BACKSQL_REALLOC_STMT */
 
 		c_mod = &ml->sml_mod;
-
 		ad = c_mod->sm_desc;
-		Debug( LDAP_DEBUG_TRACE, "backsql_modify(): attribute '%s'\n",
-				ad->ad_cname.bv_val, 0, 0 );
+
+		Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): "
+			"modifying attribute '%s'\n",
+			ad->ad_cname.bv_val, 0, 0 );
+
+		if ( backsql_attr_skip( ad, c_mod->sm_bvalues ) ) {
+			continue;
+		}
+
   		at = backsql_ad2at( oc, ad );
 		if ( at == NULL ) {
-			Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+			Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): "
 				"attribute provided is not registered "
-				"in objectclass '%s'\n",
+				"in objectClass '%s'\n",
 				ad->ad_cname.bv_val, 0, 0 );
+
+			if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+				res = LDAP_UNWILLING_TO_PERFORM;
+				*text = "operation not permitted "
+					"within namingContext";
+				goto done;
+			}
+
 			continue;
 		}
   
@@ -77,58 +112,100 @@ backsql_modify_internal(
 		case LDAP_MOD_REPLACE: {
 			SQLHSTMT asth;
 			BACKSQL_ROW_NTS row;
-			 
-			Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+			
+			Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): "
 				"replacing values for attribute '%s'\n",
-				at->name.bv_val, 0, 0 );
+				at->ad->ad_cname.bv_val, 0, 0 );
 
 			if ( at->add_proc == NULL ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"add procedure is not defined "
 					"for attribute '%s' "
 					"- unable to perform replacements\n",
-					at->name.bv_val, 0, 0 );
+					at->ad->ad_cname.bv_val, 0, 0 );
+
+				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+					res = LDAP_UNWILLING_TO_PERFORM;
+					*text = "operation not permitted "
+						"within namingContext";
+					goto done;
+				}
+
 				break;
 			}
 
 			if ( at->delete_proc == NULL ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"delete procedure is not defined "
 					"for attribute '%s' "
 					"- adding only\n",
-					at->name.bv_val, 0, 0 );
+					at->ad->ad_cname.bv_val, 0, 0 );
+
+				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+					res = LDAP_UNWILLING_TO_PERFORM;
+					*text = "operation not permitted "
+						"within namingContext";
+					goto done;
+				}
+
 				goto add_only;
 			}
 			
 del_all:
 			rc = backsql_Prepare( dbh, &asth, at->query, 0 );
 			if ( rc != SQL_SUCCESS ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"error preparing query\n", 0, 0, 0 );
 				backsql_PrintErrors( bi->db_env, dbh, 
 						asth, rc );
+
+				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+					res = LDAP_OTHER;
+					*text = "SQL-backend error";
+					goto done;
+				}
+
 				break;
 			}
 
 			rc = backsql_BindParamID( asth, 1, &e_id->keyval );
 			if ( rc != SQL_SUCCESS ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"error binding key value parameter\n",
 					0, 0, 0 );
 				backsql_PrintErrors( bi->db_env, dbh, 
 						asth, rc );
 				SQLFreeStmt( asth, SQL_DROP );
+
+				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+					res = LDAP_OTHER;
+					*text = "SQL-backend error";
+					goto done;
+				}
+
 				break;
 			}
 			
 			rc = SQLExecute( asth );
 			if ( !BACKSQL_SUCCESS( rc ) ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"error executing attribute query\n",
 					0, 0, 0 );
 				backsql_PrintErrors( bi->db_env, dbh, 
 						asth, rc );
 				SQLFreeStmt( asth, SQL_DROP );
+
+				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+					res = LDAP_OTHER;
+					*text = "SQL-backend error";
+					goto done;
+				}
+
 				break;
 			}
 
@@ -163,19 +240,25 @@ del_all:
 						strlen( row.cols[ i ] ), 0 );
 			 
 					Debug( LDAP_DEBUG_TRACE, 
-						"backsql_modify(): "
+						"backsql_modify_internal(): "
 						"executing '%s'\n",
 						at->delete_proc, 0, 0 );
 					rc = SQLExecDirect( sth,
 						at->delete_proc, SQL_NTS );
 					if ( rc != SQL_SUCCESS ) {
 						Debug( LDAP_DEBUG_TRACE,
-							"backsql_modify(): "
+							"backsql_modify_internal(): "
 							"delete_proc "
 							"execution failed\n",
 							0, 0, 0 );
 						backsql_PrintErrors( bi->db_env,
 								dbh, sth, rc );
+
+						if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+							res = LDAP_OTHER;
+							*text = "SQL-backend error";
+							goto done;
+						}
 					}
 #ifdef BACKSQL_REALLOC_STMT
 					SQLFreeStmt( sth, SQL_DROP );
@@ -194,24 +277,25 @@ del_all:
 		case SLAP_MOD_SOFTADD:
 add_only:;
 			if ( at->add_proc == NULL ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"add procedure is not defined "
 					"for attribute '%s'\n",
-					at->name.bv_val, 0, 0 );
-				break;
-			}
-			
-			if ( c_mod->sm_bvalues == NULL ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
-					"no values given to add "
-					"for attribute '%s'\n",
-					at->name.bv_val, 0, 0 );
+					at->ad->ad_cname.bv_val, 0, 0 );
+
+				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+					res = LDAP_UNWILLING_TO_PERFORM;
+					*text = "operation not permitted "
+						"within namingContext";
+					goto done;
+				}
+
 				break;
 			}
 			
-			Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+			Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): "
 				"adding new values for attribute '%s'\n",
-				at->name.bv_val, 0, 0 );
+				at->ad->ad_cname.bv_val, 0, 0 );
 			for ( i = 0, at_val = c_mod->sm_bvalues;
 					at_val->bv_val != NULL; 
 					i++, at_val++ ) {
@@ -240,18 +324,25 @@ add_only:;
 					0, 0, at_val->bv_val, 
 					at_val->bv_len, 0 );
 
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"executing '%s'\n", 
 					at->add_proc, 0, 0 );
 				rc = SQLExecDirect( sth, at->add_proc, 
 						SQL_NTS );
 				if ( rc != SQL_SUCCESS ) {
 					Debug( LDAP_DEBUG_TRACE,
-						"backsql_modify(): "
+						"backsql_modify_internal(): "
 						"add_proc execution failed\n",
 						0, 0, 0 );
 					backsql_PrintErrors( bi->db_env,
 							dbh, sth, rc );
+
+					if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+						res = LDAP_OTHER;
+						*text = "SQL-backend error";
+						goto done;
+					}
 				}
 #ifdef BACKSQL_REALLOC_STMT
 				SQLFreeStmt( sth, SQL_DROP );
@@ -262,25 +353,36 @@ add_only:;
 			
 	      	case LDAP_MOD_DELETE:
 			if ( at->delete_proc == NULL ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"delete procedure is not defined "
 					"for attribute '%s'\n",
-					at->name.bv_val, 0, 0 );
+					at->ad->ad_cname.bv_val, 0, 0 );
+
+				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+					res = LDAP_UNWILLING_TO_PERFORM;
+					*text = "operation not permitted "
+						"within namingContext";
+					goto done;
+				}
+
 				break;
 			}
 
 			if ( c_mod->sm_bvalues == NULL ) {
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"no values given to delete "
 					"for attribute '%s' "
 					"-- deleting all values\n",
-					at->name.bv_val, 0, 0 );
+					at->ad->ad_cname.bv_val, 0, 0 );
 				goto del_all;
 			}
 
-			Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+			Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): "
 				"deleting values for attribute '%s'\n",
-				at->name.bv_val, 0, 0 );
+				at->ad->ad_cname.bv_val, 0, 0 );
+
 			for ( i = 0, at_val = c_mod->sm_bvalues;
 					at_val->bv_val != NULL;
 					i++, at_val++ ) {
@@ -308,18 +410,25 @@ add_only:;
 					0, 0, at_val->bv_val, 
 					at_val->bv_len, 0 );
 
-				Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+				Debug( LDAP_DEBUG_TRACE,
+					"backsql_modify_internal(): "
 					"executing '%s'\n", 
 					at->delete_proc, 0, 0 );
 				rc = SQLExecDirect( sth, at->delete_proc,
 						SQL_NTS );
 				if ( rc != SQL_SUCCESS ) {
 					Debug( LDAP_DEBUG_TRACE,
-						"backsql_modify(): "
+						"backsql_modify_internal(): "
 						"delete_proc execution "
 						"failed\n", 0, 0, 0 );
 					backsql_PrintErrors( bi->db_env,
 							dbh, sth, rc );
+
+					if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+						res = LDAP_OTHER;
+						*text = "SQL-backend error";
+						goto done;
+					}
 				}
 #ifdef BACKSQL_REALLOC_STMT
 				SQLFreeStmt( sth, SQL_DROP );
@@ -335,6 +444,8 @@ add_only:;
 #endif /* BACKSQL_REALLOC_STMT */
 	}
 
+done:;
+	
 #ifndef BACKSQL_REALLOC_STMT
 	SQLFreeStmt( sth, SQL_DROP );
 #endif /* BACKSQL_REALLOC_STMT */
@@ -342,7 +453,7 @@ add_only:;
 	/*
 	 * FIXME: should fail in case one change fails?
 	 */
-	return LDAP_SUCCESS;
+	return res;
 }
 
 int
@@ -358,7 +469,9 @@ backsql_modify(
 	SQLHDBC 		dbh;
 	backsql_oc_map_rec	*oc = NULL;
 	backsql_entryID		e_id;
+	Entry			e;
 	int			res;
+	const char		*text = NULL;
 
 	/*
 	 * FIXME: in case part of the operation cannot be performed
@@ -415,7 +528,17 @@ backsql_modify(
 		return 1;
 	}
 
-	res = backsql_modify_internal( bi, dbh, oc, &e_id, modlist );
+	e.e_attrs = NULL;
+	e.e_name = *dn;
+	e.e_nname = *ndn;
+	if ( !acl_check_modlist( be, conn, op, &e, modlist )) {
+		res = LDAP_INSUFFICIENT_ACCESS;
+
+	} else {
+		res = backsql_modify_internal( bi, dbh, oc, &e_id, 
+				modlist, &text );
+	}
+
 	if ( res == LDAP_SUCCESS ) {
 		/*
 		 * Commit only if all operations succeed
@@ -428,7 +551,7 @@ backsql_modify(
 		 */
 		SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT );
 	}
-	send_ldap_result( conn, op, res, "", NULL, NULL, NULL );
+	send_ldap_result( conn, op, res, "", text, NULL, NULL );
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_modify()\n", 0, 0, 0 );
 
 	return 0;
@@ -460,6 +583,7 @@ backsql_modrdn(
 	const char		*text = NULL;
 	LDAPRDN			*new_rdn = NULL;
 	LDAPRDN			*old_rdn = NULL;
+	Entry			e;
 	Modifications		*mod;
  
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_modrdn() renaming entry '%s', "
@@ -494,9 +618,43 @@ backsql_modrdn(
 	Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): entry id is %ld\n",
 		e_id.id, 0, 0 );
 
+	if ( backsql_has_children( bi, dbh, ndn ) == LDAP_COMPARE_TRUE ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+			"entry \"%s\" has children\n", dn->bv_val, 0, 0 );
+		send_ldap_result( conn, op, LDAP_NOT_ALLOWED_ON_NONLEAF,
+                        	NULL, "subtree delete not supported",
+				NULL, NULL );
+		return 1;
+	}
+
 	dnParent( dn, &p_dn );
 	dnParent( ndn, &p_ndn );
 
+	/*
+	 * namingContext "" is not supported
+	 */
+	if ( p_dn.bv_len == 0 ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+			"parent is \"\" - aborting\n", 0, 0, 0 );
+		send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, 
+				"", "not allowed within namingContext", 
+				NULL, NULL );
+		goto modrdn_return;
+	}
+
+	/*
+	 * Check for children access to parent
+	 */
+	e.e_attrs = NULL;
+	e.e_name = p_dn;
+	e.e_nname = p_ndn;
+	if ( !access_allowed( be, conn, op, &e, slap_schema.si_ad_children, 
+				NULL, ACL_WRITE, NULL ) ) {
+		Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0, 0, 0 );
+		res = LDAP_INSUFFICIENT_ACCESS;
+		goto modrdn_return;
+	}
+
 	if ( newSuperior ) {
 		/*
 		 * namingContext "" is not supported
@@ -513,6 +671,21 @@ backsql_modrdn(
 		new_pdn = newSuperior;
 		new_npdn = nnewSuperior;
 
+		e.e_name = *new_pdn;
+		e.e_nname = *new_npdn;
+
+		/*
+		 * Check for children access to new parent
+		 */
+		if ( !access_allowed( be, conn, op, &e, 
+					slap_schema.si_ad_children, 
+					NULL, ACL_WRITE, NULL ) ) {
+			Debug( LDAP_DEBUG_TRACE, "no access to new parent\n", 
+					0, 0, 0 );
+			res = LDAP_INSUFFICIENT_ACCESS;
+			goto modrdn_return;
+		}
+
 	} else {
 		new_pdn = &p_dn;
 		new_npdn = &p_ndn;
@@ -611,8 +784,9 @@ backsql_modrdn(
 		goto modrdn_return;
 	}
 
-	/* Get attribute type and attribute value of our new rdn, we will
-	 * need to add that to our new entry
+	/*
+	 * Get attribute type and attribute value of our new rdn,
+	 * we will need to add that to our new entry
 	 */
 	if ( ldap_bv2rdn( newrdn, &new_rdn, (char **)&text, 
 				LDAP_DN_FORMAT_LDAP ) ) {
@@ -664,29 +838,35 @@ backsql_modrdn(
 		}
 	}
 
-	res = slap_modrdn2mods( NULL, NULL, NULL, NULL, old_rdn, new_rdn, 
+	e.e_name = new_dn;
+	e.e_nname = new_ndn;
+	res = slap_modrdn2mods( be, conn, op, &e, old_rdn, new_rdn, 
 			deleteoldrdn, &mod );
 	if ( res != LDAP_SUCCESS ) {
 		goto modrdn_return;
 	}
 
-	oc = backsql_id2oc( bi, e_id.oc_id );
-	res = backsql_modify_internal( bi, dbh, oc, &e_id, mod );
-
-	if ( res != LDAP_SUCCESS ) {
+	if ( !acl_check_modlist( be, conn, op, &e, mod )) {
+		res = LDAP_INSUFFICIENT_ACCESS;
 		goto modrdn_return;
 	}
 
-	/*
-	 * Commit only if all operations succeed
-	 *
-	 * FIXME: backsql_modify_internal() does not fail 
-	 * if add/delete operations are not available, or
-	 * if a multiple value add actually results in a replace, 
-	 * or if a single operation on an attribute fails for any
-	 * reason
-	 */
-	SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT );
+	oc = backsql_id2oc( bi, e_id.oc_id );
+	res = backsql_modify_internal( bi, dbh, oc, &e_id, mod, &text );
+
+	if ( res == LDAP_SUCCESS ) {
+
+		/*
+		 * Commit only if all operations succeed
+		 *
+		 * FIXME: backsql_modify_internal() does not fail 
+		 * if add/delete operations are not available, or
+		 * if a multiple value add actually results in a replace, 
+		 * or if a single operation on an attribute fails for any
+		 * reason
+		 */
+		SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT );
+	}
 
 modrdn_return:
 	SQLFreeStmt( sth, SQL_DROP );
@@ -714,7 +894,7 @@ modrdn_return:
 		}
 	}
 
-	send_ldap_result( conn, op, res, "", NULL, NULL, NULL );
+	send_ldap_result( conn, op, res, "", text, NULL, NULL );
 
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_modrdn()\n", 0, 0, 0 );
 	return 0;
@@ -736,26 +916,43 @@ backsql_add(
 	backsql_oc_map_rec 	*oc = NULL;
 	backsql_at_map_rec	*at_rec = NULL;
 	backsql_entryID		e_id, parent_id;
+	Entry			p;
 	int			res;
 	Attribute		*at;
 	struct berval		*at_val;
 	struct berval		pdn;
-	/* first parameter no, parameter order */
+	/* first parameter #, parameter order */
 	SQLUSMALLINT		pno, po;
 	/* procedure return code */
 	int			prc;
 
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_add(): adding entry '%s'\n",
-			e->e_dn, 0, 0 );
+			e->e_name.bv_val, 0, 0 );
 
 	for ( at = e->e_attrs; at != NULL; at = at->a_next ) {
 		if ( at->a_desc == slap_schema.si_ad_objectClass ) {
-			/*
-			 * FIXME: only the objectClass provided first
-			 * is considered when creating a new entry
-			 */
-			oc = backsql_name2oc( bi, &at->a_vals[ 0 ] );
-		     	break;
+			if ( global_schemacheck ) {
+				const char	*text = NULL;
+				char		textbuf[ 1024 ];
+				size_t		textlen = sizeof( textbuf );
+				struct berval	soc;
+
+				int rc = structural_class( at->a_vals, &soc, 
+						NULL, &text, textbuf, textlen );
+				if ( rc != LDAP_SUCCESS ) {
+					break;
+				}
+				oc = backsql_name2oc( bi, &soc );
+
+			} else {
+
+				/*
+				 * FIXME: only the objectClass provided first
+				 * is considered when creating a new entry
+				 */
+				oc = backsql_name2oc( bi, &at->a_vals[ 0 ] );
+			}
+			break;
 		}
 	}
 
@@ -777,6 +974,16 @@ backsql_add(
 				"operation not permitted within namingContext",
 				NULL, NULL );
 		return 1;
+
+	} else if ( BACKSQL_CREATE_NEEDS_SELECT( bi )
+			&& oc->create_keyval == NULL ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+			"create procedure needs select, but none is defined"
+			"- aborting\n", 0, 0, 0 );
+		send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, "",
+				"operation not permitted within namingContext",
+				NULL, NULL );
+		return 1;
 	}
 
 	prc = backsql_get_db_conn( be, conn, &dbh );
@@ -862,6 +1069,16 @@ backsql_add(
 	 * is expected to return the id as the first column of a select
 	 */
 
+	p.e_attrs = NULL;
+	p.e_name = pdn;
+	dnParent( &e->e_nname, &p.e_nname );
+	if ( !access_allowed( be, conn, op, &p, slap_schema.si_ad_children,
+				NULL, ACL_WRITE, NULL ) ) {
+		send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, 
+				NULL, NULL, NULL, NULL );
+		return 1;
+	}
+
 #ifndef BACKSQL_REALLOC_STMT
 	rc = SQLAllocStmt( dbh, &sth );
 #else /* BACKSQL_REALLOC_STMT */
@@ -899,6 +1116,27 @@ backsql_add(
 		SWORD		ncols;
 		SQLINTEGER	is_null;
 
+		if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
+#ifndef BACKSQL_REALLOC_STMT
+			SQLFreeStmt( sth, SQL_RESET_PARAMS );
+#else /* BACKSQL_REALLOC_STMT */
+			SQLFreeStmt( sth, SQL_DROP );
+			rc = SQLAllocStmt( dbh, &sth );
+			if ( rc != SQL_SUCCESS ) {
+				send_ldap_result( conn, op, LDAP_OTHER, "",
+					"SQL-backend error", NULL, NULL );
+				return 1;
+			}
+#endif /* BACKSQL_REALLOC_STMT */
+
+			rc = SQLExecDirect( sth, oc->create_keyval, SQL_NTS );
+			if ( rc != SQL_SUCCESS ) {
+				send_ldap_result( conn, op, LDAP_OTHER, "",
+					"SQL-backend error", NULL, NULL );
+				return 1;
+			}
+		}
+
 		/*
 		 * the query to know the id of the inserted entry
 		 * must be embedded in the create procedure
@@ -916,8 +1154,8 @@ backsql_add(
 
 		} else if ( ncols != 1 ) {
 			Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
-				"create_proc result is bogus\n",
-				0, 0, 0 );
+				"create_proc result is bogus (ncols=%d)\n",
+				ncols, 0, 0 );
 			backsql_PrintErrors( bi->db_env, dbh, sth, rc);
 			SQLFreeStmt( sth, SQL_DROP );
 			send_ldap_result( conn, op, LDAP_OTHER, "",
@@ -979,7 +1217,18 @@ backsql_add(
 	for ( at = e->e_attrs; at != NULL; at = at->a_next ) {
 		SQLUSMALLINT	currpos;
 
-		if ( at->a_vals[ 0 ].bv_val == NULL ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+			"adding attribute '%s'\n", 
+			at->a_desc->ad_cname.bv_val, 0, 0 );
+
+		/*
+		 * Skip:
+		 * - the first occurrence of objectClass, which is used
+		 *   to determine how to bulid the SQL entry (FIXME ?!?)
+		 * - operational attributes
+		 *   empty attributes (FIXME ?!?)
+		 */
+		if ( backsql_attr_skip( at->a_desc, at->a_vals ) ) {
 			continue;
 		}
 
@@ -991,6 +1240,16 @@ backsql_add(
 				"in objectclass '%s'\n",
 				at->a_desc->ad_cname.bv_val,
 				oc->name.bv_val, 0 );
+
+			if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+				send_ldap_result( conn, op, 
+						LDAP_UNWILLING_TO_PERFORM, "",
+						"operation not permitted "
+						"within namingContext",
+						NULL, NULL );
+				return 1;
+			}
+
 			continue;
 		}
 		
@@ -999,12 +1258,31 @@ backsql_add(
 				"add procedure is not defined "
 				"for attribute '%s'\n",
 				at->a_desc->ad_cname.bv_val, 0, 0 );
+
+			if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+				send_ldap_result( conn, op, 
+						LDAP_UNWILLING_TO_PERFORM, "",
+						"operation not permitted "
+						"within namingContext",
+						NULL, NULL );
+				return 1;
+			}
+
 			continue;
 		}
 
 #ifdef BACKSQL_REALLOC_STMT
 		rc = backsql_Prepare( dbh, &sth, at_rec->add_proc, 0 );
 		if ( rc != SQL_SUCCESS ) {
+
+			if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+				send_ldap_result( conn, op, 
+						LDAP_OTHER, "",
+						"SQL-backend error",
+						NULL, NULL );
+				return 1;
+			}
+
 			continue;
 		}
 #endif /* BACKSQL_REALLOC_STMT */
@@ -1025,10 +1303,20 @@ backsql_add(
 				SQL_INTEGER, 0, 0, &new_keyval, 0, 0 );
 		currpos = pno + 2 - po;
 
-		for ( i = 0, at_val = &at->a_vals[ 0 ];
-				at_val->bv_val != NULL;
+		for ( i = 0, at_val = &at->a_vals[ i ];
+			       	at_val->bv_val != NULL;
 				i++, at_val = &at->a_vals[ i ] ) {
 
+			/*
+			 * Do not deal with the objectClass that is used
+			 * to build the entry
+			 */
+			if ( at->a_desc == slap_schema.si_ad_objectClass ) {
+				if ( ber_bvcmp( at_val, &oc->name ) == 0 ) {
+					continue;
+				}
+			}
+
 			/*
 			 * check for syntax needed here 
 			 * maybe need binary bind?
@@ -1055,6 +1343,14 @@ backsql_add(
 					"add_proc execution failed\n", 
 					0, 0, 0 );
 				backsql_PrintErrors( bi->db_env, dbh, sth, rc );
+
+				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+					send_ldap_result( conn, op, 
+							LDAP_OTHER, "",
+							"SQL-backend error",
+							NULL, NULL );
+					return 1;
+				}
 			}
 		}
 #ifndef BACKSQL_REALLOC_STMT
@@ -1072,6 +1368,7 @@ backsql_add(
 		return 1;
 	}
 #endif /* BACKSQL_REALLOC_STMT */
+	
 	backsql_BindParamStr( sth, 1, e->e_name.bv_val, BACKSQL_MAX_DN_LEN );
 	SQLBindParameter( sth, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
 			0, 0, &oc->id, 0, 0 );
@@ -1135,12 +1432,30 @@ backsql_delete(
 	RETCODE			rc;
 	backsql_oc_map_rec	*oc = NULL;
 	backsql_entryID		e_id;
+	Entry			e;
 	int			res;
 	/* first parameter no */
 	SQLUSMALLINT		pno;
 
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_delete(): deleting entry '%s'\n",
 			ndn->bv_val, 0, 0 );
+
+	dnParent( dn, &e.e_name );
+	dnParent( ndn, &e.e_nname );
+	e.e_attrs = NULL;
+
+	/* check parent for "children" acl */
+	if ( !access_allowed( be, conn, op, &e, slap_schema.si_ad_children, 
+			NULL, ACL_WRITE, NULL ) ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
+			"no write access to parent\n", 
+			0, 0, 0 );
+		send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, 
+				"", NULL, NULL, NULL );
+		return 1;
+
+	}
+	
 	res = backsql_get_db_conn( be, conn, &dbh );
 	if ( res != LDAP_SUCCESS ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
@@ -1160,13 +1475,32 @@ backsql_delete(
 		return 1;
 	}
 
+	res = backsql_has_children( bi, dbh, ndn );
+	switch ( res ) {
+	case LDAP_COMPARE_TRUE:
+		Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
+			"entry \"%s\" has children\n", dn->bv_val, 0, 0 );
+		send_ldap_result( conn, op, LDAP_NOT_ALLOWED_ON_NONLEAF,
+                        	NULL, "subtree delete not supported",
+				NULL, NULL );
+		return 1;
+
+	case LDAP_COMPARE_FALSE:
+		break;
+
+	default:
+		send_ldap_result( conn, op, res, NULL, NULL, NULL, NULL );
+		return 1;
+	}
+
 	oc = backsql_id2oc( bi, e_id.oc_id );
 	if ( oc == NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
 			"cannot determine objectclass of entry "
 			"-- aborting\n", 0, 0, 0 );
-		send_ldap_result( conn, op, LDAP_OTHER, "",
-				"SQL-backend error", NULL, NULL );
+		send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, "",
+				"operation not permitted within namingContext",
+				NULL, NULL );
  		return 1;
 	}
 
@@ -1175,7 +1509,7 @@ backsql_delete(
 			"delete procedure is not defined "
 			"for this objectclass - aborting\n", 0, 0, 0 );
 		send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, "",
-				"operation not supported for required DN", 
+				"operation not permitted within namingContext",
 				NULL, NULL );
 		return 1;
 	}
diff --git a/servers/slapd/back-sql/other.c b/servers/slapd/back-sql/other.c
index 57c16d546e..1629a086e3 100644
--- a/servers/slapd/back-sql/other.c
+++ b/servers/slapd/back-sql/other.c
@@ -16,6 +16,7 @@
 #include "slap.h"
 #include "back-sql.h"
 #include "sql-wrap.h"
+#include "entry-id.h"
 
 int
 backsql_compare(
@@ -43,5 +44,70 @@ backsql_abandon(
 	return 0;
 }
 
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+
+int
+backsql_operational(
+	BackendDB	*be,
+	Connection	*conn, 
+	Operation	*op,
+	Entry		*e,
+	AttributeName	*attrs,
+	int		opattrs,
+	Attribute	**a )
+{
+
+	backsql_info 		*bi = (backsql_info*)be->be_private;
+	SQLHDBC 		dbh = SQL_NULL_HDBC;
+	Attribute		**aa = a;
+	int			rc;
+
+	Debug( LDAP_DEBUG_TRACE, "==>backsql_operational(): entry '%s'\n",
+			e->e_nname.bv_val, 0, 0 );
+
+
+	if ( opattrs || ad_inlist( slap_schema.si_ad_hasSubordinates, attrs ) ) {
+		rc = backsql_get_db_conn( be, conn, &dbh );
+		if ( rc != LDAP_SUCCESS ) {
+			goto no_connection;
+		}
+		
+		rc = backsql_has_children( bi, dbh, &e->e_nname );
+
+		switch( rc ) {
+		case LDAP_COMPARE_TRUE:
+		case LDAP_COMPARE_FALSE:
+			*aa = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
+			if ( *aa != NULL ) {
+				aa = &(*aa)->a_next;
+			}
+			rc = 0;
+			break;
+
+		default:
+			Debug(LDAP_DEBUG_TRACE, 
+				"backsql_operational(): "
+				"has_children failed( %d)\n", 
+				rc, 0, 0 );
+			rc = 1;
+			break;
+		}
+	}
+
+	return rc;
+
+no_connection:;
+	Debug( LDAP_DEBUG_TRACE, "backsql_operational(): "
+		"could not get connection handle - exiting\n", 
+		0, 0, 0 );
+	send_ldap_result( conn, op, rc, "", 
+			rc == LDAP_OTHER ? "SQL-backend error" : "",
+			NULL, NULL );
+	return 1;
+}
+
 #endif /* SLAPD_SQL */
 
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql
index ca19bb1ed1..c82e75002e 100644
--- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql
@@ -6,6 +6,7 @@ create table ldap_oc_mappings
 	keytbl varchar(64) not null,
 	keycol varchar(64) not null,
 	create_proc varchar(255),
+	create_keyval varchar(255),
 	delete_proc varchar(255),
 	expect_return integer not null
 );
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf b/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf
index 86661da25d..556470cb53 100644
--- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf
@@ -30,4 +30,7 @@ dbpasswd	ibmdb2
 subtree_cond	"upper(ldap_entries.dn) LIKE CONCAT('%',?)"
 insentry_query	"insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select max(id)+1 from ldap_entries),?,?,?,?)"
 upper_func	"upper"
+upper_needs_cast	"yes"
+create_needs_select	"yes"
+has_ldapinfo_dn_ru	"no"
 
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql
index a1da4a1d5b..137d91f326 100644
--- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql
@@ -1,7 +1,8 @@
 drop table persons;
 CREATE TABLE persons (
 	id int NOT NULL,
-	name varchar(255) NOT NULL
+	name varchar(255) NOT NULL,
+	surname varchar(255) NOT NULL
 );
 
 drop table institutes;
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql
index f141f414eb..623d81990d 100644
--- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql
@@ -1,8 +1,8 @@
 insert into institutes (id,name) values (1,'sql');
 
-insert into persons (id,name) values (1,'Mitya Kovalev');
-insert into persons (id,name) values (2,'Torvlobnor Puzdoy');
-insert into persons (id,name) values (3,'Akakiy Zinberstein');
+insert into persons (id,name,surname) values (1,'Mitya','Kovalev');
+insert into persons (id,name,surname) values (2,'Torvlobnor','Puzdoy');
+insert into persons (id,name,surname) values (3,'Akakiy','Zinberstein');
 
 insert into phones (id,phone,pers_id) values (1,'332-2334',1);
 insert into phones (id,phone,pers_id) values (2,'222-3234',1);
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql
index c38036ba20..e538f19e7d 100644
--- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql
@@ -1,24 +1,27 @@
 --mappings 
 
-insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
-values (1,'inetOrgPerson','persons','id','insert into persons (name) values ('''');\n select last_insert_id();',NULL,0);
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return)
+values (1,'inetOrgPerson','persons','id','insert into persons (id,name,surname) values ((select max(id)+1 from persons),'''','''')','select max(id) from persons',NULL,0);
 
-insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
-values (2,'document','documents','id',NULL,NULL,0);
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return)
+values (2,'document','documents','id',NULL,NULL,NULL,0);
 
-insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
-values (3,'organization','institutes','id',NULL,NULL,0);
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return)
+values (3,'organization','institutes','id',NULL,NULL,NULL,0);
 
 
 insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
-values (1,1,'cn','persons.name','persons',NULL,NULL,NULL,3,0);
+values (1,1,'cn','case when persons.name!='''' and persons.surname!='''' then persons.name||'' ''||persons.surname when persons.surname!='''' then persons.surname when persons.name!='''' then persons.name else '''' end','persons',NULL,NULL,NULL,3,0);
 
 insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
 values (2,1,'telephoneNumber','phones.phone','persons,phones',
         'phones.pers_id=persons.id',NULL,NULL,3,0);
 
 insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
-values (3,1,'sn','persons.name','persons',NULL,NULL,NULL,3,0);
+values (3,1,'sn','persons.surname','persons',NULL,'update persons set surname=? where id=?',NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (6,1,'givenName','persons.name','persons',NULL,'update persons set name=? where id=?',NULL,3,0);
 
 insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
 values (4,2,'description','documents.abstract','documents',NULL,NULL,NULL,3,0);
@@ -35,7 +38,7 @@ insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,
 values (7,3,'o','institutes.name','institutes',NULL,NULL,NULL,3,0);
 
 insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
-values (8,1,'documentDN','ldap_entries.dn','ldap_entries,documents,authors_docs,persons',
+values (8,1,'documentAuthor','ldap_entries.dn','ldap_entries,documents,authors_docs,persons',
         'ldap_entries.keyval=documents.id AND ldap_entries.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
 	NULL,NULL,3,0);
 
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf b/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf
index 3a5dbb5ed8..aecc890597 100644
--- a/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf
+++ b/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf
@@ -27,9 +27,9 @@ rootpw		secret
 dbname		PostgreSQL
 dbuser		postgres
 dbpasswd	postgres
-subtree_cond	"upper(ldap_entries.dn) LIKE '%'||?"
 insentry_query	"insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select max(id)+1 from ldap_entries),?,?,?,?)"
 upper_func	"upper"
 strcast_func	"text"
+concat_pattern	"?||?"
 has_ldapinfo_dn_ru	no
 
diff --git a/servers/slapd/back-sql/schema-map.c b/servers/slapd/back-sql/schema-map.c
index cf765adc93..eaa63ab692 100644
--- a/servers/slapd/back-sql/schema-map.c
+++ b/servers/slapd/back-sql/schema-map.c
@@ -73,16 +73,25 @@ backsql_make_attr_query(
 	backsql_at_map_rec 	*at_map )
 {
 	struct berval	tmps = { 0, NULL };
-	int		tmpslen = 0;
-
-	backsql_strcat( &tmps, &tmpslen, "SELECT ", at_map->sel_expr, 
-			" AS ", at_map->name.bv_val, 
-			" FROM ", at_map->from_tbls,
-			" WHERE ", oc_map->keytbl,".", oc_map->keycol,
-			"=?", NULL );
-	if ( at_map->join_where != NULL ) {
-		backsql_strcat( &tmps, &tmpslen, " AND ", 
-				at_map->join_where, NULL );
+	ber_len_t	tmpslen = 0;
+
+	backsql_strfcat( &tmps, &tmpslen, "lblblblbcbl", 
+			(ber_len_t)sizeof( "SELECT " ) - 1, "SELECT ", 
+			&at_map->sel_expr, 
+			(ber_len_t)sizeof( " AS " ) - 1, " AS ", 
+			&at_map->ad->ad_cname,
+			(ber_len_t)sizeof( " FROM " ) - 1, " FROM ", 
+			&at_map->from_tbls, 
+			(ber_len_t)sizeof( " WHERE " ) - 1, " WHERE ", 
+			&oc_map->keytbl,
+			'.', 
+			&oc_map->keycol,
+			(ber_len_t)sizeof( "=?" ) - 1, "=?" );
+
+	if ( at_map->join_where.bv_val != NULL ) {
+		backsql_strfcat( &tmps, &tmpslen, "lb",
+				(ber_len_t)sizeof( " AND ") - 1, " AND ", 
+				&at_map->join_where );
 	}
 
 	at_map->query = tmps.bv_val;
@@ -94,30 +103,34 @@ static int
 backsql_add_sysmaps( backsql_oc_map_rec *oc_map )
 {
 	backsql_at_map_rec	*at_map;
-	int			len;
-	char			s[ 30 ]; 
-	struct berval		bv;
+	char			s[ 30 ];
+	ber_len_t		len, slen;
+	
 
 	snprintf( s, sizeof( s ), "%ld", oc_map->id );
+	slen = strlen( s );
 
 	at_map = (backsql_at_map_rec *)ch_calloc(1, 
 			sizeof( backsql_at_map_rec ) );
 	at_map->ad = slap_schema.si_ad_objectClass;
-	ber_dupbv( &at_map->name, &at_map->ad->ad_cname );
-	at_map->sel_expr = ch_strdup( "ldap_entry_objclasses.oc_name" );
-	at_map->from_tbls = ch_strdup( "ldap_entry_objclasses,ldap_entries" );
-	len = strlen( at_map->from_tbls );
-	backsql_merge_from_clause( &at_map->from_tbls, &len, oc_map->keytbl );
+	ber_str2bv( "ldap_entry_objclasses.oc_name", 0, 1, &at_map->sel_expr );
+	ber_str2bv( "ldap_entry_objclasses,ldap_entries", 0, 1, 
+			&at_map->from_tbls );
+	len = at_map->from_tbls.bv_len + 1;
+	backsql_merge_from_clause( &at_map->from_tbls, &len, &oc_map->keytbl );
 
 	len = 0;
-	bv.bv_val = NULL;
-	bv.bv_len = 0;
-	backsql_strcat( &bv, &len,
-			"ldap_entries.id=ldap_entry_objclasses.entry_id "
-			"and ldap_entries.keyval=",
-			oc_map->keytbl, ".", oc_map->keycol,
-			" and ldap_entries.oc_map_id=", s, NULL );
-	at_map->join_where = bv.bv_val;
+	at_map->join_where.bv_val = NULL;
+	at_map->join_where.bv_len = 0;
+	backsql_strfcat( &at_map->join_where, &len, "lbcbll",
+			(ber_len_t)sizeof( "ldap_entries.id=ldap_entry_objclasses.entry_id and ldap_entries.keyval=" ) - 1,
+				"ldap_entries.id=ldap_entry_objclasses.entry_id and ldap_entries.keyval=",
+			&oc_map->keytbl, 
+			'.', 
+			&oc_map->keycol,
+			(ber_len_t)sizeof( " and ldap_entries.oc_map_id=" ) - 1, 
+				" and ldap_entries.oc_map_id=", 
+			slen, s );
 
 	at_map->add_proc = NULL;
 	at_map->delete_proc = NULL;
@@ -130,21 +143,23 @@ backsql_add_sysmaps( backsql_oc_map_rec *oc_map )
 	at_map = (backsql_at_map_rec *)ch_calloc( 1, 
 			sizeof( backsql_at_map_rec ) );
 	at_map->ad = slap_schema.si_ad_ref;
-	ber_dupbv( &at_map->name, &at_map->ad->ad_cname );
-	at_map->sel_expr = ch_strdup( "ldap_referrals.url" );
-	at_map->from_tbls = ch_strdup( "ldap_referrals,ldap_entries" );
-	len = strlen( at_map->from_tbls );
-	backsql_merge_from_clause( &at_map->from_tbls, &len,oc_map->keytbl );
+	ber_str2bv( "ldap_referrals.url", 0, 1, &at_map->sel_expr );
+	ber_str2bv( "ldap_referrals,ldap_entries", 0, 1, &at_map->from_tbls );
+	len = at_map->from_tbls.bv_len + 1;
+	backsql_merge_from_clause( &at_map->from_tbls, &len, &oc_map->keytbl );
 
 	len = 0;
-	bv.bv_val = NULL;
-	bv.bv_len = 0;
-	backsql_strcat( &bv, &len,
-			"ldap_entries.id=ldap_referrals.entry_id "
-			"and ldap_entries.keyval=",
-			oc_map->keytbl, ".", oc_map->keycol,
-			" and ldap_entries.oc_map_id=", s, NULL );
-	at_map->join_where = bv.bv_val;
+	at_map->join_where.bv_val = NULL;
+	at_map->join_where.bv_len = 0;
+	backsql_strfcat( &at_map->join_where, &len, "lbcbll",
+			(ber_len_t)sizeof( "ldap_entries.id=ldap_referrals.entry_id and ldap_entries.keyval=" ) - 1,
+				"ldap_entries.id=ldap_referrals.entry_id and ldap_entries.keyval=",
+			&oc_map->keytbl, 
+			'.', 
+			&oc_map->keycol,
+			(ber_len_t)sizeof( " and ldap_entries.oc_map_id=" ) - 1, 
+				" and ldap_entries.oc_map_id=", 
+			slen, s );
 
 	at_map->add_proc = NULL;
 	at_map->delete_proc = NULL;
@@ -166,23 +181,23 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh )
 	unsigned long		oc_id;
 	backsql_oc_map_rec	*oc_map;
 	backsql_at_map_rec	*at_map;
-	char			*tmps;
-	int			tmpslen;
 
 	Debug( LDAP_DEBUG_TRACE, "==>load_schema_map()\n", 0, 0, 0 );
 
 	/* 
 	 * TimesTen : See if the ldap_entries.dn_ru field exists in the schema
 	 */
-	if ( si->has_ldapinfo_dn_ru == -1 ) {
+	if ( !BACKSQL_DONTCHECK_LDAPINFO_DN_RU( si ) ) {
 		rc = backsql_Prepare( dbh, &oc_sth, 
 				backsql_check_dn_ru_query, 0 );
 		if ( rc == SQL_SUCCESS ) {
-			si->has_ldapinfo_dn_ru = 1;  /* Yes, the field exists */
+			/* Yes, the field exists */
+			si->bsql_flags |= BSQLF_HAS_LDAPINFO_DN_RU;
    			Debug( LDAP_DEBUG_TRACE, "ldapinfo.dn_ru field exists "
 				"in the schema\n", 0, 0, 0 );
 		} else {
-			si->has_ldapinfo_dn_ru = 0;  /* No such field exists */
+			/* No such field exists */
+			si->bsql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU;
 		}
 
 		SQLFreeStmt( oc_sth, SQL_DROP );
@@ -228,10 +243,12 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh )
 	backsql_BindRowAsStrings( oc_sth, &oc_row );
 	rc = SQLFetch( oc_sth );
 	for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( oc_sth ) ) {
+		int	colnum;
+
 		oc_map = (backsql_oc_map_rec *)ch_calloc( 1,
 				sizeof( backsql_oc_map_rec ) );
 
-		oc_map->id = atoi( oc_row.cols[ 0 ] );
+		oc_map->id = strtol( oc_row.cols[ 0 ], NULL, 0 );
 
 		ber_str2bv( oc_row.cols[ 1 ], 0, 1, &oc_map->name );
 		oc_map->oc = oc_bvfind( &oc_map->name );
@@ -242,13 +259,21 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh )
 			return LDAP_OTHER;	/* undefined objectClass ? */
 		}
 		
-		oc_map->keytbl = ch_strdup( oc_row.cols[ 2 ] );
-		oc_map->keycol = ch_strdup( oc_row.cols[ 3 ] );
+		ber_str2bv( oc_row.cols[ 2 ], 0, 1, &oc_map->keytbl );
+		ber_str2bv( oc_row.cols[ 3 ], 0, 1, &oc_map->keycol );
 		oc_map->create_proc = ( oc_row.is_null[ 4 ] < 0 ) ? NULL 
 			: ch_strdup( oc_row.cols[ 4 ] );
-		oc_map->delete_proc = ( oc_row.is_null[ 5 ] < 0 ) ? NULL 
-			: ch_strdup( oc_row.cols[ 5 ] );
-		oc_map->expect_return = atoi( oc_row.cols[ 6 ] );
+
+		colnum = 5;
+		if ( BACKSQL_CREATE_NEEDS_SELECT( si ) ) {
+			colnum = 6;
+			oc_map->create_keyval = ( oc_row.is_null[ 5 ] < 0 ) 
+				? NULL : ch_strdup( oc_row.cols[ 5 ] );
+		}
+		oc_map->delete_proc = ( oc_row.is_null[ colnum ] < 0 ) ? NULL 
+			: ch_strdup( oc_row.cols[ colnum ] );
+		oc_map->expect_return = strtol( oc_row.cols[ colnum + 1 ], 
+				NULL, 0 );
 
 		/*
 		 * FIXME: first attempt to check for offending
@@ -263,17 +288,22 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh )
 		oc_id = oc_map->id;
 		Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
 			"objectClass '%s': keytbl='%s' keycol='%s'\n",
-			oc_map->name.bv_val, oc_map->keytbl, oc_map->keycol );
+			oc_map->name.bv_val, 
+			oc_map->keytbl.bv_val, oc_map->keycol.bv_val );
 		if ( oc_map->create_proc ) {
 			Debug( LDAP_DEBUG_TRACE, "create_proc='%s'\n",
 				oc_map->create_proc, 0, 0 );
 		}
+		if ( oc_map->create_keyval ) {
+			Debug( LDAP_DEBUG_TRACE, "create_keyval='%s'\n",
+				oc_map->create_keyval, 0, 0 );
+		}
 		if ( oc_map->delete_proc ) {
 			Debug( LDAP_DEBUG_TRACE, "delete_proc='%s'\n", 
 				oc_map->delete_proc, 0, 0 );
 		}
 		Debug( LDAP_DEBUG_TRACE, "expect_return: "
-			"add=%s, del=%s; attributes:\n",
+			"add=%d, del=%d; attributes:\n",
 			BACKSQL_IS_ADD( oc_map->expect_return ), 
 			BACKSQL_IS_DEL( oc_map->expect_return ), 0 );
 
@@ -293,6 +323,8 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh )
 		rc = SQLFetch( at_sth );
 		for ( ; BACKSQL_SUCCESS(rc); rc = SQLFetch( at_sth ) ) {
 			const char	*text = NULL;
+			struct berval	bv;
+			ber_len_t	tmpslen;
 
 			Debug( LDAP_DEBUG_TRACE, "********'%s'\n",
 				at_row.cols[ 0 ], 0, 0 );
@@ -310,33 +342,44 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh )
 					at_row.cols[ 8 ], 0, 0 );
 			at_map = (backsql_at_map_rec *)ch_calloc( 1,
 					sizeof( backsql_at_map_rec ) );
-			ber_str2bv( at_row.cols[ 0 ], 0, 1, &at_map->name );
-			rc = slap_bv2ad( &at_map->name, &at_map->ad, &text );
+			rc = slap_str2ad( at_row.cols[ 0 ], 
+					&at_map->ad, &text );
 			if ( rc != LDAP_SUCCESS ) {
 				Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
 					"attribute '%s' for objectClass '%s' "
 					"is not defined in schema: %s\n", 
-					at_map->name.bv_val, 
+					at_map->ad->ad_cname.bv_val, 
 					oc_map->name.bv_val, text );
 				return LDAP_CONSTRAINT_VIOLATION;
 			}
 				
-			at_map->sel_expr = ch_strdup( at_row.cols[ 1 ] );
-			at_map->sel_expr_u = ( at_row.is_null[ 8 ] < 0 ) ? NULL
-				: ch_strdup( at_row.cols[ 8 ] );
-			tmps = NULL;
+			ber_str2bv( at_row.cols[ 1 ], 0, 1, &at_map->sel_expr );
+			if ( at_row.is_null[ 8 ] < 0 ) {
+				at_map->sel_expr_u.bv_val = NULL;
+				at_map->sel_expr_u.bv_len = 0;
+			} else {
+				ber_str2bv( at_row.cols[ 8 ], 0, 1, 
+						&at_map->sel_expr_u );
+			}
 			tmpslen = 0;
-			backsql_merge_from_clause( &tmps, &tmpslen,
-					at_row.cols[ 2 ] );
-			at_map->from_tbls = tmps;
-			at_map->join_where = ( at_row.is_null[ 3 ] < 0 ) ? NULL 
-				: ch_strdup( at_row.cols[ 3 ] );
+			ber_str2bv( at_row.cols[ 2 ], 0, 0, &bv );
+			backsql_merge_from_clause( &at_map->from_tbls, 
+					&tmpslen, &bv );
+			if ( at_row.is_null[ 3 ] < 0 ) {
+				at_map->join_where.bv_val = NULL;
+				at_map->join_where.bv_len = 0;
+			} else {
+				ber_str2bv( at_row.cols[ 3 ], 0, 1, 
+						&at_map->join_where );
+			}
 			at_map->add_proc = ( at_row.is_null[ 4 ] < 0 ) ? NULL
 				: ch_strdup( at_row.cols[4] );
 			at_map->delete_proc = ( at_row.is_null[ 5 ] < 0 ) ? NULL
 				: ch_strdup( at_row.cols[ 5 ] );
-			at_map->param_order = atoi( at_row.cols[ 6 ] );
-			at_map->expect_return = atoi( at_row.cols[ 7 ] );
+			at_map->param_order = strtol( at_row.cols[ 6 ], 
+					NULL, 0 );
+			at_map->expect_return = strtol( at_row.cols[ 7 ],
+					NULL, 0 );
 			backsql_make_attr_query( oc_map, at_map );
 			Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
 				"preconstructed query '%s'\n",
@@ -350,7 +393,7 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh )
 	backsql_FreeRow( &oc_row );
 	SQLFreeStmt( at_sth, SQL_DROP );
 	SQLFreeStmt( oc_sth, SQL_DROP );
-	si->schema_loaded = 1;
+	si->bsql_flags |= BSQLF_SCHEMA_LOADED;
 	Debug( LDAP_DEBUG_TRACE, "<==load_schema_map()\n", 0, 0, 0 );
 	return LDAP_SUCCESS;
 }
@@ -360,16 +403,16 @@ backsql_oc2oc( backsql_info *si, ObjectClass *oc )
 {
 	backsql_oc_map_rec	tmp, *res;
 
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_oc2oc(): "
 		"searching for objectclass with name='%s'\n",
 		objclass, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	tmp.oc = oc;
 	res = (backsql_oc_map_rec *)avl_find( si->oc_by_oc, &tmp,
 			(AVL_CMP)backsql_cmp_oc );
-#if 0
+#ifdef BACKSQL_TRACE
 	if ( res != NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): "
 			"found name='%s', id=%d\n", res->name, res->id, 0 );
@@ -377,7 +420,7 @@ backsql_oc2oc( backsql_info *si, ObjectClass *oc )
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): "
 			"not found\n", 0, 0, 0 );
 	}
-#endif
+#endif /* BACKSQL_TRACE */
  
 	return res;
 }
@@ -390,11 +433,11 @@ backsql_name2oc( backsql_info *si, struct berval *oc_name )
 {
 	backsql_oc_map_rec	tmp, *res;
 
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==>oc_with_name(): "
 		"searching for objectclass with name='%s'\n",
 		objclass, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	tmp.oc = oc_bvfind( oc_name );
 	if ( tmp.oc == NULL ) {
@@ -403,7 +446,7 @@ backsql_name2oc( backsql_info *si, struct berval *oc_name )
 
 	res = (backsql_oc_map_rec *)avl_find( si->oc_by_oc, &tmp,
 			(AVL_CMP)backsql_cmp_oc );
-#if 0
+#ifdef BACKSQL_TRACE
 	if ( res != NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
 			"found name='%s', id=%d\n", res->name, res->id, 0 );
@@ -411,7 +454,7 @@ backsql_name2oc( backsql_info *si, struct berval *oc_name )
 		Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
 			"not found\n", 0, 0, 0 );
 	}
-#endif
+#endif /* BACKSQL_TRACE */
  
 	return res;
 }
@@ -421,16 +464,16 @@ backsql_id2oc( backsql_info *si, unsigned long id )
 {
 	backsql_oc_map_rec	tmp, *res;
  
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==>oc_with_id(): "
 		"searching for objectclass with id='%d'\n", id, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	tmp.id = id;
 	res = (backsql_oc_map_rec *)avl_find( si->oc_by_id, &tmp,
 			(AVL_CMP)backsql_cmp_oc_id );
 
-#if 0
+#ifdef BACKSQL_TRACE
 	if ( res != NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
 			"found name='%s', id=%d\n", res->name, res->id, 0 );
@@ -438,7 +481,7 @@ backsql_id2oc( backsql_info *si, unsigned long id )
 		Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
 			"not found\n", 0, 0, 0 );
 	}
-#endif
+#endif /* BACKSQL_TRACE */
 	
 	return res;
 }
@@ -448,25 +491,26 @@ backsql_ad2at( backsql_oc_map_rec* objclass, AttributeDescription *ad )
 {
 	backsql_at_map_rec	tmp, *res;
  
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_ad2at(): "
 		"searching for attribute '%s' for objectclass '%s'\n",
 		attr, objclass->name, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
+
 	tmp.ad = ad;
 	res = (backsql_at_map_rec *)avl_find( objclass->attrs, &tmp,
 			(AVL_CMP)backsql_cmp_attr );
 
-#if 0
+#ifdef BACKSQL_TRACE
 	if ( res != NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): "
 			"found name='%s', sel_expr='%s'\n",
-			res->name, res->sel_expr, 0 );
+			res->name, res->sel_expr.bv_val, 0 );
 	} else {
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): "
 			"not found\n", 0, 0, 0 );
 	}
-#endif
+#endif /* BACKSQL_TRACE */
 
 	return res;
 }
@@ -480,11 +524,11 @@ backsql_name2at( backsql_oc_map_rec* objclass, struct berval *attr )
 	backsql_at_map_rec	tmp, *res;
 	const char		*text = NULL;
  
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_name2at(): "
 		"searching for attribute '%s' for objectclass '%s'\n",
 		attr, objclass->name, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	if ( slap_bv2ad( attr, &tmp.ad, &text ) != LDAP_SUCCESS ) {
 		return NULL;
@@ -493,16 +537,16 @@ backsql_name2at( backsql_oc_map_rec* objclass, struct berval *attr )
 	res = (backsql_at_map_rec *)avl_find( objclass->attrs, &tmp,
 			(AVL_CMP)backsql_cmp_attr );
 
-#if 0
+#ifdef BACKSQL_TRACE
 	if ( res != NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_name2at(): "
 			"found name='%s', sel_expr='%s'\n",
-			res->name, res->sel_expr, 0 );
+			res->name, res->sel_expr.bv_val, 0 );
 	} else {
 		Debug( LDAP_DEBUG_TRACE, "<==backsql_name2at(): "
 			"not found\n", 0, 0, 0 );
 	}
-#endif
+#endif /* BACKSQL_TRACE */
 
 	return res;
 }
@@ -511,14 +555,13 @@ static void
 backsql_free_attr( backsql_at_map_rec *at )
 {
 	Debug( LDAP_DEBUG_TRACE, "==>free_attr(): '%s'\n", 
-			at->name.bv_val, 0, 0 );
-	ch_free( at->name.bv_val );
-	ch_free( at->sel_expr );
-	if ( at->from_tbls != NULL ) {
-		ch_free( at->from_tbls );
+			at->ad->ad_cname.bv_val, 0, 0 );
+	ch_free( at->sel_expr.bv_val );
+	if ( at->from_tbls.bv_val != NULL ) {
+		ch_free( at->from_tbls.bv_val );
 	}
-	if ( at->join_where != NULL ) {
-		ch_free( at->join_where );
+	if ( at->join_where.bv_val != NULL ) {
+		ch_free( at->join_where.bv_val );
 	}
 	if ( at->add_proc != NULL ) {
 		ch_free( at->add_proc );
@@ -531,8 +574,8 @@ backsql_free_attr( backsql_at_map_rec *at )
 	}
 
 	/* TimesTen */
-	if ( at->sel_expr_u ) {
-		ch_free( at->sel_expr_u );
+	if ( at->sel_expr_u.bv_val ) {
+		ch_free( at->sel_expr_u.bv_val );
 	}
 	
 	ch_free( at );
@@ -547,11 +590,14 @@ backsql_free_oc( backsql_oc_map_rec *oc )
 			oc->name.bv_val, 0, 0 );
 	avl_free( oc->attrs, (AVL_FREE)backsql_free_attr );
 	ch_free( oc->name.bv_val );
-	ch_free( oc->keytbl );
-	ch_free( oc->keycol );
+	ch_free( oc->keytbl.bv_val );
+	ch_free( oc->keycol.bv_val );
 	if ( oc->create_proc != NULL ) {
 		ch_free( oc->create_proc );
 	}
+	if ( oc->create_keyval != NULL ) {
+		ch_free( oc->create_keyval );
+	}
 	if ( oc->delete_proc != NULL ) {
 		ch_free( oc->delete_proc );
 	}
diff --git a/servers/slapd/back-sql/schema-map.h b/servers/slapd/back-sql/schema-map.h
index 27f07278ef..7c7f0cf39e 100644
--- a/servers/slapd/back-sql/schema-map.h
+++ b/servers/slapd/back-sql/schema-map.h
@@ -10,14 +10,23 @@
  *	 in file LICENSE in the top-level directory of the distribution.
  */
 
-
 typedef struct {
+	/*
+	 * FIXME: we explicitly keep the objectClass name because
+	 * the ObjectClass structure does not use bervals (yet?)
+	 */
 	struct berval	name;
+	/*
+	 * Structure of corresponding LDAP objectClass definition
+	 */
 	ObjectClass	*oc;
-	char		*keytbl;
-	char		*keycol;
+	struct berval	keytbl;
+	struct berval	keycol;
 	/* expected to return keyval of newly created entry */
 	char		*create_proc;
+	/* in case create_proc does not return the keyval of the newly
+	 * created row */
+	char		*create_keyval;
 	/* supposed to expect keyval as parameter and delete 
 	 * all the attributes as well */
 	char		*delete_proc;
@@ -29,12 +38,11 @@ typedef struct {
 } backsql_oc_map_rec;
 
 typedef struct {
-	/* literal name of corresponding LDAP attribute type */
-	struct berval	name;
+	/* Description of corresponding LDAP attribute type */
 	AttributeDescription	*ad;
-	char		*from_tbls;
-	char		*join_where;
-	char		*sel_expr;
+	struct berval	from_tbls;
+	struct berval	join_where;
+	struct berval	sel_expr;
 	/* supposed to expect 2 binded values: entry keyval 
 	 * and attr. value to add, like "add_name(?,?,?)" */
 	char		*add_proc;
@@ -45,7 +53,7 @@ typedef struct {
 	 * is preconstructed from parts on schemamap load time */
 	char		*query;
 	/* following flags are bitmasks (first bit used for add_proc, 
-	 * second - for modify, third - for delete_proc) */
+	 * second - for delete_proc) */
 	/* order of parameters for procedures above; 
 	 * 1 means "data then keyval", 0 means "keyval then data" */
 	int 		param_order;
@@ -54,7 +62,7 @@ typedef struct {
 	 * for return code) */
 	int 		expect_return;
 	/* TimesTen */
-	char		*sel_expr_u;
+	struct berval	sel_expr_u;
 } backsql_at_map_rec;
 
 /* defines to support bitmasks above */
diff --git a/servers/slapd/back-sql/search.c b/servers/slapd/back-sql/search.c
index b8f5e143b0..ffd1856fd8 100644
--- a/servers/slapd/back-sql/search.c
+++ b/servers/slapd/back-sql/search.c
@@ -23,14 +23,6 @@
 #include "entry-id.h"
 #include "util.h"
 
-static struct berval AllUser = BER_BVC( LDAP_ALL_USER_ATTRIBUTES );
-static struct berval AllOper = BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES );
-static struct berval NoAttrs = BER_BVC( LDAP_NO_ATTRS );
-
-#if 0
-static struct berval NoAttrs = BER_BVC( LDAP_NO_ATTRS );
-#endif
-
 static int
 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
 {
@@ -102,6 +94,7 @@ backsql_init_search(
 	bsi->be = be;
 	bsi->conn = conn;
 	bsi->op = op;
+	bsi->attr_flags = 0;
 
 	/*
 	 * FIXME: need to discover how to deal with 1.1 (NoAttrs)
@@ -123,8 +116,11 @@ backsql_init_search(
 			/*
 			 * ignore "+"
 			 */
-			if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 
-					|| BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
+			if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
+				continue;
+
+			} else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
+				bsi->attr_flags |= BSQL_SF_ALL_OPER;
 				continue;
 			}
 
@@ -161,8 +157,8 @@ backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
 	if ( !f ) {
 		return 0;
 	}
-	
-	backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, "(", NULL );
+
+	backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", '(' /* ) */  );
 
 	while ( 1 ) {
 		res = backsql_process_filter( bsi, f );
@@ -181,18 +177,20 @@ backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
 
 		switch ( op ) {
 		case LDAP_FILTER_AND:
-			backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-					" AND ", NULL );
+			backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
+					(ber_len_t)sizeof( " AND " ) - 1, 
+						" AND " );
 			break;
 
 		case LDAP_FILTER_OR:
-			backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-					" OR ", NULL );
+			backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
+					(ber_len_t)sizeof( " OR " ) - 1,
+						" OR " );
 			break;
 		}
 	}
 
-	backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, /* ( */ ")", NULL );
+	backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", /* ( */ ')' );
 
 	return 1;
 }
@@ -209,63 +207,75 @@ backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f )
 
 	at = backsql_ad2at( bsi->oc, f->f_sub_desc );
 
+	assert( at );
+
 	/*
 	 * When dealing with case-sensitive strings 
 	 * we may omit normalization; however, normalized
 	 * SQL filters are more liberal.
 	 */
 
-	backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, "(" /* ) */ , NULL );
+	backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", '(' /* ) */  );
 
 	/* TimesTen */
-	Debug( LDAP_DEBUG_TRACE, "expr: '%s' '%s'\n", at->sel_expr,
-		at->sel_expr_u ? at->sel_expr_u : "<NULL>", 0 );
-	if ( bsi->bi->upper_func ) {
+	Debug( LDAP_DEBUG_TRACE, "expr: '%s' '%s'\n", at->sel_expr.bv_val,
+		at->sel_expr_u.bv_val ? at->sel_expr_u.bv_val : "<NULL>", 0 );
+	if ( bsi->bi->upper_func.bv_val ) {
 		/*
 		 * If a pre-upper-cased version of the column exists, use it
 		 */
-		if ( at->sel_expr_u ) {
-			backsql_strcat( &bsi->flt_where, &bsi->fwhere_len,
-					at->sel_expr_u, " LIKE '", NULL);
+		if ( at->sel_expr_u.bv_val ) {
+			backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, 
+					"bl",
+					&at->sel_expr_u,
+					(ber_len_t)sizeof( " LIKE '" ) - 1,
+						" LIKE '" );
    		} else {
-			backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-					bsi->bi->upper_func,
-					"(", at->sel_expr, ")", 
-					" LIKE '", NULL );
+			backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
+					"bcbcl",
+					&bsi->bi->upper_func,
+					'(',
+					&at->sel_expr,
+					')', 
+					(ber_len_t)sizeof( " LIKE '" ) - 1,
+						" LIKE '" );
 		}
 	} else {
-		backsql_strcat( &bsi->flt_where, &bsi->fwhere_len,
-				at->sel_expr, " LIKE '", NULL );
+		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "bl",
+				&at->sel_expr,
+				(ber_len_t)sizeof( " LIKE '" ) - 1, " LIKE '" );
 	}
  
 	if ( f->f_sub_initial.bv_val != NULL ) {
 		size_t	start;
 
 		start = bsi->flt_where.bv_len;
-		backsql_strcat( &bsi->flt_where, &bsi->fwhere_len,
-				f->f_sub_initial.bv_val, NULL );
-		if ( bsi->bi->upper_func ) {
+		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "b",
+				&f->f_sub_initial );
+		if ( bsi->bi->upper_func.bv_val ) {
 			ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
 		}
 	}
 
-	backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, "%", NULL );
+	backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", '%' );
 
 	if ( f->f_sub_any != NULL ) {
 		for ( i = 0; f->f_sub_any[ i ].bv_val != NULL; i++ ) {
 			size_t	start;
 
-#if 0
+#ifdef BACKSQL_TRACE
 			Debug( LDAP_DEBUG_TRACE, 
 				"==>backsql_process_sub_filter(): "
 				"sub_any='%s'\n", f->f_sub_any[ i ].bv_val,
 				0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 			start = bsi->flt_where.bv_len;
-			backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-					f->f_sub_any[ i ].bv_val, "%", NULL );
-			if ( bsi->bi->upper_func) {
+			backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
+					"bc",
+					&f->f_sub_any[ i ],
+					'%' );
+			if ( bsi->bi->upper_func.bv_val ) {
 				/*
 				 * Note: toupper('%') = '%'
 				 */
@@ -277,15 +287,16 @@ backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f )
 			size_t	start;
 
 			start = bsi->flt_where.bv_len;
-    			backsql_strcat( &bsi->flt_where, &bsi->fwhere_len,
-					f->f_sub_final.bv_val, NULL);
-  			if ( bsi->bi->upper_func ) {
+    			backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "b",
+					&f->f_sub_final );
+  			if ( bsi->bi->upper_func.bv_val ) {
 				ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
 			}
 		}
 	}
 
-	backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, /* ( */ "')", NULL );
+	backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l", 
+			(ber_len_t)sizeof( /* (' */ "')" ) - 1, /* ( */ "')" );
  
 	return 1;
 }
@@ -294,10 +305,12 @@ int
 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
 {
 	backsql_at_map_rec	*at;
-	backsql_at_map_rec 	oc_attr = { BER_BVC("objectClass"),
-		slap_schema.si_ad_objectClass, "", "", NULL, NULL, NULL, NULL };
+	backsql_at_map_rec 	oc_attr = {
+		slap_schema.si_ad_objectClass, BER_BVC(""), BER_BVC(""), 
+		{ 0, NULL }, NULL, NULL, NULL };
 	AttributeDescription	*ad = NULL;
-	int 			done = 0, len = 0;
+	int 			done = 0;
+	ber_len_t		len = 0;
 	/* TimesTen */
 	int			rc = 0;
 
@@ -320,10 +333,12 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f )
 		break;
 
 	case LDAP_FILTER_NOT:
-		backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-				"NOT (", NULL );
+		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
+				(ber_len_t)sizeof( "NOT (" /* ) */ ) - 1,
+					"NOT (" /* ) */ );
 		rc = backsql_process_filter( bsi, f->f_not );
-		backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, ")", NULL );
+		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c",
+				/* ( */ ')' );
 		done = 1;
 		break;
 
@@ -345,50 +360,52 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f )
 		goto done;
 	}
 
-	if ( strcasecmp( ad->ad_cname.bv_val, "objectclass" ) ) {
+	if ( ad != slap_schema.si_ad_objectClass ) {
 		at = backsql_ad2at( bsi->oc, ad );
 
 	} else {
-		struct berval	bv;
-		
 		at = &oc_attr;
-
-		/*
-		 * FIXME: use berval for at->sel_expr ?
-		 */
-		bv.bv_val = at->sel_expr;
-		bv.bv_len = at->sel_expr ? strlen( at->sel_expr ) : 0;
-		backsql_strcat( &bv, &len, "'", bsi->oc->name.bv_val, 
-				"'", NULL );
-		at->sel_expr = bv.bv_val;
+		backsql_strfcat( &at->sel_expr, &len, "cbc",
+				'\'', 
+				&bsi->oc->name, 
+				'\'' );
 	}
+
 	if ( at == NULL ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
 			"attribute '%s' is not defined for objectclass '%s'\n",
 			ad->ad_cname.bv_val, bsi->oc->name.bv_val, 0 );
-		backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-				" 1=0 ", NULL );
+		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
+				(ber_len_t)sizeof( " 1=0 " ) - 1, " 1=0 " );
 		goto impossible;
 	}
 
-	backsql_merge_from_clause( &bsi->from.bv_val, &bsi->from_len, 
-			at->from_tbls );
+	backsql_merge_from_clause( &bsi->from, &bsi->from_len, 
+			&at->from_tbls );
 	/*
 	 * need to add this attribute to list of attrs to load,
 	 * so that we could do test_filter() later
 	 */
 	backsql_attrlist_add( bsi, ad );
 
-	if ( at->join_where != NULL && strstr( bsi->join_where.bv_val, at->join_where ) == NULL ) {
-	       	backsql_strcat( &bsi->join_where, &bsi->jwhere_len, 
-				" AND ", at->join_where, NULL );
+	if ( at->join_where.bv_val != NULL 
+			&& strstr( bsi->join_where.bv_val, at->join_where.bv_val ) == NULL ) {
+	       	backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, "lb",
+				(ber_len_t)sizeof( " AND " ) - 1, " AND ",
+				&at->join_where );
 	}
 
-#if 0	
+#if 0
+	/*
+	 * FIXME: this is not required any more; however, note that
+	 * attribute name syntax might collide with SQL legal aliases
+	 */
 	if ( at != &oc_attr ) {
-		backsql_strcat( &bsi->sel, &bsi->sel_len,
-				",", at->sel_expr, " AS ", 
-				at->name.bv_val, NULL );
+		backsql_strfcat( &bsi->sel, &bsi->sel_len, "cblb",
+				',',
+				&at->sel_expr,
+				(ber_len_t)sizeof( " AS " ) - 1, " AS ", 
+				&at->name );
  	}
 #endif
 
@@ -400,49 +417,72 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f )
 		 * upper_func stuff is made for Oracle, where UPPER is
 		 * safely applicable to NUMBER etc.
 		 */
-		if ( bsi->bi->upper_func ) {
+		if ( bsi->bi->upper_func.bv_val ) {
 			size_t	start;
 
-			if ( at->sel_expr_u ) {
-				backsql_strcat( &bsi->flt_where,
-						&bsi->fwhere_len, "(",
-						at->sel_expr_u, "='", NULL );
+			if ( at->sel_expr_u.bv_val ) {
+				backsql_strfcat( &bsi->flt_where,
+						&bsi->fwhere_len, "cbl",
+						'(',
+						&at->sel_expr_u, 
+						(ber_len_t)sizeof( "='" ) - 1,
+							"='" );
 			} else {
-				backsql_strcat( &bsi->flt_where,
-						&bsi->fwhere_len, "(",
-						bsi->bi->upper_func, "(",
-						at->sel_expr, ")='", NULL );
+				backsql_strfcat( &bsi->flt_where,
+						&bsi->fwhere_len, "cbcbl",
+						'(' /* ) */ ,
+						&bsi->bi->upper_func,
+						'(' /* ) */ ,
+						&at->sel_expr,
+						(ber_len_t)sizeof( /* ( */ ")='" ) - 1,
+							/* ( */ ")='" );
 			}
 
 			start = bsi->flt_where.bv_len;
 
-			backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-					f->f_av_value.bv_val, "')", NULL );
+			backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
+					"bl",
+					&f->f_av_value, 
+					(ber_len_t)sizeof( /* (' */ "')" ) - 1,
+						/* (' */ "')" );
 
 			ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
 
 		} else {
-			backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-					"(", at->sel_expr, "='",
-					f->f_av_value.bv_val, "')", NULL );
+			backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
+					"cblbl",
+					'(',
+					&at->sel_expr,
+					(ber_len_t)sizeof( "='" ) - 1, "='",
+					&f->f_av_value,
+					(ber_len_t)sizeof( /* (' */ "')" ) - 1,
+						/* (' */ "')" );
 		}
 		break;
 
 	case LDAP_FILTER_GE:
-		backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-				"(", at->sel_expr, ">=", 
-				f->f_av_value.bv_val, ")", NULL );
+		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "cblbc",
+				'(' /* ) */ ,
+				&at->sel_expr,
+				(ber_len_t)sizeof( ">=" ) - 1, ">=", 
+				&f->f_av_value,
+				/* ( */ ')' );
 		break;
 		
 	case LDAP_FILTER_LE:
-		backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-				"(", at->sel_expr, "<=", 
-				f->f_av_value.bv_val, ")", NULL );
+		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "cblbc",
+				'(' /* ) */ ,
+				&at->sel_expr,
+				(ber_len_t)sizeof( "<=" ) - 1, "<=", 
+				&f->f_av_value,
+				/* ( */ ')' );
 		break;
 
 	case LDAP_FILTER_PRESENT:
-		backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
-				"NOT (", at->sel_expr, " IS NULL)", NULL );
+		backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "lbl",
+				(ber_len_t)sizeof( "NOT (" ) - 1, "NOT (", 
+				&at->sel_expr, 
+				(ber_len_t)sizeof( " IS NULL)" ) - 1, " IS NULL)" );
 		break;
 
 	case LDAP_FILTER_SUBSTRINGS:
@@ -451,16 +491,16 @@ backsql_process_filter( backsql_srch_info *bsi, Filter *f )
 	}
 
 done:
-	if ( oc_attr.sel_expr != NULL ) {
-		free( oc_attr.sel_expr );
+	if ( oc_attr.sel_expr.bv_val != NULL ) {
+		free( oc_attr.sel_expr.bv_val );
 	}
 	
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter()\n", 0, 0, 0 );
 	return 1;
 
 impossible:
-	if ( oc_attr.sel_expr != NULL ) {
-		free( oc_attr.sel_expr );
+	if ( oc_attr.sel_expr.bv_val != NULL ) {
+		free( oc_attr.sel_expr.bv_val );
 	}
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter() returns -1\n",
 			0, 0, 0 );
@@ -471,7 +511,7 @@ static int
 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
 {
 	backsql_info	*bi = (backsql_info *)bsi->be->be_private;
-	int		q_len = 0;
+	ber_len_t	q_len = 0;
 	int		rc;
 
 	assert( query );
@@ -491,55 +531,87 @@ backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
 	bsi->flt_where.bv_val = NULL;
 	bsi->flt_where.bv_len = 0;
 	bsi->fwhere_len = 0;
+
 #if 0
+	/*
+	 * FIXME: this query has been split in case a string cast function
+	 * is defined; more sophisticated (pattern based) function should
+	 * be used
+	 */
 	backsql_strcat( &bsi->sel, &bsi->sel_len,
 			"SELECT DISTINCT ldap_entries.id,", 
-			bsi->oc->keytbl, ".", bsi->oc->keycol,
+			bsi->oc->keytbl.bv_val, ".", bsi->oc->keycol.bv_val,
 			",'", bsi->oc->name.bv_val, "' AS objectClass",
 			",ldap_entries.dn AS dn", NULL );
 #endif
-	backsql_strcat( &bsi->sel, &bsi->sel_len,
-			"SELECT DISTINCT ldap_entries.id,", 
-			bsi->oc->keytbl, ".", bsi->oc->keycol, ",", NULL );
-	if ( bi->strcast_func ) {
-		backsql_strcat( &bsi->sel, &bsi->sel_len,
-				bi->strcast_func, 
-				"('", bsi->oc->name.bv_val, "')", NULL );
+
+	backsql_strfcat( &bsi->sel, &bsi->sel_len, "lbcbc",
+			(ber_len_t)sizeof( "SELECT DISTINCT ldap_entries.id," ) - 1,
+				"SELECT DISTINCT ldap_entries.id,", 
+			&bsi->oc->keytbl, 
+			'.', 
+			&bsi->oc->keycol, 
+			',' );
+
+	if ( bi->strcast_func.bv_val ) {
+		backsql_strfcat( &bsi->sel, &bsi->sel_len, "blbl",
+				&bi->strcast_func, 
+				(ber_len_t)sizeof( "('" /* ') */ ) - 1,
+					"('" /* ') */ ,
+				&bsi->oc->name,
+				(ber_len_t)sizeof( /* (' */ "')" ) - 1,
+					/* (' */ "')" );
 	} else {
-		backsql_strcat( &bsi->sel, &bsi->sel_len,
-				"'", bsi->oc->name.bv_val, "'", NULL );
+		backsql_strfcat( &bsi->sel, &bsi->sel_len, "cbc",
+				'\'',
+				&bsi->oc->name,
+				'\'' );
 	}
-	backsql_strcat( &bsi->sel, &bsi->sel_len,
-			" AS objectClass,ldap_entries.dn AS dn", NULL );
-
-	backsql_strcat( &bsi->from, &bsi->from_len,
-			" FROM ldap_entries,", bsi->oc->keytbl, NULL );
-	backsql_strcat( &bsi->join_where, &bsi->jwhere_len,
-			" WHERE ", bsi->oc->keytbl, ".", bsi->oc->keycol,
-			"=ldap_entries.keyval AND ",
-			"ldap_entries.oc_map_id=? AND ", NULL );
+	backsql_strfcat( &bsi->sel, &bsi->sel_len, "l",
+			(ber_len_t)sizeof( " AS objectClass,ldap_entries.dn AS dn" ) - 1,
+			" AS objectClass,ldap_entries.dn AS dn" );
+
+	backsql_strfcat( &bsi->from, &bsi->from_len, "lb",
+			(ber_len_t)sizeof( " FROM ldap_entries," ) - 1,
+				" FROM ldap_entries,",
+			&bsi->oc->keytbl );
+
+	backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, "lbcbl",
+			(ber_len_t)sizeof( " WHERE " ) - 1, " WHERE ",
+			&bsi->oc->keytbl,
+			'.',
+			&bsi->oc->keycol,
+			(ber_len_t)sizeof( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ) - 1,
+				"=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
 
 	switch ( bsi->scope ) {
 	case LDAP_SCOPE_BASE:
-		if ( bsi->bi->upper_func ) {
-      			backsql_strcat( &bsi->join_where, &bsi->jwhere_len, 
-					bsi->bi->upper_func,
-					"(","ldap_entries.dn)=",
-					bsi->bi->upper_func, "(?)", NULL );
+		if ( bsi->bi->upper_func.bv_val ) {
+      			backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, 
+					"blbcb",
+					&bsi->bi->upper_func,
+					(ber_len_t)sizeof( "(ldap_entries.dn)=" ) - 1,
+						"(ldap_entries.dn)=",
+					&bsi->bi->upper_func_open,
+					'?', 
+					&bsi->bi->upper_func_close );
 		} else {
-			backsql_strcat( &bsi->join_where, &bsi->jwhere_len, 
-					"ldap_entries.dn=?", NULL );
+			backsql_strfcat( &bsi->join_where, &bsi->jwhere_len,
+					"l",
+					(ber_len_t)sizeof( "ldap_entries.dn=?" ) - 1,
+						"ldap_entries.dn=?" );
 		}
 		break;
 		
 	case LDAP_SCOPE_ONELEVEL:
-		backsql_strcat( &bsi->join_where, &bsi->jwhere_len, 
-				"ldap_entries.parent=?", NULL );
+		backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, "l",
+				(ber_len_t)sizeof( "ldap_entries.parent=?" ) - 1,
+					"ldap_entries.parent=?" );
 		break;
 
 	case LDAP_SCOPE_SUBTREE:
-		backsql_strcat( &bsi->join_where, &bsi->jwhere_len,
-				bsi->bi->subtree_cond, NULL );
+		backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, "b",
+				&bsi->bi->subtree_cond );
 		break;
 
 	default:
@@ -548,10 +620,12 @@ backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
 
 	rc = backsql_process_filter( bsi, bsi->filter );
 	if ( rc > 0 ) {
-		backsql_strcat( query, &q_len,
-				bsi->sel.bv_val, bsi->from.bv_val, 
-				bsi->join_where.bv_val,
-				" AND ", bsi->flt_where.bv_val, NULL );
+		backsql_strfcat( query, &q_len, "bbblb",
+				&bsi->sel,
+				&bsi->from, 
+				&bsi->join_where,
+				(ber_len_t)sizeof( " AND " ) - 1, " AND ",
+				&bsi->flt_where );
 
 	} else if ( rc < 0 ) {
 		/* 
@@ -590,17 +664,19 @@ backsql_oc_get_candidates( backsql_oc_map_rec *oc, backsql_srch_info *bsi )
 	RETCODE			rc;
 	backsql_entryID		base_id, *c_id;
 	int			res;
-#if 0
-	Entry			*e;
-#endif
 	BACKSQL_ROW_NTS		row;
 	int			i;
 	int			j;
-	/* TimesTen */
-	char			temp_base_dn[ BACKSQL_MAX_DN_LEN + 1 ];
  
 	Debug(	LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc='%s'\n",
 			oc->name.bv_val, 0, 0 );
+
+	if ( bsi->n_candidates == -1 ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+			"unchecked limit has been overcome\n", 0, 0, 0 );
+		return 1;
+	}
+	
 	bsi->oc = oc;
 	if ( backsql_srch_query( bsi, &query ) ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
@@ -640,7 +716,21 @@ backsql_oc_get_candidates( backsql_oc_map_rec *oc, backsql_srch_info *bsi )
 		}
 		break;
 
-	case LDAP_SCOPE_SUBTREE:
+	case LDAP_SCOPE_SUBTREE: {
+
+		/* 
+		 * + 1 because we need room for '%'; this makes a subtree
+		 * search for a DN BACKSQL_MAX_DN_LEN long legal 
+		 * if it returns that DN only
+		 */
+		char		temp_base_dn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
+
+		/*
+		 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
+		 * however this should be handled earlier
+		 */
+		assert( bsi->base_dn->bv_len <= BACKSQL_MAX_DN_LEN );
+			
 		/* 
 		 * Sets the parameters for the SQL built earlier
 		 * NOTE that all the databases could actually use 
@@ -654,7 +744,7 @@ backsql_oc_get_candidates( backsql_oc_map_rec *oc, backsql_srch_info *bsi )
 		 * If "dn" is being used, do a suffix search.
 		 * If "dn_ru" is being used, do a prefix search.
 		 */
-		if ( bsi->bi->has_ldapinfo_dn_ru ) {
+		if ( BACKSQL_HAS_LDAPINFO_DN_RU( bsi->bi ) ) {
 			temp_base_dn[ 0 ] = '\0';
 			for ( i = 0, j = bsi->base_dn->bv_len - 1;
 					j >= 0; i++, j--) {
@@ -684,6 +774,7 @@ backsql_oc_get_candidates( backsql_oc_map_rec *oc, backsql_srch_info *bsi )
 			return 1;
 		}
 		break;
+	}
 
  	case LDAP_SCOPE_ONELEVEL:
 		res = backsql_dn2id( bsi->bi, &base_id, 
@@ -719,41 +810,23 @@ backsql_oc_get_candidates( backsql_oc_map_rec *oc, backsql_srch_info *bsi )
 	backsql_BindRowAsStrings( sth, &row );
 	rc = SQLFetch( sth );
 	for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
-#if 0
-		e = (Entry *)ch_calloc( 1, sizeof( Entry ) ); 
-		for ( i = 1; i < row.ncols; i++ ) {
-			if ( row.is_null[ i ] > 0 ) {
-				struct berval	bv;
-
-				ber_str2bv( row.cols[ i ], 
-						row.col_prec[ i ], 0, &bv );
-
-				backsql_entry_addattr( e, 
-						&row.col_names[ i ], &bv );
-
-				Debug( LDAP_DEBUG_TRACE, "prec=%d\n", 
-						(int)row.col_prec[ i ], 0, 0 );
-			} else {
-				Debug( LDAP_DEBUG_TRACE, 
-					"NULL value in this row "
-					"for attribute '%s'\n", 
-					&row.col_names[ i ], 0, 0 );
-			}
-		}
-#endif
-
 		c_id = (backsql_entryID *)ch_calloc( 1, 
 				sizeof( backsql_entryID ) );
-		c_id->id = atoi( row.cols[ 0 ] );
-		c_id->keyval = atoi( row.cols[ 1 ] );
+		c_id->id = strtol( row.cols[ 0 ], NULL, 0 );
+		c_id->keyval = strtol( row.cols[ 1 ], NULL, 0 );
 		c_id->oc_id = bsi->oc->id;
 		ber_str2bv( row.cols[ 3 ], 0, 1, &c_id->dn );
 		c_id->next = bsi->id_list;
 		bsi->id_list = c_id;
-		bsi->n_candidates++;
+		bsi->n_candidates--;
+
 		Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
 			"added entry id=%ld, keyval=%ld dn='%s'\n",
 			c_id->id, c_id->keyval, row.cols[ 3 ] );
+
+		if ( bsi->n_candidates == -1 ) {
+			break;
+		}
 	}
 	backsql_FreeRow( &row );
 	SQLFreeStmt( sth, SQL_DROP );
@@ -799,6 +872,20 @@ backsql_search(
 		"attributes to load: %s\n",
 		deref, attrsonly, attrs == NULL ? "all" : "custom list" );
 
+	if ( nbase->bv_len > BACKSQL_MAX_DN_LEN ) {
+		Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+			"search base length (%ld) exceeds max length (%ld)\n", 
+			nbase->bv_len, BACKSQL_MAX_DN_LEN, 0 );
+		/*
+		 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
+		 * since it is impossible that such a long DN exists
+		 * in the backend
+		 */
+		send_ldap_result( conn, op, LDAP_ADMINLIMIT_EXCEEDED, 
+				"", NULL, NULL, NULL );
+		return 1;
+	}
+
 	sres = backsql_get_db_conn( be, conn, &dbh );
 	if ( sres != LDAP_SUCCESS ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
@@ -811,7 +898,7 @@ backsql_search(
 	}
 
 	/* TimesTen : Pass it along to the lower level routines */ 
-	srch_info.isTimesTen = bi->isTimesTen; 
+	srch_info.use_reverse_dn = BACKSQL_USE_REVERSE_DN( bi ); 
  
 	/* if not root, get appropriate limits */
 	if ( be_isroot( be, &op->o_ndn ) ) {
@@ -890,11 +977,12 @@ backsql_search(
 	 * of entries matching LDAP query filter and scope (or at least 
 	 * candidates), and get the IDs
 	 */
+	srch_info.n_candidates = ( isroot ? -2 : limit->lms_s_unchecked == -1 
+			? -2 : limit->lms_s_unchecked );
 	avl_apply( bi->oc_by_oc, (AVL_APPLY)backsql_oc_get_candidates,
 			&srch_info, 0, AVL_INORDER );
-
 	if ( !isroot && limit->lms_s_unchecked != -1 ) {
-		if ( srch_info.n_candidates > limit->lms_s_unchecked ) {
+		if ( srch_info.n_candidates == -1 ) {
 			send_search_result( conn, op,
 					LDAP_ADMINLIMIT_EXCEEDED,
 					NULL, NULL, NULL, NULL, 0 );
@@ -908,7 +996,8 @@ backsql_search(
 	 * mentioned in attrs and filter), test it against full filter 
 	 * and then send to client
 	 */
-	for ( eid = srch_info.id_list; eid != NULL; eid = eid->next ) {
+	for ( eid = srch_info.id_list; eid != NULL; 
+			eid = backsql_free_entryID( eid, 1 ) ) {
 
 		/* check for abandon */
 		if ( op->o_abandon ) {
@@ -919,7 +1008,7 @@ backsql_search(
 		if ( tlimit != -1 && slap_get_time() > stoptime ) {
 			send_search_result( conn, op, LDAP_TIMELIMIT_EXCEEDED,
 				NULL, NULL, v2refs, NULL, nentries );
-			break;
+			goto end_of_search;
 		}
 
 		Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
@@ -950,23 +1039,35 @@ backsql_search(
 				== LDAP_COMPARE_TRUE ) {
 			sres = send_search_entry( be, conn, op, entry,
 					attrs, attrsonly, NULL );
-			if ( sres == -1 ) {
+			switch ( sres ) {
+			case 0:
+				nentries++;
+				break;
+
+			case -1:
 				Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
 					"connection lost\n", 0, 0, 0 );
+				goto end_of_search;
+
+			default:
+				/*
+				 * FIXME: send_search_entry failed;
+				 * better stop
+				 */
 				break;
 			}
-			nentries += !sres;					
 		}
 		entry_free( entry );
 
-		if ( slimit != -1 && nentries > slimit ) {
+		if ( slimit != -1 && nentries >= slimit ) {
 			send_search_result( conn, op, LDAP_SIZELIMIT_EXCEEDED,
 				NULL, NULL, v2refs, NULL, nentries );
-			break;
+			goto end_of_search;
 		}
-     
 	}
 
+end_of_search:;
+
 	if ( nentries > 0 ) {
 		send_search_result( conn, op,
 			v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
@@ -977,9 +1078,6 @@ backsql_search(
 	}
 	
 done:;
-	for ( eid = srch_info.id_list; eid != NULL; 
-			eid = backsql_free_entryID( eid, 1 ) );
-
 	ch_free( srch_info.attrs );
 
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
diff --git a/servers/slapd/back-sql/sql-wrap.c b/servers/slapd/back-sql/sql-wrap.c
index f5c63d9026..bbc2374d09 100644
--- a/servers/slapd/back-sql/sql-wrap.c
+++ b/servers/slapd/back-sql/sql-wrap.c
@@ -64,16 +64,16 @@ backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, char *query, int timeout )
 		return rc;
 	}
 
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==>_SQLPrepare()\n", 0, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	SQLGetInfo( dbh, SQL_DRIVER_NAME, drv_name, sizeof( drv_name ), &len );
 
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "_SQLPrepare(): driver name='%s'\n",
 			drv_name, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	ldap_pvt_str2upper( drv_name );
 	if ( !strncmp( drv_name, "SQLSRV32.DLL", sizeof( drv_name ) ) ) {
@@ -107,10 +107,10 @@ backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, char *query, int timeout )
 		}
 	}
 
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "<==_SQLPrepare() calling SQLPrepare()\n",
 			0, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	return SQLPrepare( *sth, query, SQL_NTS );
 }
@@ -153,23 +153,23 @@ backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row )
 		return SQL_ERROR;
 	}
 
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==> backsql_BindRowAsStrings()\n", 0, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 	
 	rc = SQLNumResultCols( sth, &row->ncols );
 	if ( rc != SQL_SUCCESS ) {
-#if 0
+#ifdef BACKSQL_TRACE
 		Debug( LDAP_DEBUG_TRACE, "_SQLBindRowAsStrings(): "
 			"SQLNumResultCols() failed:\n", 0, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 		
 		backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC, sth, rc );
 	} else {
-#if 0
+#ifdef BACKSQL_TRACE
 		Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
 			"ncols=%d\n", (int)row->ncols, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 		row->col_names = (BerVarray)ch_calloc( row->ncols + 1, 
 				sizeof( struct berval ) );
@@ -185,11 +185,11 @@ backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row )
 					&name_len, &col_type,
 					&col_prec, &col_scale, &col_null );
 			ber_str2bv( colname, 0, 1, &row->col_names[ i - 1 ] );
-#if 0
+#ifdef BACKSQL_TRACE
 			Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
 				"col_name=%s, col_prec[%d]=%d\n",
 				colname, (int)i, (int)col_prec );
-#endif
+#endif /* BACKSQL_TRACE */
 			if ( col_type == SQL_LONGVARCHAR 
 					|| col_type == SQL_LONGVARBINARY) {
 #if 0
@@ -229,9 +229,10 @@ backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row )
 		row->col_names[ i - 1 ].bv_len = 0;
 		row->cols[ i - 1 ] = NULL;
 	}
-#if 0
+
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "<== backsql_BindRowAsStrings()\n", 0, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	return rc;
 }
@@ -305,10 +306,11 @@ int
 backsql_free_db_env( backsql_info *si )
 {
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_env()\n", 0, 0, 0 );
-#if 0
+
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "free_db_env(): delete AVL tree here!!!\n",
 			0, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	/*
 	 * stop, if frontend waits for all threads to shutdown 
@@ -366,7 +368,8 @@ backsql_open_db_conn( backsql_info *si, int ldap_cid, backsql_db_conn **pdbc )
 	 * See if this connection is to TimesTen.  If it is,
 	 * remember that fact for later use.
 	 */
-	si->isTimesTen = 0;	/* Assume until proven otherwise */
+	/* Assume until proven otherwise */
+	si->bsql_flags &= ~BSQLF_USE_REVERSE_DN;
 	DBMSName[ 0 ] = '\0';
 	rc = SQLGetInfo( dbc->dbh, SQL_DBMS_NAME, (PTR)&DBMSName,
 			sizeof( DBMSName ), NULL );
@@ -375,7 +378,7 @@ backsql_open_db_conn( backsql_info *si, int ldap_cid, backsql_db_conn **pdbc )
 				strcmp( DBMSName, "Front-Tier" ) == 0 ) {
 			Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
 				"TimesTen database!\n", 0, 0, 0 );
-			si->isTimesTen = 1;
+			si->bsql_flags |= BSQLF_USE_REVERSE_DN;
 		}
 	} else {
 		Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
@@ -454,11 +457,12 @@ backsql_get_db_conn( Backend *be, Connection *ldapc, SQLHDBC *dbh )
 	}
 
 	ldap_pvt_thread_mutex_lock( &si->schema_mutex );
-	if ( !si->schema_loaded ) {
+	if ( !BACKSQL_SCHEMA_LOADED( si ) ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_get_db_conn(): "
 			"first call -- reading schema map\n", 0, 0, 0 );
 		rc = backsql_load_schema_map( si, dbc->dbh );
 		if ( rc != LDAP_SUCCESS ) {
+			ldap_pvt_thread_mutex_unlock( &si->schema_mutex );
 			backsql_free_db_conn( be, ldapc );
 			return rc;
 		}
diff --git a/servers/slapd/back-sql/util.c b/servers/slapd/back-sql/util.c
index 48701c6aa4..ffd10efc95 100644
--- a/servers/slapd/back-sql/util.c
+++ b/servers/slapd/back-sql/util.c
@@ -23,10 +23,17 @@
 #include "schema-map.h"
 #include "util.h"
 
+#define BACKSQL_MAX(a,b) ((a)>(b)?(a):(b))
+#define BACKSQL_MIN(a,b) ((a)<(b)?(a):(b))
+
+#define BACKSQL_STR_GROW 256
 
 char backsql_def_oc_query[] = 
 	"SELECT id,name,keytbl,keycol,create_proc,delete_proc,expect_return "
 	"FROM ldap_oc_mappings";
+char backsql_def_needs_select_oc_query[] = 
+	"SELECT id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,"
+	"expect_return FROM ldap_oc_mappings";
 char backsql_def_at_query[] = 
 	"SELECT name,sel_expr,from_tbls,join_where,add_proc,delete_proc,"
 	"param_order,expect_return,sel_expr_u FROM ldap_attr_mappings "
@@ -38,34 +45,29 @@ char backsql_def_insentry_query[] =
 char backsql_def_subtree_cond[] = "ldap_entries.dn LIKE CONCAT('%',?)";
 char backsql_def_upper_subtree_cond[] = "(ldap_entries.dn) LIKE CONCAT('%',?)";
 char backsql_id_query[] = "SELECT id,keyval,oc_map_id FROM ldap_entries WHERE ";
+/* better ?||? or cast(?||? as varchar) */ 
+char backsql_def_concat_func[] = "CONCAT(?,?)";
 
 /* TimesTen */
 char backsql_check_dn_ru_query[] = "SELECT dn_ru from ldap_entries";
 
-/*
- * Frequently used constants
- */
-struct berval 
-	bv_n_objectclass	= BER_BVC("objectclass"),
-	bv_n_0_10		= BER_BVC("0.10");
-
 struct berval *
-backsql_strcat( struct berval *dest, int *buflen, ... )
+backsql_strcat( struct berval *dest, ber_len_t *buflen, ... )
 {
 	va_list		strs;
-	int		cdlen, cslen, grow;
+	ber_len_t	cdlen, cslen, grow;
 	char		*cstr;
 
 	assert( dest );
 	assert( dest->bv_val == NULL 
 			|| dest->bv_len == strlen( dest->bv_val ) );
  
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_strcat()\n" );
-#endif
+#endif /* BACKSQL_TRACE */
 
 	va_start( strs, buflen );
-	if ( dest->bv_val == NULL || *buflen <= 0 ) {
+	if ( dest->bv_val == NULL || *buflen == 0 ) {
 		dest->bv_val = (char *)ch_calloc( BACKSQL_STR_GROW, 
 				sizeof( char ) );
 		dest->bv_len = 0;
@@ -78,12 +80,13 @@ backsql_strcat( struct berval *dest, int *buflen, ... )
 		if ( *buflen - cdlen <= cslen ) {
 			char	*tmp_dest;
 
-#if 0
+#ifdef BACKSQL_TRACE
 			Debug( LDAP_DEBUG_TRACE, "backsql_strcat(): "
 				"buflen=%d, cdlen=%d, cslen=%d "
 				"-- reallocating dest\n",
 				*buflen, cdlen + 1, cslen );
-#endif
+#endif /* BACKSQL_TRACE */
+
 			tmp_dest = (char *)ch_realloc( dest->bv_val,
 					( *buflen ) + grow * sizeof( char ) );
 			if ( tmp_dest == NULL ) {
@@ -94,20 +97,131 @@ backsql_strcat( struct berval *dest, int *buflen, ... )
 			}
 			dest->bv_val = tmp_dest;
 			*buflen += grow;
-#if 0
+
+#ifdef BACKSQL_TRACE
 			Debug( LDAP_DEBUG_TRACE, "backsql_strcat(): "
 				"new buflen=%d, dest=%p\n", *buflen, dest, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
 		}
 		AC_MEMCPY( dest->bv_val + cdlen, cstr, cslen + 1 );
 		cdlen += cslen;
 	}
 	va_end( strs );
 
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_strcat() (dest='%s')\n", 
 			dest, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
+
+	dest->bv_len = cdlen;
+
+	return dest;
+} 
+
+struct berval *
+backsql_strfcat( struct berval *dest, ber_len_t *buflen, const char *fmt, ... )
+{
+	va_list		strs;
+	ber_len_t	cdlen;
+
+	assert( dest );
+	assert( buflen );
+	assert( fmt );
+	assert( *buflen == 0 || *buflen > dest->bv_len );
+	assert( dest->bv_val == NULL 
+			|| dest->bv_len == strlen( dest->bv_val ) );
+ 
+#ifdef BACKSQL_TRACE
+	Debug( LDAP_DEBUG_TRACE, "==>backsql_strfcat()\n" );
+#endif /* BACKSQL_TRACE */
+
+	va_start( strs, fmt );
+	if ( dest->bv_val == NULL || *buflen == 0 ) {
+		dest->bv_val = (char *)ch_calloc( BACKSQL_STR_GROW, 
+				sizeof( char ) );
+		dest->bv_len = 0;
+		*buflen = BACKSQL_STR_GROW;
+	}
+
+	cdlen = dest->bv_len;
+	for ( ; fmt[0]; fmt++ ) {
+		ber_len_t	cslen, grow;
+		char		*cstr, cc[ 2 ] = { '\0', '\0' };
+		struct berval	*cbv;
+
+		switch ( fmt[ 0 ] ) {
+
+		/* berval */
+		case 'b':
+			cbv = va_arg( strs, struct berval * );
+			cstr = cbv->bv_val;
+			cslen = cbv->bv_len;
+			break;
+
+		/* length + string */
+		case 'l':
+			cslen = va_arg( strs, ber_len_t );
+			cstr = va_arg( strs, char * );
+			break;
+			
+		/* string */
+		case 's':
+			cstr = va_arg( strs, char * );
+			cslen = strlen( cstr );
+			break;
+
+		/* char */
+		case 'c':
+			/* 
+			 * `char' is promoted to `int' when passed through `...'
+			 */
+			cc[0] = va_arg( strs, int );
+			cstr = cc;
+			cslen = 1;
+			break;
+
+		default:
+			assert( 0 );
+		}
+
+		grow = BACKSQL_MAX( BACKSQL_STR_GROW, cslen );
+		if ( *buflen - cdlen <= cslen ) {
+			char	*tmp_dest;
+
+#ifdef BACKSQL_TRACE
+			Debug( LDAP_DEBUG_TRACE, "backsql_strfcat(): "
+				"buflen=%d, cdlen=%d, cslen=%d "
+				"-- reallocating dest\n",
+				*buflen, cdlen + 1, cslen );
+#endif /* BACKSQL_TRACE */
+
+			tmp_dest = (char *)ch_realloc( dest->bv_val,
+					( *buflen ) + grow * sizeof( char ) );
+			if ( tmp_dest == NULL ) {
+				Debug( LDAP_DEBUG_ANY, "backsql_strfcat(): "
+					"could not reallocate string buffer.\n",
+					0, 0, 0 );
+				return NULL;
+			}
+			dest->bv_val = tmp_dest;
+			*buflen += grow * sizeof( char );
+
+#ifdef BACKSQL_TRACE
+			Debug( LDAP_DEBUG_TRACE, "backsql_strfcat(): "
+				"new buflen=%d, dest=%p\n", *buflen, dest, 0 );
+#endif /* BACKSQL_TRACE */
+		}
+
+		AC_MEMCPY( dest->bv_val + cdlen, cstr, cslen + 1 );
+		cdlen += cslen;
+	}
+
+	va_end( strs );
+
+#ifdef BACKSQL_TRACE
+	Debug( LDAP_DEBUG_TRACE, "<==backsql_strfcat() (dest='%s')\n", 
+			dest, 0, 0 );
+#endif /* BACKSQL_TRACE */
 
 	dest->bv_len = cdlen;
 
@@ -120,17 +234,15 @@ backsql_entry_addattr(
 	struct berval	*at_name,
 	struct berval	*at_val )
 {
-	struct berval		add_val[ 2 ];
 	AttributeDescription	*ad;
 	int			rc;
 	const char		*text;
 
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "backsql_entry_addattr(): "
 		"at_name='%s', at_val='%s'\n", 
 		at_name->bv_val, at_val->bv_val, 0 );
-	add_val[ 0 ] = *at_val;
-	add_val[ 1 ].bv_val = NULL;
-	add_val[ 1 ].bv_len = 0;
+#endif /* BACKSQL_TRACE */
 
 	ad = NULL;
 	rc = slap_bv2ad( at_name, &ad, &text );
@@ -141,7 +253,7 @@ backsql_entry_addattr(
 		return 0;
 	}
 
-	rc = attr_merge( e, ad, add_val );
+	rc = attr_merge_one( e, ad, at_val );
 
 	if ( rc != 0 ) {
 		Debug( LDAP_DEBUG_TRACE, "backsql_entry_addattr(): "
@@ -149,8 +261,11 @@ backsql_entry_addattr(
 			at_val->bv_val, at_name->bv_val, 0 );
 		return 0;
 	}
-	
+
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_query_addattr()\n", 0, 0, 0 );
+#endif /* BACKSQL_TRACE */
+
 	return 1;
 }
 
@@ -159,7 +274,10 @@ backsql_get_table_spec( char **p )
 {
 	char		*s, *q;
 	struct berval	res = { 0, NULL };
-	int		res_len = 0;
+	ber_len_t	res_len = 0;
+
+	assert( p );
+	assert( *p );
 
 	s = *p;
 	while ( **p && **p != ',' ) {
@@ -188,49 +306,57 @@ backsql_get_table_spec( char **p )
 		s = q;
 		BACKSQL_NEXT_WORD;
 	}
+
 #if 0
 	backsql_strcat( &res, &res_len, " AS ", s, NULL );
 	/* oracle doesn't understand AS :( */
 #endif
+
 	/* table alias */
-	backsql_strcat( &res, &res_len, " ", s, NULL);
+	backsql_strfcat( &res, &res_len, "cs", ' ', s );
+
 	return res.bv_val;
 }
 
 int
-backsql_merge_from_clause( char **dest_from, int *dest_len, char *src_from )
+backsql_merge_from_clause( 
+	struct berval	*dest_from,
+	ber_len_t	*dest_len, 
+	struct berval	*src_from )
 {
 	char		*s, *p, *srcc, *pos, e;
 	struct berval	res = { 0 , NULL };
 
-#if 0
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "==>backsql_merge_from_clause(): "
 		"dest_from='%s',src_from='%s'\n",
- 		dest_from, src_from, 0 );
-#endif
-	srcc = ch_strdup( src_from );
+ 		dest_from ? dest_from->bv_val : "<NULL>", src_from, 0 );
+#endif /* BACKSQL_TRACE */
+
+	srcc = ch_strdup( src_from->bv_val );
 	p = srcc;
 
-	if ( *dest_from != NULL ) {
-		res.bv_val = *dest_from;
-		res.bv_len = strlen( *dest_from );
+	if ( dest_from != NULL ) {
+		res = *dest_from;
 	}
 	
 	while ( *p ) {
 		s = backsql_get_table_spec( &p );
-#if 0
+
+#ifdef BACKSQL_TRACE
 		Debug( LDAP_DEBUG_TRACE, "backsql_merge_from_clause(): "
 			"p='%s' s='%s'\n", p, s, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
+
 		if ( res.bv_val == NULL ) {
 			backsql_strcat( &res, dest_len, s, NULL );
 
 		} else {
 			pos = strstr( res.bv_val, s );
 			if ( pos == NULL ) {
-				backsql_strcat( &res, dest_len, ",", s, NULL );
+				backsql_strfcat( &res, dest_len, "cs", ',', s );
 			} else if ( ( e = pos[ strlen( s ) ] ) != '\0' && e != ',' ) {
-				backsql_strcat( &res, dest_len, ",", s, NULL );
+				backsql_strfcat( &res, dest_len, "cs", ',', s );
 			}
 		}
 		
@@ -238,14 +364,115 @@ backsql_merge_from_clause( char **dest_from, int *dest_len, char *src_from )
 			ch_free( s );
 		}
 	}
-#if 0
+
+#ifdef BACKSQL_TRACE
 	Debug( LDAP_DEBUG_TRACE, "<==backsql_merge_from_clause()\n", 0, 0, 0 );
-#endif
+#endif /* BACKSQL_TRACE */
+
 	free( srcc );
-	*dest_from = res.bv_val;
+	*dest_from = res;
 
 	return 1;
 }
 
+/*
+ * splits a pattern in components separated by '?'
+ * (double ?? are turned into single ? and left in the string)
+ * expected contains the number of expected occurrences of '?'
+ * (a negative value means parse as many as possible)
+ */
+
+int
+backsql_split_pattern(
+	const char	*_pattern,
+	BerVarray	*split_pattern,
+	int		expected )
+{
+	char		*pattern, *start, *end;
+	struct berval	bv;
+	int		rc = 0;
+
+#define SPLIT_CHAR	'?'
+	
+	assert( _pattern );
+	assert( split_pattern );
+
+	pattern = ch_strdup( _pattern );
+
+	start = pattern;
+	end = strchr( start, SPLIT_CHAR );
+	for ( ; start; expected-- ) {
+		char		*real_end = end;
+		ber_len_t	real_len;
+		
+		if ( real_end == NULL ) {
+			real_end = start + strlen( start );
+
+		} else if ( real_end[ 1 ] == SPLIT_CHAR ) {
+			expected++;
+			AC_MEMCPY( real_end, real_end + 1, strlen( real_end ) );
+			end = strchr( real_end + 1, SPLIT_CHAR );
+			continue;
+		}
+
+		real_len = real_end - start;
+		if ( real_len == 0 ) {
+			ber_str2bv( "", 0, 1, &bv );
+		} else {
+			ber_str2bv( start, real_len, 1, &bv );
+		}
+
+		ber_bvarray_add( split_pattern, &bv );
+
+		if ( expected == 0 ) {
+			if ( end != NULL ) {
+				rc = -1;
+				goto done;
+			}
+			break;
+		}
+
+		if ( end != NULL ) {
+			start = end + 1;
+			end = strchr( start, SPLIT_CHAR );
+		}
+	}
+
+done:;
+
+     	ch_free( pattern );
+
+	return rc;
+}
+
+int
+backsql_prepare_pattern(
+	BerVarray	split_pattern,
+	BerVarray	values,
+	struct berval	*res )
+{
+	ber_len_t	len = 0;
+	int		i;
+
+	res->bv_val = NULL;
+	res->bv_len = 0;
+
+	for ( i = 0; values[i].bv_val; i++ ) {
+		if ( split_pattern[i].bv_val == NULL ) {
+			return -1;
+		}
+		backsql_strfcat( res, &len, "b", &split_pattern[ i ] );
+		backsql_strfcat( res, &len, "b", &values[ i ] );
+	}
+
+	if ( split_pattern[ i ].bv_val == NULL ) {
+		return -1;
+	}
+
+	backsql_strfcat( res, &len, "b", &split_pattern[ i ] );
+
+	return 0;
+}
+
 #endif /* SLAPD_SQL */
 
diff --git a/servers/slapd/back-sql/util.h b/servers/slapd/back-sql/util.h
index adc5911caf..a9f1042d03 100644
--- a/servers/slapd/back-sql/util.h
+++ b/servers/slapd/back-sql/util.h
@@ -14,21 +14,16 @@
 #include "entry-id.h"
 #include "schema-map.h"
 
-#define BACKSQL_MAX(a,b) ((a)>(b)?(a):(b))
-#define BACKSQL_MIN(a,b) ((a)<(b)?(a):(b))
+#define BACKSQL_CONCAT
 
-#define BACKSQL_STR_GROW 64
-
-extern struct berval 
-	bv_n_objectclass,
-	bv_n_0_10;
-
-struct berval *backsql_strcat( struct berval *dest, int *buflen, ... );
+struct berval * backsql_strcat( struct berval *dest, ber_len_t *buflen, ... );
+struct berval * backsql_strfcat( struct berval *dest, ber_len_t *buflen,
+		const char *fmt, ... );
 
 int backsql_entry_addattr( Entry *e, struct berval *at_name, 
 		struct berval *at_val );
 
-typedef struct __backsql_srch_info {
+typedef struct backsql_srch_info {
 	struct berval		*base_dn;
 	int			scope;
 	Filter			*filter;
@@ -40,16 +35,18 @@ typedef struct __backsql_srch_info {
 	backsql_info		*bi;
 	backsql_oc_map_rec	*oc;
 	struct berval		sel, from, join_where, flt_where;
-	int			sel_len, from_len, jwhere_len, fwhere_len;
+	ber_len_t		sel_len, from_len, jwhere_len, fwhere_len;
 	SQLHDBC			dbh;
 	int			status;
 	Backend			*be;
 	Connection		*conn;
 	Operation		*op;
 	AttributeName		*attrs;
+	int			attr_flags;
+#define	BSQL_SF_ALL_OPER	0x0001
 	Entry			*e;
 	/* 1 if the db is TimesTen; 0 if it's not */
-	int			isTimesTen; 
+	int			use_reverse_dn; 
 } backsql_srch_info;
 
 int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
@@ -63,18 +60,24 @@ Entry *backsql_id2entry( backsql_srch_info *bsi, Entry *e,
 
 extern char 
 	backsql_def_oc_query[],
+	backsql_def_needs_select_oc_query[],
 	backsql_def_at_query[],
 	backsql_def_delentry_query[],
 	backsql_def_insentry_query[],
 	backsql_def_subtree_cond[],
 	backsql_def_upper_subtree_cond[],
-	backsql_id_query[];
+	backsql_id_query[],
+	backsql_def_concat_func[];
 extern char 
 	backsql_check_dn_ru_query[];
 
-int backsql_merge_from_clause( char **dest_from, int *dest_len, 
-		char *src_from );
+int backsql_merge_from_clause( struct berval *dest_from, ber_len_t *dest_len, 
+		struct berval *src_from );
 
+int backsql_split_pattern( const char *pattern, BerVarray *split_pattern,
+		int expected );
+int backsql_prepare_pattern( BerVarray split_pattern, BerVarray values,
+		struct berval *res );
 
 #endif /* __BACKSQL_UTIL_H__ */
 
-- 
GitLab