]> git.sur5r.net Git - openldap/commitdiff
Sync with HEAD
authorKurt Zeilenga <kurt@openldap.org>
Fri, 16 Aug 2002 19:19:49 +0000 (19:19 +0000)
committerKurt Zeilenga <kurt@openldap.org>
Fri, 16 Aug 2002 19:19:49 +0000 (19:19 +0000)
33 files changed:
servers/slapd/back-ldap/search.c
servers/slapd/back-meta/search.c
servers/slapd/back-sql/back-sql.h [new file with mode: 0644]
servers/slapd/back-sql/bind.c [new file with mode: 0644]
servers/slapd/back-sql/config.c [new file with mode: 0644]
servers/slapd/back-sql/docs/install [new file with mode: 0644]
servers/slapd/back-sql/entry-id.c [new file with mode: 0644]
servers/slapd/back-sql/entry-id.h [new file with mode: 0644]
servers/slapd/back-sql/init.c [new file with mode: 0644]
servers/slapd/back-sql/modify.c [new file with mode: 0644]
servers/slapd/back-sql/other.c [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql [new file with mode: 0644]
servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql [new file with mode: 0644]
servers/slapd/back-sql/schema-map.c [new file with mode: 0644]
servers/slapd/back-sql/schema-map.h [new file with mode: 0644]
servers/slapd/back-sql/search.c
servers/slapd/back-sql/sql-types.h [new file with mode: 0644]
servers/slapd/back-sql/sql-wrap.c [new file with mode: 0644]
servers/slapd/back-sql/sql-wrap.h [new file with mode: 0644]
servers/slapd/back-sql/util.c
servers/slapd/back-sql/util.h [new file with mode: 0644]

index 45c5bd43df769e5a8e8009198d42e093eeabe27e..c2e278d5c7da2d4da400cff694e41c624be0e41f 100644 (file)
@@ -48,7 +48,7 @@
 #undef ldap_debug      /* silence a warning in ldap-int.h */
 #include "../../../libraries/libldap/ldap-int.h"
 
-static void ldap_send_entry( Backend *be, Operation *op, struct ldapconn *lc,
+static int ldap_send_entry( Backend *be, Operation *op, struct ldapconn *lc,
                              LDAPMessage *e, AttributeName *attrs, int attrsonly );
 
 int
@@ -272,8 +272,9 @@ fail:;
                        ldap_pvt_thread_yield();
                } else if (rc == LDAP_RES_SEARCH_ENTRY) {
                        e = ldap_first_entry(lc->ld,res);
-                       ldap_send_entry(be, op, lc, e, attrs, attrsonly);
-                       count++;
+                       if ( ldap_send_entry(be, op, lc, e, attrs, attrsonly) == LDAP_SUCCESS ) {
+                               count++;
+                       }
                        ldap_msgfree(res);
                } else {
                        sres = ldap_result2error(lc->ld, res, 1);
@@ -353,7 +354,7 @@ finish:;
        return rc;
 }
 
-static void
+static int
 ldap_send_entry(
        Backend *be,
        Operation *op,
@@ -373,7 +374,7 @@ ldap_send_entry(
        const char *text;
 
        if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
-               return;
+               return LDAP_DECODING_ERROR;
        }
 #ifdef ENABLE_REWRITE
 
@@ -400,7 +401,7 @@ ldap_send_entry(
                
        case REWRITE_REGEXEC_ERR:
        case REWRITE_REGEXEC_UNWILLING:
-               return;
+               return LDAP_OTHER;
        }
 #else /* !ENABLE_REWRITE */
        ldap_back_dn_massage( li, &bdn, &ent.e_name, 0, 0 );
@@ -414,7 +415,7 @@ ldap_send_entry(
         * FIXME: should we log anything, or delegate to dnNormalize2?
         */
        if ( dnNormalize2( NULL, &ent.e_name, &ent.e_nname ) != LDAP_SUCCESS ) {
-               return;
+               return LDAP_INVALID_DN_SYNTAX;
        }
        
        ent.e_id = 0;
@@ -560,4 +561,6 @@ ldap_send_entry(
                free( ent.e_dn );
        if ( ent.e_ndn )
                free( ent.e_ndn );
+
+       return LDAP_SUCCESS;
 }
index a670ef415cf8720605cbd65761c7cc7684b7ad97..4bab7a31f8fa9380fcb1fc6e7fe94a60b834cbef 100644 (file)
@@ -80,7 +80,7 @@
 #include "ldap_log.h"
 #include "../../../libraries/libldap/ldap-int.h"
 
-static void
+static int
 meta_send_entry(
                Backend         *be,
                Operation       *op,
@@ -445,9 +445,10 @@ meta_back_search(
                                goto finish;
                        } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
                                e = ldap_first_entry( lsc->ld, res );
-                               meta_send_entry(be, op, lc, i, e, attrs,
-                                               attrsonly);
-                               count++;
+                               if ( meta_send_entry( be, op, lc, i, e, attrs,
+                                               attrsonly ) == LDAP_SUCCESS ) {
+                                       count++;
+                               }
                                ldap_msgfree( res );
                                gotit = 1;
                        } else {
@@ -569,7 +570,7 @@ finish:;
        return rc;
 }
 
-static void
+static int
 meta_send_entry(
                Backend         *be,
                Operation       *op,
@@ -590,7 +591,7 @@ meta_send_entry(
        const char              *text;
 
        if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
-               return;
+               return LDAP_DECODING_ERROR;
        }
 
        /*
@@ -617,7 +618,7 @@ meta_send_entry(
                
        case REWRITE_REGEXEC_ERR:
        case REWRITE_REGEXEC_UNWILLING:
-               return;
+               return LDAP_OTHER;
        }
 
        /*
@@ -628,7 +629,7 @@ meta_send_entry(
         * FIXME: should we log anything, or delegate to dnNormalize2?
         */
        if ( dnNormalize2( NULL, &ent.e_name, &ent.e_nname ) != LDAP_SUCCESS ) {
-               return;
+               return LDAP_INVALID_DN_SYNTAX;
        }
 
        /*
@@ -782,6 +783,8 @@ meta_send_entry(
        if ( ent.e_ndn ) {
                free( ent.e_ndn );
        }
+
+       return LDAP_SUCCESS;
 }
 
 static int
diff --git a/servers/slapd/back-sql/back-sql.h b/servers/slapd/back-sql/back-sql.h
new file mode 100644 (file)
index 0000000..bd34917
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef __BACKSQL_H__
+#define __BACKSQL_H__
+
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "external.h"
+#include "sql-types.h"
+
+/*
+ * Better use the standard length of 8192 (as of servers/slapd/dn.c) ?
+ */
+#define BACKSQL_MAX_DN_LEN     255
+
+typedef struct {
+       char            *dbhost;
+       int             dbport;
+       char            *dbuser;
+       char            *dbpasswd;
+       char            *dbname;
+       /*
+        * SQL condition for subtree searches differs in syntax:
+        * "LIKE CONCAT('%',?)" or "LIKE '%'+?" or smth else 
+        */
+       char            *subtree_cond;
+       char            *oc_query,*at_query;
+       char            *insentry_query,*delentry_query;
+       char            *id_query;
+       char            *upper_func;
+       char            *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 ) \
+       ( (rc) == SQL_SUCCESS || (rc) == SQL_SUCCESS_WITH_INFO )
+
+#endif /* __BACKSQL_H__ */
+
diff --git a/servers/slapd/back-sql/bind.c b/servers/slapd/back-sql/bind.c
new file mode 100644 (file)
index 0000000..30c060f
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "slap.h"
+#include "back-sql.h"
+#include "sql-wrap.h"
+#include "util.h"
+#include "entry-id.h"
+
+int 
+backsql_bind(
+       BackendDB       *be,
+       Connection      *conn,
+       Operation       *op,
+       struct berval   *dn,
+       struct berval   *ndn,
+       int method,
+       struct berval   *cred,
+       struct berval   *edn )
+{
+       backsql_info            *bi = (backsql_info*)be->be_private;
+       backsql_entryID         user_id;
+       SQLHDBC                 dbh;
+       AttributeDescription    *password = slap_schema.si_ad_userPassword;
+       Entry                   *e, user_entry;
+       Attribute               *a;
+       backsql_srch_info       bsi;
+       int                     rc;
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_bind()\n", 0, 0, 0 );
+
+       if ( be_isroot_pw( be, conn, ndn, cred ) ) {
+               ber_dupbv( edn, be_root_dn( be ) );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_bind() root bind\n", 
+                               0, 0, 0 );
+               return LDAP_SUCCESS;
+       }
+
+       ber_dupbv( edn, ndn );
+
+       if ( method != LDAP_AUTH_SIMPLE ) {
+               send_ldap_result( conn, op, LDAP_STRONG_AUTH_NOT_SUPPORTED,
+                       NULL, "authentication method not supported", 
+                       NULL, NULL );
+               return 1;
+       }
+
+       /*
+        * method = LDAP_AUTH_SIMPLE
+        */
+       rc = backsql_get_db_conn( be, conn, &dbh );
+       if (!dbh) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_bind(): "
+                       "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;
+       }
+
+       if ( backsql_dn2id( bi, &user_id, dbh, ndn ) != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_bind(): "
+                       "could not retrieve bind dn id - no such entry\n", 
+                       0, 0, 0 );
+               send_ldap_result( conn, op, LDAP_INVALID_CREDENTIALS,
+                               NULL, NULL, NULL, NULL );
+               return 1;
+       }
+
+       backsql_init_search( &bsi, bi, ndn, LDAP_SCOPE_BASE, -1, -1, -1,
+                       NULL, dbh, be, conn, op, NULL );
+       e = backsql_id2entry( &bsi, &user_entry, &user_id );
+       if ( e == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_bind(): "
+                       "error in backsql_id2entry() - auth failed\n",
+                       0, 0, 0 );
+               send_ldap_result( conn, op, LDAP_OTHER,
+                               NULL, NULL, NULL, NULL );
+               return 1;
+       }
+
+       if ( ! access_allowed( be, conn, op, e, password, NULL, 
+                               ACL_AUTH, NULL ) ) {
+               send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, 
+                               NULL, NULL, NULL, NULL );
+               return 1;
+       }
+
+       if ( ( a = attr_find( e->e_attrs, password ) ) == NULL ) {
+               send_ldap_result( conn, op, LDAP_INAPPROPRIATE_AUTH, 
+                               NULL, NULL, NULL, NULL );
+               return 1;
+       }
+
+       if ( slap_passwd_check( conn, a, cred ) != 0 ) {
+               send_ldap_result( conn, op, LDAP_INVALID_CREDENTIALS,
+                               NULL, NULL, NULL, NULL );
+               return 1;
+       }
+
+       Debug(LDAP_DEBUG_TRACE,"<==backsql_bind()\n",0,0,0);
+       return 0;
+}
+int
+backsql_unbind(
+       BackendDB       *be,
+       Connection      *conn,
+       Operation       *op )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_unbind()\n", 0, 0, 0 );
+       send_ldap_result( conn, op, LDAP_SUCCESS, NULL, NULL, NULL, 0 );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_unbind()\n", 0, 0, 0 );
+       return 0;
+}
+
+#endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/config.c b/servers/slapd/back-sql/config.c
new file mode 100644 (file)
index 0000000..7403cc1
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "slap.h"
+#include "back-sql.h"
+#include "sql-wrap.h"
+
+int
+backsql_db_config(
+       BackendDB       *be,
+       const char      *fname,
+       int             lineno,
+       int             argc,
+       char            **argv )
+{
+       backsql_info    *si = (backsql_info *)be->be_private;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_db_config()\n", 0, 0, 0 );
+       assert( si );
+  
+       if ( !strcasecmp( argv[ 0 ], "dbhost" ) ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "<==backsql_db_config (%s line %d): "
+                               "missing hostname in dbhost directive\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->dbhost = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE,
+                       "<==backsql_db_config(): hostname=%s\n",
+                       si->dbhost, 0, 0 );
+
+       } else if ( !strcasecmp( argv[ 0 ], "dbuser" ) ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "<==backsql_db_config (%s line %d): "
+                               "missing username in dbuser directive\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->dbuser = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): dbuser=%s\n",
+                       si->dbuser, 0, 0 );
+
+       } else if ( !strcasecmp( argv[ 0 ], "dbpasswd" ) ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "<==backsql_db_config (%s line %d): "
+                               "missing password in dbpasswd directive\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->dbpasswd = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "dbpasswd=%s\n", /* si->dbpasswd */ "xxxx", 0, 0 );
+
+       } else if ( !strcasecmp( argv[ 0 ], "dbname" ) ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "<==backsql_db_config (%s line %d): "
+                               "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 ], "subtree_cond" ) ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "<==backsql_db_config (%s line %d): "
+                               "missing SQL condition "
+                               "in subtree_cond directive\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->subtree_cond = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "subtree_cond=%s\n", si->subtree_cond, 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",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->oc_query = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "oc_query=%s\n", si->oc_query, 0, 0 );
+
+       } else if ( !strcasecmp( argv[ 0 ], "at_query" ) ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<==backsql_db_config (%s line %d): "
+                               "missing SQL statement "
+                               "in at_query directive\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->at_query = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "at_query=%s\n", si->at_query, 0, 0 );
+
+       } else if ( !strcasecmp( argv[ 0 ], "insentry_query" ) ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "<==backsql_db_config (%s line %d): "
+                               "missing SQL statement "
+                               "in insentry_query directive\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->insentry_query = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "insentry_query=%s\n", si->insentry_query, 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",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->upper_func = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "upper_func=%s\n", si->upper_func, 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",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->strcast_func = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "strcast_func=%s\n", si->strcast_func, 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",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+               si->delentry_query = ch_strdup( argv[ 1 ] );
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "delentry_query=%s\n", si->delentry_query, 0, 0 );
+
+       } else if ( !strcasecmp( argv[ 0 ], "has_ldapinfo_dn_ru") ) {
+               if ( argc < 2 ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<==backsql_db_config (%s line %d): "
+                               "missing { yes | no }"
+                               "in has_ldapinfo_dn_ru directive\n",
+                               fname, lineno, 0 );
+                       return 1;
+               }
+
+               if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+                       si->has_ldapinfo_dn_ru = 1;
+               } else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+                       si->has_ldapinfo_dn_ru = 0;
+               } else {
+                       Debug( LDAP_DEBUG_TRACE,
+                               "<==backsql_db_config (%s line %d): "
+                               "has_ldapinfo_dn_ru directive arg "
+                               "must be \"yes\" or \"no\"\n",
+                               fname, lineno, 0 );
+                       return 1;
+
+               }
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config(): "
+                       "has_ldapinfo_dn_ru=%s\n", 
+                       si->has_ldapinfo_dn_ru == 0 ? "no" : "yes", 0, 0 );
+
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_db_config (%s line %d): "
+                       "unknown directive '%s' (ignored)\n",
+                       fname, lineno, argv[ 0 ] );
+       }
+
+       return 0;
+}
+
+#endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/docs/install b/servers/slapd/back-sql/docs/install
new file mode 100644 (file)
index 0000000..230bf0a
--- /dev/null
@@ -0,0 +1,86 @@
+PLEASE READ THIS WHOLE FILE AND CONCEPT, BECAUSE THEY COVER SEVERAL STICKY
+ISSUES THAT YOU WILL PROBABLY STUMBLE ACROSS ANYWAY
+
+1. Build
+To build slapd with back-sql under Unix you need to build and install
+iODBC 2.50.3 (later versions should probably work, but not earlier),
+or unixODBC (you will have to change -liodbc to -lodbc then).
+Then, at top of OpenLDAP source tree, run
+"configure <other options you need> --enable-sql", then "make" -
+this should build back-sql-enabled slapd, provided that you have iODBC/unixODBC
+libraries and include files in include/library paths, "make install"...
+In other words, follow installation procedure described in OpenLDAP 
+Administrators Guide, adding --enable-sql option to configure, and
+having iODBC/unixODBC libraries installed an accessible by compiler.
+
+Under Win32/MSVC++, I modified the workspace so that back-sql is built into
+slapd automatically, since MS ODBC manager, odbc32.dll, is included in
+standard library pack, and it does no bad even if you don't plan to use it.
+I also could provide precompiled executables for those who don't have MSVC.
+Note that Win32 port of OpenLDAP itself is experimental, and thus doesn't
+provide very convenient build environment (yet).
+
+2. Tune datasources and slapd.conf
+Next, you need to define ODBC datasource with data you want to publish
+with help of back-sql. Assuming that you have your data in some SQL-compliant
+RDBMS, and have installed proper ODBC driver for this RDBMS, this is as simple
+as adding a record into odbc.ini (for iODBC/unixODBC), or using ODBC wizard in
+Control Panel (for odbc32).
+Next, you need to add appropriate "database" record to your slapd.conf.
+See samples provided in "back-sql/RDBMS_DEPENDENT/" subdirectory. 
+
+Several things worth noting about ODBC:
+- "dbname" directive stands for ODBC datasource name (DSN),
+  not the name of your database in RDBMS context
+- ODBC under Unix is not so common as under Windows, so you could have
+  problems with Unix drivers for your RDBMS. Visit http://www.openlinksw.com,
+  they provide a multitier solution which allows connecting to DBMSes on
+  different platforms, proxying and other connectivity and integration issues.
+  They also support iODBC, and have good free customer service through
+  newsserver (at news.openlinksw.com).
+  Also worth noting are: ODBC-ODBC bridge by EasySoft (which was claimed
+   by several people to be far more effective and stable than OpenLink),
+   OpenRDA package etc.
+- be careful defining RDBMS connection parameters, you'll probably need only
+  "dbname" directive - all the rest can be defined in datasource. Every other
+  directive is used to override value stored in datasource definition.
+  Maybe you will want to use dbuser/dbpasswd to override credentials defined in datasource
+- full list of configuration directives supported is available in file "guide",
+  you may also analyze output of 'slapd -d 5' to find out some useful 
+  directives for redefining default queries 
+
+3. Creating and using back-sql metatables
+Read the file "concept" to understand, what metainformation you need to add,
+and what for... ;)
+See SQL scripts and slapd.conf files in samples directory.
+Find subdirectory in "rdbms_depend/" corresponding to your RDBMS (Oracle,
+MS SQL Server and mySQL are listed there currently), or copy and edit
+any of these to conform to SQL dialect of your RDBMS (please be sure to send
+me scripts and notes for new RDBMSes ;).
+
+Execute "backsql_create.sql" from that subdirectory (or edited one),
+so that the tables it creates appear in the same
+context with the data you want to export through LDAP (under same DB/user, 
+or whatever is needed in RDBMS you use). You can use something like
+"mysql < xxx.sql" for mySQL, Query Analyzer+Open query file for MS SQL,
+sqlplus and "@xxx.sql" for Oracle.
+
+You may well want to try it with test data first, and see how metatables
+are used. Create test data and metadata by running testdb_create.sql,
+testdb_data.sql, and testdb_metadata.sql scripts (again, adopted for your
+RDBMS, and in the same context as metatables you created before), and
+tune slapd.conf to use your test DB.
+
+4. Testing
+To diagnose back-sql, run slapd with debug level TRACE ("slapd -d 5" will go).
+Then, use some LDAP client to query corresponding subtree (for test database,
+you could for instance search one level from "o=sql,c=RU"). I personally used
+saucer, which is included in OpenLDAP package (it builds automatically under
+Unix/GNU configure and for MSVC I added appropriate project to workspace).
+And also Java LDAP browser-editor (see link somewhere on OpenLDAP site) to
+test ADD/DELETE/MODIFY operations on Oracle and MS SQL.
+
+See file "platforms" if you encounter connection problems - you may find
+a hint for your RDBMS or OS there. If you are stuck - please contact me at
+mit@openldap.org, or (better) post an issue through OpenLDAP's Issue Tracking
+System (see http:/www.openldap.org/its).
diff --git a/servers/slapd/back-sql/entry-id.c b/servers/slapd/back-sql/entry-id.c
new file mode 100644 (file)
index 0000000..552d239
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+#include "ldap_pvt.h"
+#include "slap.h"
+#include "back-sql.h"
+#include "sql-wrap.h"
+#include "schema-map.h"
+#include "entry-id.h"
+#include "util.h"
+
+backsql_entryID *
+backsql_free_entryID( backsql_entryID *id, int freeit )
+{
+       backsql_entryID         *next;
+
+       assert( id );
+
+       next = id->next;
+
+       if ( id->dn.bv_val != NULL ) {
+               free( id->dn.bv_val );
+       }
+
+       if ( freeit ) {
+               free( id );
+       }
+
+       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,
+       backsql_entryID         *id,
+       SQLHDBC                 dbh,
+       struct berval           *dn )
+{
+       SQLHSTMT                sth; 
+       BACKSQL_ROW_NTS         row;
+#if 0
+       SQLINTEGER              nrows = 0;
+#endif
+       RETCODE                 rc;
+       int                     res;
+
+       /* TimesTen */
+       char                    upperdn[ BACKSQL_MAX_DN_LEN + 1 ];
+       char                    *toBind;
+       int                     i, j;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_dn2id(): dn='%s'\n", 
+                       dn->bv_val, 0, 0 );
+
+       assert( id );
+
+       if ( dn->bv_len > BACKSQL_MAX_DN_LEN ) {
+               Debug( LDAP_DEBUG_TRACE, 
+                       "backsql_dn2id(): 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, "id_query '%s'\n", bi->id_query, 0, 0);
+       assert( bi->id_query );
+       rc = backsql_Prepare( dbh, &sth, bi->id_query, 0 );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, 
+                       "backsql_dn2id(): error preparing SQL:\n%s", 
+                       bi->id_query, 0, 0);
+               backsql_PrintErrors( SQL_NULL_HENV, dbh, sth, rc );
+               SQLFreeStmt( sth, SQL_DROP );
+               return LDAP_OTHER;
+       }
+
+       if ( bi->has_ldapinfo_dn_ru ) {
+               /*
+                * Prepare an upper cased, byte reversed version 
+                * that can be searched using indexes
+                */
+
+               for ( i = 0, j = dn->bv_len - 1; dn->bv_val[ i ]; i++, j--) {
+                       upperdn[ i ] = dn->bv_val[ j ];
+               }
+               upperdn[ i ] = '\0';
+               ldap_pvt_str2upper( upperdn );
+
+               Debug( LDAP_DEBUG_TRACE, "==>backsql_dn2id(): upperdn='%s'\n",
+                               upperdn, 0, 0 );
+               toBind = upperdn;
+       } else {
+               if ( bi->isTimesTen ) {
+                       AC_MEMCPY( upperdn, dn->bv_val, dn->bv_len + 1 );
+                       ldap_pvt_str2upper( upperdn );
+                       Debug( LDAP_DEBUG_TRACE,
+                               "==>backsql_dn2id(): upperdn='%s'\n",
+                               upperdn, 0, 0 );
+                       toBind = upperdn;
+
+               } else {
+                       toBind = dn->bv_val;
+               }
+       }
+
+       rc = backsql_BindParamStr( sth, 1, toBind, BACKSQL_MAX_DN_LEN );
+       if ( rc != SQL_SUCCESS) {
+               /* end TimesTen */ 
+               Debug( LDAP_DEBUG_TRACE, "backsql_dn2id(): "
+                       "error binding dn=\"%s\" parameter:\n", toBind, 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_dn2id(): "
+                       "error executing query (\"%s\", \"%s\"):\n", 
+                       bi->id_query, toBind, 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 ) ) {
+               id->id = atoi( row.cols[ 0 ] );
+               id->keyval = atoi( row.cols[ 1 ] );
+               id->oc_id = atoi( row.cols[ 2 ] );
+               ber_dupbv( &id->dn, dn );
+               id->next = NULL;
+
+               res = LDAP_SUCCESS;
+
+       } else {
+               res = LDAP_NO_SUCH_OBJECT;
+       }
+       backsql_FreeRow( &row );
+
+       SQLFreeStmt( sth, SQL_DROP );
+       if ( res == LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_dn2id(): id=%ld\n",
+                               id->id, 0, 0 );
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_dn2id(): no match\n",
+                               0, 0, 0 );
+       }
+       return res;
+}
+
+int
+backsql_get_attr_vals( backsql_at_map_rec *at, backsql_srch_info *bsi )
+{
+       RETCODE         rc;
+       SQLHSTMT        sth;
+       BACKSQL_ROW_NTS row;
+       int             i;
+
+       assert( at );
+       assert( 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 );
+
+       rc = backsql_Prepare( bsi->dbh, &sth, at->query, 0 );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_values(): "
+                       "error preparing query: %s\n", at->query, 0, 0 );
+               backsql_PrintErrors( bsi->bi->db_env, bsi->dbh, sth, rc );
+               return 1;
+       }
+
+       rc = backsql_BindParamID( sth, 1, &bsi->c_eid->keyval );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_values(): "
+                       "error binding key value parameter\n", 0, 0, 0 );
+               return 1;
+       }
+
+       rc = SQLExecute( sth );
+       if ( ! BACKSQL_SUCCESS( rc ) ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_values(): "
+                       "error executing attribute query '%s'\n",
+                       at->query, 0, 0 );
+               backsql_PrintErrors( bsi->bi->db_env, bsi->dbh, sth, rc );
+               SQLFreeStmt( sth, SQL_DROP );
+               return 1;
+       }
+
+       backsql_BindRowAsStrings( sth, &row );
+
+       rc = SQLFetch( sth );
+       for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
+               for ( i = 0; i < row.ncols; i++ ) {
+                       if ( row.is_null[ i ] > 0 ) {
+                               struct berval   bv;
+
+                               bv.bv_val = row.cols[ i ];
+#if 0
+                               bv.bv_len = row.col_prec[ i ];
+#else
+                               /*
+                                * FIXME: what if a binary 
+                                * is fetched?
+                                */
+                               bv.bv_len = strlen( row.cols[ i ] );
+#endif
+                                       backsql_entry_addattr( bsi->e, 
+                                               &row.col_names[ i ], &bv );
+
+#if 0
+                               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
+                       }
+               }
+       }
+
+       backsql_FreeRow( &row );
+       SQLFreeStmt( sth, SQL_DROP );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_get_attr_vals()\n", 0, 0, 0 );
+
+       return 1;
+}
+
+Entry *
+backsql_id2entry( backsql_srch_info *bsi, Entry *e, backsql_entryID *eid )
+{
+       int                     i;
+       backsql_at_map_rec      *at;
+       int                     rc;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_id2entry()\n", 0, 0, 0 );
+
+       rc = dnPrettyNormal( NULL, &eid->dn, &e->e_name, &e->e_nname );
+       if ( rc != LDAP_SUCCESS ) {
+               return NULL;
+       }
+
+       bsi->oc = backsql_id2oc( bsi->bi, eid->oc_id );
+       bsi->e = e;
+       bsi->c_eid = eid;
+       e->e_attrs = NULL;
+       e->e_private = NULL;
+       /* if ( bsi->base_dn != NULL)??? */
+       
+       e->e_id = eid->id;
+       if ( bsi->attrs != NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(): "
+                       "custom attribute list\n", 0, 0, 0 );
+               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 0  /* FIXME: what is 0.10 ? */
+                                       || !BACKSQL_NCMP( &attr->an_name, &bv_n_0_10 ) 
+#endif
+                                       ) {
+#if 0
+                               backsql_entry_addattr( bsi->e, 
+                                               &bv_n_objectclass,
+                                               &bsi->oc->name );
+#endif
+                               continue;
+                       }
+
+                       at = backsql_ad2at( bsi->oc, attr->an_desc );
+                       if ( at != NULL ) {
+                               backsql_get_attr_vals( at, bsi );
+                       } else {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(): "
+                                       "attribute '%s' is not defined "
+                                       "for objectlass '%s'\n",
+                                       attr->an_name.bv_val, 
+                                       bsi->oc->name.bv_val, 0 );
+                       }
+               }
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(): "
+                       "retrieving all attributes\n", 0, 0, 0 );
+               avl_apply( bsi->oc->attrs, (AVL_APPLY)backsql_get_attr_vals,
+                               bsi, 0, AVL_INORDER );
+       }
+
+       backsql_entry_addattr( bsi->e, &bv_n_objectclass, &bsi->oc->name );
+
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_id2entry()\n", 0, 0, 0 );
+
+       return e;
+}
+
+#endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/entry-id.h b/servers/slapd/back-sql/entry-id.h
new file mode 100644 (file)
index 0000000..bc52fa8
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __BACKSQL_ENTRYID_H__
+#define __BACKSQL_ENTRYID_H__
+
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+
+typedef struct backsql_entryID {
+       unsigned long           id;
+       unsigned long           keyval;
+       unsigned long           oc_id;
+       struct berval           dn;
+       struct backsql_entryID  *next;
+} backsql_entryID;
+
+int backsql_dn2id( backsql_info *bi, backsql_entryID *id,
+               SQLHDBC dbh, struct berval *dn );
+/* returns next */
+backsql_entryID *backsql_free_entryID( backsql_entryID *id, int freeit );
+
+#endif /* __BACKSQL_ENTRYID_H__ */
+
diff --git a/servers/slapd/back-sql/init.c b/servers/slapd/back-sql/init.c
new file mode 100644 (file)
index 0000000..79d8dec
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "slap.h"
+#include "ldap_pvt.h"
+#include "back-sql.h"
+#include "sql-wrap.h"
+#include "schema-map.h"
+#include "util.h"
+
+#ifdef SLAPD_SQL_DYNAMIC
+
+int
+backsql_LTX_init_module(
+       int             argc, 
+       char            *argv[] )
+{
+       BackendInfo bi;
+
+       memset( &bi, '\0', sizeof( bi ) );
+       bi.bi_type = "sql";
+       bi.bi_init = backbacksql_initialize;
+
+       backend_add( &bi );
+       return 0;
+}
+
+#endif /* SLAPD_SHELL_DYNAMIC */
+
+int
+sql_back_initialize(
+       BackendInfo     *bi )
+{ 
+       Debug( LDAP_DEBUG_TRACE,"==>backsql_initialize()\n", 0, 0, 0 );
+       
+       bi->bi_open = 0;
+       bi->bi_config = 0;
+       bi->bi_close = 0;
+       bi->bi_destroy = 0;
+
+       bi->bi_db_init = backsql_db_init;
+       bi->bi_db_config = backsql_db_config;
+       bi->bi_db_open = backsql_db_open;
+       bi->bi_db_close = backsql_db_close;
+       bi->bi_db_destroy = backsql_db_destroy;
+
+#ifdef BACKSQL_ALL_DONE
+       bi->bi_op_abandon = backsql_abandon;
+       bi->bi_op_compare = backsql_compare;
+#else
+       bi->bi_op_abandon = 0;
+       bi->bi_op_compare = 0;
+#endif
+       bi->bi_op_bind = backsql_bind;
+       bi->bi_op_unbind = backsql_unbind;
+       bi->bi_op_search = backsql_search;
+       bi->bi_op_modify = backsql_modify;
+       bi->bi_op_modrdn = backsql_modrdn;
+       bi->bi_op_add = backsql_add;
+       bi->bi_op_delete = backsql_delete;
+       
+       bi->bi_acl_group = 0;
+       bi->bi_acl_attribute = 0;
+       bi->bi_chk_referrals = 0;
+       bi->bi_connection_init = 0;
+       bi->bi_connection_destroy = backsql_connection_destroy;
+       
+       Debug( LDAP_DEBUG_TRACE,"<==backsql_initialize()\n", 0, 0, 0 );
+       return 0;
+}
+
+
+int
+backsql_destroy( 
+       BackendInfo     *bi )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_destroy()\n", 0, 0, 0 );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_destroy()\n", 0, 0, 0 );
+       return 0;
+}
+
+int
+backsql_db_init(
+       BackendDB       *bd )
+{
+       backsql_info *si;
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_db_init()\n", 0, 0, 0 );
+       si = (backsql_info *)ch_calloc( 1, 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 );
+       return 0;
+}
+
+int
+backsql_db_destroy(
+       BackendDB       *bd )
+{
+       backsql_info *si = (backsql_info*)bd->be_private;
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_db_destroy()\n", 0, 0, 0 );
+       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_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 ) {
+               free( si->dbpasswd );
+       }
+       if ( si->dbhost ) {
+               free( si->dbhost );
+       }
+       if ( si->upper_func ) {
+               free( si->upper_func );
+       }
+       
+       free( si->subtree_cond );
+       free( si->oc_query );
+       free( si->at_query );
+       free( si->insentry_query );
+       free( si->delentry_query );
+       free( si );
+       
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_db_destroy()\n", 0, 0, 0 );
+       return 0;
+}
+
+int
+backsql_db_open(
+       BackendDB       *bd )
+{
+       backsql_info    *si = (backsql_info*)bd->be_private;
+       Connection      tmp;
+       SQLHDBC         dbh;
+       int             idq_len;
+       struct berval   bv;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_db_open(): "
+               "testing RDBMS connection\n", 0, 0, 0 );
+       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 );
+               return 1;
+       }
+       
+       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 );
+               return 1;
+       }
+       
+       if ( si->subtree_cond == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+                       "subtree search SQL condition not specified "
+                       "(use subtree_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;
+               } else {
+                       si->subtree_cond = ch_strdup( backsql_def_subtree_cond );
+               }
+                       
+               Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+                       "setting '%s' as default\n",
+                       si->subtree_cond, 0, 0 );
+       }
+
+       if ( si->oc_query == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+                       "objectclass mapping SQL statement not specified "
+                       "(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 );
+       }
+       
+       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",
+                       0, 0, 0 );
+               Debug(LDAP_DEBUG_TRACE, "backsql_db_open(): "
+                       "setting '%s' by default\n",
+                       backsql_def_at_query, 0, 0 );
+               si->at_query = ch_strdup( backsql_def_at_query );
+       }
+       
+       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",
+                       0, 0, 0 );
+               Debug(LDAP_DEBUG_TRACE, "backsql_db_open(): "
+                       "setting '%s' by default\n",
+                       backsql_def_insentry_query, 0, 0 );
+               si->insentry_query = ch_strdup( backsql_def_insentry_query );
+       }
+       
+       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",
+                       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(): "
+                       "connection failed, exiting\n", 0, 0, 0 );
+               return 1;
+       }
+
+       si->id_query = NULL;
+       idq_len = 0;
+
+       bv.bv_val = NULL;
+       bv.bv_len = 0;
+       if ( si->upper_func == NULL ) {
+               backsql_strcat( &bv, &idq_len, backsql_id_query, 
+                               "dn=?", NULL );
+       } else {
+               if ( si->has_ldapinfo_dn_ru ) {
+                       backsql_strcat( &bv, &idq_len, backsql_id_query,
+                                       "dn_ru=?", NULL );
+               } else {
+                       if ( si->isTimesTen ) {
+                               backsql_strcat( &bv, &idq_len, 
+                                               backsql_id_query,
+                                               si->upper_func, "(dn)=?",
+                                               NULL );
+                       } else {
+                               backsql_strcat( &bv, &idq_len, 
+                                               backsql_id_query,
+                                               si->upper_func, "(dn)=",
+                                               si->upper_func, "(?)", NULL );
+                       }
+               }
+       }
+       si->id_query = bv.bv_val;
+       backsql_free_db_conn( bd, &tmp );
+       if ( !si->schema_loaded ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+                       "test failed, schema map not loaded - exiting\n",
+                       0, 0, 0 );
+               return 1;
+       }
+       
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_db_open(): "
+               "test succeeded, schema map loaded\n", 0, 0, 0 );
+       return 0;
+}
+
+int
+backsql_db_close(
+       BackendDB       *bd )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_db_close()\n", 0, 0, 0 );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_db_close()\n", 0, 0, 0 );
+       return 0;
+}
+
+int
+backsql_connection_destroy(
+       BackendDB       *be,
+       Connection      *conn )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_connection_destroy()\n", 0, 0, 0 );
+       backsql_free_db_conn( be, conn );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_connection_destroy()\n", 0, 0, 0 );
+       return 0;
+}
+
+#endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/modify.c b/servers/slapd/back-sql/modify.c
new file mode 100644 (file)
index 0000000..2757d33
--- /dev/null
@@ -0,0 +1,1247 @@
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include "slap.h"
+#include "ldap_pvt.h"
+#include "back-sql.h"
+#include "sql-wrap.h"
+#include "schema-map.h"
+#include "entry-id.h"
+#include "util.h"
+
+/*
+ * PostgreSQL doesn't work without :(
+ */
+#define        BACKSQL_REALLOC_STMT
+
+static int
+backsql_modify_internal(
+       backsql_info            *bi,
+       SQLHDBC                 dbh, 
+       backsql_oc_map_rec      *oc,
+       backsql_entryID         *e_id,
+       Modifications           *modlist )
+{
+       RETCODE         rc;
+       SQLHSTMT        sth;
+       Modifications   *ml;
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+               "traversing modifications list\n", 0, 0, 0 );
+#ifndef BACKSQL_REALLOC_STMT
+       SQLAllocStmt( dbh, &sth );
+#endif /* BACKSQL_REALLOC_STMT */
+       for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+               AttributeDescription *ad;
+               backsql_at_map_rec      *at = NULL;
+               struct berval           *at_val;
+               Modification            *c_mod;
+               int                     i;
+               /* first parameter no, parameter order */
+               SQLUSMALLINT            pno, po;
+               /* procedure return code */
+               int                     prc;
+
+#ifdef BACKSQL_REALLOC_STMT
+               SQLAllocStmt( dbh, &sth );
+#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 );
+               at = backsql_ad2at( oc, ad );
+               if ( at == NULL ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                               "attribute provided is not registered "
+                               "in objectclass '%s'\n",
+                               ad->ad_cname.bv_val, 0, 0 );
+                       continue;
+               }
+  
+               switch( c_mod->sm_op ) {
+               case LDAP_MOD_REPLACE: {
+                       SQLHSTMT asth;
+                       BACKSQL_ROW_NTS row;
+                        
+                       Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                               "replacing values for attribute '%s'\n",
+                               at->name.bv_val, 0, 0 );
+
+                       if ( at->add_proc == NULL ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "add procedure is not defined "
+                                       "for attribute '%s' "
+                                       "- unable to perform replacements\n",
+                                       at->name.bv_val, 0, 0 );
+                               break;
+                       }
+
+                       if ( at->delete_proc == NULL ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "delete procedure is not defined "
+                                       "for attribute '%s' "
+                                       "- adding only\n",
+                                       at->name.bv_val, 0, 0 );
+                               goto add_only;
+                       }
+                       
+del_all:
+                       rc = backsql_Prepare( dbh, &asth, at->query, 0 );
+                       if ( rc != SQL_SUCCESS ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "error preparing query\n", 0, 0, 0 );
+                               backsql_PrintErrors( bi->db_env, dbh, 
+                                               asth, rc );
+                               break;
+                       }
+
+                       rc = backsql_BindParamID( asth, 1, &e_id->keyval );
+                       if ( rc != SQL_SUCCESS ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "error binding key value parameter\n",
+                                       0, 0, 0 );
+                               backsql_PrintErrors( bi->db_env, dbh, 
+                                               asth, rc );
+                               SQLFreeStmt( asth, SQL_DROP );
+                               break;
+                       }
+                       
+                       rc = SQLExecute( asth );
+                       if ( !BACKSQL_SUCCESS( rc ) ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "error executing attribute query\n",
+                                       0, 0, 0 );
+                               backsql_PrintErrors( bi->db_env, dbh, 
+                                               asth, rc );
+                               SQLFreeStmt( asth, SQL_DROP );
+                               break;
+                       }
+
+                       backsql_BindRowAsStrings( asth, &row );
+                       rc = SQLFetch( asth );
+                       for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( asth ) ) {
+                               for ( i = 0; i < row.ncols; i++ ) {
+                                       if ( BACKSQL_IS_DEL( at->expect_return ) ) {
+                                               pno = 1;
+                                               SQLBindParameter(sth, 1,
+                                                       SQL_PARAM_OUTPUT,
+                                                       SQL_C_ULONG,
+                                                       SQL_INTEGER,
+                                                       0, 0, &prc, 0, 0 );
+                                       } else {
+                                               pno = 0;
+                                       }
+                                       po = ( BACKSQL_IS_DEL( at->param_order ) ) > 0;
+                                       SQLBindParameter( sth, pno + 1 + po,
+                                               SQL_PARAM_INPUT,
+                                               SQL_C_ULONG, SQL_INTEGER,
+                                               0, 0, &e_id->keyval, 0, 0 );
+
+                                       /*
+                                        * check for syntax needed here 
+                                        * maybe need binary bind?
+                                        */
+                                       SQLBindParameter(sth, pno + 2 - po,
+                                               SQL_PARAM_INPUT,
+                                               SQL_C_CHAR, SQL_CHAR,
+                                               0, 0, row.cols[ i ],
+                                               strlen( row.cols[ i ] ), 0 );
+                        
+                                       Debug( LDAP_DEBUG_TRACE, 
+                                               "backsql_modify(): "
+                                               "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(): "
+                                                       "delete_proc "
+                                                       "execution failed\n",
+                                                       0, 0, 0 );
+                                               backsql_PrintErrors( bi->db_env,
+                                                               dbh, sth, rc );
+                                       }
+#ifdef BACKSQL_REALLOC_STMT
+                                       SQLFreeStmt( sth, SQL_DROP );
+                                       SQLAllocStmt( dbh, &sth );
+#endif /* BACKSQL_REALLOC_STMT */
+                               }
+                       }
+                       backsql_FreeRow( &row );
+                       SQLFreeStmt( asth, SQL_DROP );
+               }
+                                      
+               /*
+                * PASSTHROUGH - to add new attributes -- do NOT add break
+                */
+               case LDAP_MOD_ADD:
+               case SLAP_MOD_SOFTADD:
+add_only:;
+                       if ( at->add_proc == NULL ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "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 );
+                               break;
+                       }
+                       
+                       Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                               "adding new values for attribute '%s'\n",
+                               at->name.bv_val, 0, 0 );
+                       for ( i = 0, at_val = c_mod->sm_bvalues;
+                                       at_val->bv_val != NULL; 
+                                       i++, at_val++ ) {
+                               if ( BACKSQL_IS_ADD( at->expect_return ) ) {
+                                       pno = 1;
+                                       SQLBindParameter( sth, 1,
+                                               SQL_PARAM_OUTPUT,
+                                               SQL_C_ULONG, SQL_INTEGER,
+                                               0, 0, &prc, 0, 0);
+                               } else {
+                                       pno = 0;
+                               }
+                               po = ( BACKSQL_IS_ADD( at->param_order ) ) > 0;
+                               SQLBindParameter( sth, pno + 1 + po,
+                                       SQL_PARAM_INPUT, 
+                                       SQL_C_ULONG, SQL_INTEGER,
+                                       0, 0, &e_id->keyval, 0, 0 );
+
+                               /*
+                                * check for syntax needed here
+                                * maybe need binary bind?
+                                */
+                               SQLBindParameter( sth, pno + 2 - po,
+                                       SQL_PARAM_INPUT,
+                                       SQL_C_CHAR, SQL_CHAR,
+                                       0, 0, at_val->bv_val, 
+                                       at_val->bv_len, 0 );
+
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "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(): "
+                                               "add_proc execution failed\n",
+                                               0, 0, 0 );
+                                       backsql_PrintErrors( bi->db_env,
+                                                       dbh, sth, rc );
+                               }
+#ifdef BACKSQL_REALLOC_STMT
+                               SQLFreeStmt( sth, SQL_DROP );
+                               SQLAllocStmt( dbh, &sth );
+#endif /* BACKSQL_REALLOC_STMT */
+                       }
+                       break;
+                       
+               case LDAP_MOD_DELETE:
+                       if ( at->delete_proc == NULL ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "delete 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 delete "
+                                       "for attribute '%s' "
+                                       "-- deleting all values\n",
+                                       at->name.bv_val, 0, 0 );
+                               goto del_all;
+                       }
+
+                       Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                               "deleting values for attribute '%s'\n",
+                               at->name.bv_val, 0, 0 );
+                       for ( i = 0, at_val = c_mod->sm_bvalues;
+                                       at_val->bv_val != NULL;
+                                       i++, at_val++ ) {
+                               if ( BACKSQL_IS_DEL( at->expect_return ) ) {
+                                       pno = 1;
+                                       SQLBindParameter( sth, 1,
+                                               SQL_PARAM_OUTPUT,
+                                               SQL_C_ULONG, SQL_INTEGER,
+                                               0, 0, &prc, 0, 0 );
+                               } else {
+                                       pno = 0;
+                               }
+                               po = ( BACKSQL_IS_DEL( at->param_order ) ) > 0;
+                               SQLBindParameter( sth, pno + 1 + po,
+                                       SQL_PARAM_INPUT, 
+                                       SQL_C_ULONG, SQL_INTEGER,
+                                       0, 0, &e_id->keyval, 0, 0 );
+
+                               /*
+                                * check for syntax needed here 
+                                * maybe need binary bind?
+                                */
+                               SQLBindParameter( sth, pno + 2 - po,
+                                       SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
+                                       0, 0, at_val->bv_val, 
+                                       at_val->bv_len, 0 );
+
+                               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                                       "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(): "
+                                               "delete_proc execution "
+                                               "failed\n", 0, 0, 0 );
+                                       backsql_PrintErrors( bi->db_env,
+                                                       dbh, sth, rc );
+                               }
+#ifdef BACKSQL_REALLOC_STMT
+                               SQLFreeStmt( sth, SQL_DROP );
+                               SQLAllocStmt( dbh, &sth );
+#endif /* BACKSQL_REALLOC_STMT */
+                       }
+                       break;
+               }
+#ifndef BACKSQL_REALLOC_STMT
+               SQLFreeStmt( sth, SQL_RESET_PARAMS );
+#else /* BACKSQL_REALLOC_STMT */
+               SQLFreeStmt( sth, SQL_DROP );
+#endif /* BACKSQL_REALLOC_STMT */
+       }
+
+#ifndef BACKSQL_REALLOC_STMT
+       SQLFreeStmt( sth, SQL_DROP );
+#endif /* BACKSQL_REALLOC_STMT */
+
+       /*
+        * FIXME: should fail in case one change fails?
+        */
+       return LDAP_SUCCESS;
+}
+
+int
+backsql_modify(
+       BackendDB       *be,
+       Connection      *conn,
+       Operation       *op,
+       struct berval   *dn,
+       struct berval   *ndn,
+       Modifications   *modlist )
+{
+       backsql_info            *bi = (backsql_info*)be->be_private;
+       SQLHDBC                 dbh;
+       backsql_oc_map_rec      *oc = NULL;
+       backsql_entryID         e_id;
+       int                     res;
+
+       /*
+        * FIXME: in case part of the operation cannot be performed
+        * (missing mapping, SQL write fails or so) the entire operation
+        * should be rolled-back
+        */
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_modify(): changing entry '%s'\n",
+               ndn->bv_val, 0, 0 );
+       res = backsql_get_db_conn( be, conn, &dbh );
+       if ( res != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                       "could not get connection handle - exiting\n", 
+                       0, 0, 0 );
+               /*
+                * FIXME: we don't want to send back 
+                * excessively detailed messages
+                */
+               send_ldap_result( conn, op, res, "", 
+                               res == LDAP_OTHER ?  "SQL-backend error" : "", 
+                               NULL, NULL );
+               return 1;
+       }
+
+       res = backsql_dn2id( bi, &e_id, dbh, ndn );
+       if ( res != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                       "could not lookup entry id\n", 0, 0, 0 );
+               send_ldap_result( conn, op, res , "", 
+                               res == LDAP_OTHER ? "SQL-backend error" : "",
+                               NULL, NULL );
+               return 1;
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+               "modifying entry '%s' (id=%ld)\n", 
+               e_id.dn.bv_val, e_id.id, 0 );
+
+       oc = backsql_id2oc( bi, e_id.oc_id );
+       if ( oc == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+                       "cannot determine objectclass of entry -- aborting\n",
+                       0, 0, 0 );
+               /*
+                * FIXME: should never occur, since the entry was built!!!
+                */
+
+               /*
+                * FIXME: we don't want to send back 
+                * excessively detailed messages
+                */
+               send_ldap_result( conn, op, LDAP_OTHER, "",
+                               "SQL-backend error", NULL, NULL );
+               return 1;
+       }
+
+       res = backsql_modify_internal( bi, dbh, oc, &e_id, modlist );
+       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 );
+       }
+       send_ldap_result( conn, op, res, "", NULL, NULL, NULL );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_modify()\n", 0, 0, 0 );
+
+       return 0;
+}
+
+int
+backsql_modrdn(
+       BackendDB       *be,
+       Connection      *conn,
+       Operation       *op,
+       struct berval   *dn,
+       struct berval   *ndn,
+       struct berval   *newrdn,
+       struct berval   *nnewrdn,
+       int             deleteoldrdn,
+       struct berval   *newSuperior,
+       struct berval   *nnewSuperior )
+{
+       backsql_info            *bi = (backsql_info*)be->be_private;
+       SQLHDBC                 dbh;
+       SQLHSTMT                sth;
+       RETCODE                 rc;
+       backsql_entryID         e_id, pe_id, new_pid;
+       backsql_oc_map_rec      *oc = NULL;
+       int                     res;
+       struct berval           p_dn, p_ndn,
+                               *new_pdn = NULL, *new_npdn = NULL,
+                               new_dn, new_ndn;
+       const char              *text = NULL;
+       LDAPRDN                 *new_rdn = NULL;
+       LDAPRDN                 *old_rdn = NULL;
+       Modifications           *mod;
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_modrdn() renaming entry '%s', "
+                       "newrdn='%s', newSuperior='%s'\n",
+                       dn->bv_val, newrdn->bv_val, 
+                       newSuperior ? newSuperior->bv_val : "(NULL)" );
+       res = backsql_get_db_conn( be, conn, &dbh );
+       if ( res != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "could not get connection handle - exiting\n", 
+                       0, 0, 0 );
+               send_ldap_result( conn, op, res, "",
+                               res == LDAP_OTHER ?  "SQL-backend error" : "",
+                               NULL, NULL );
+               return 1;
+       }
+
+       res = backsql_dn2id( bi, &e_id, dbh, ndn );
+       if ( res != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "could not lookup entry id\n", 0, 0, 0 );
+               send_ldap_result( conn, op, res, "",
+                               res == LDAP_OTHER ?  "SQL-backend error" : "",
+                               NULL, NULL );
+               return 1;
+       }
+
+       /*
+        * FIXME: check whether entry has children
+        */
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): entry id is %ld\n",
+               e_id.id, 0, 0 );
+
+       dnParent( dn, &p_dn );
+       dnParent( ndn, &p_ndn );
+
+       if ( newSuperior ) {
+               /*
+                * namingContext "" is not supported
+                */
+               if ( newSuperior->bv_len == 0 ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                               "newSuperior is \"\" - aborting\n", 0, 0, 0 );
+                       send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, 
+                                       "", "not allowed within namingContext", 
+                                       NULL, NULL );
+                       goto modrdn_return;
+               }
+
+               new_pdn = newSuperior;
+               new_npdn = nnewSuperior;
+
+       } else {
+               new_pdn = &p_dn;
+               new_npdn = &p_ndn;
+       }
+
+       if ( newSuperior && dn_match( &p_ndn, new_npdn ) ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "newSuperior is equal to old parent - ignored\n",
+                       0, 0, 0 );
+               newSuperior = NULL;
+       }
+
+       if ( newSuperior && dn_match( ndn, new_npdn ) ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "newSuperior is equal to entry being moved "
+                       "- aborting\n", 0, 0, 0 );
+               send_ldap_result( conn, op, LDAP_OPERATIONS_ERROR, "", 
+                               NULL, NULL, NULL );
+               goto modrdn_return;
+       }
+
+       build_new_dn( &new_dn, new_pdn, newrdn ); 
+       if ( dnNormalize2( NULL, &new_dn, &new_ndn ) != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "new dn is invalid ('%s') - aborting\n",
+                       new_dn.bv_val, 0, 0 );
+               send_ldap_result( conn, op, LDAP_INVALID_DN_SYNTAX, "", 
+                               NULL, NULL, NULL );
+               goto modrdn_return;
+       }
+       
+       Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): new entry dn is '%s'\n",
+                       new_dn.bv_val, 0, 0 );
+
+       res = backsql_dn2id( bi, &pe_id, dbh, &p_ndn );
+       if ( res != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "could not lookup old parent entry id\n", 0, 0, 0 );
+               send_ldap_result( conn, op, res, "", 
+                               res == LDAP_OTHER ? "SQL-backend error" : "",
+                               NULL, NULL );
+               goto modrdn_return;
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+               "old parent entry id is %ld\n", pe_id.id, 0, 0 );
+
+       res = backsql_dn2id( bi, &new_pid, dbh, new_npdn );
+       if ( res != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "could not lookup new parent entry id\n", 0, 0, 0 );
+               send_ldap_result( conn, op, res, "",
+                               res == LDAP_OTHER ? "SQL-backend error" : "",
+                               NULL, NULL );
+               goto modrdn_return;
+       }
+       
+       Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+               "new parent entry id is %ld\n", new_pid.id, 0, 0 );
+
+       Debug(  LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+               "executing delentry_query\n", 0, 0, 0 );
+       SQLAllocStmt( dbh, &sth );
+       SQLBindParameter( sth, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
+                       0, 0, &e_id.id, 0, 0 );
+       rc = SQLExecDirect( sth, bi->delentry_query, SQL_NTS );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "failed to delete record from ldap_entries\n",
+                       0, 0, 0 );
+               backsql_PrintErrors( bi->db_env, dbh, sth, rc );
+               send_ldap_result( conn, op, LDAP_OTHER, "",
+                               "SQL-backend error", NULL, NULL );
+               goto modrdn_return;
+       }
+
+       SQLFreeStmt( sth, SQL_RESET_PARAMS );
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+               "executing insentry_query\n", 0, 0, 0 );
+       backsql_BindParamStr( sth, 1, new_dn.bv_val, BACKSQL_MAX_DN_LEN );
+       SQLBindParameter( sth, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
+                       0, 0, &e_id.oc_id, 0, 0 );
+       SQLBindParameter( sth, 3, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
+                       0, 0, &new_pid.id, 0, 0 );
+       SQLBindParameter( sth, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
+                       0, 0, &e_id.keyval, 0, 0 );
+       rc = SQLExecDirect( sth, bi->insentry_query, SQL_NTS );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+                       "could not insert ldap_entries record\n", 0, 0, 0 );
+               backsql_PrintErrors( bi->db_env, dbh, sth, rc );
+               send_ldap_result( conn, op, LDAP_OTHER, "",
+                               "SQL-backend error", NULL, NULL );
+               goto modrdn_return;
+       }
+
+       /* 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 ) ) {
+#ifdef NEW_LOGGING
+               LDAP_LOG ( OPERATION, ERR, 
+                       "backsql_modrdn: can't figure out "
+                       "type(s)/values(s) of newrdn\n", 
+                       0, 0, 0 );
+#else
+               Debug( LDAP_DEBUG_TRACE,
+                       "backsql_modrdn: can't figure out "
+                       "type(s)/values(s) of newrdn\n", 
+                       0, 0, 0 );
+#endif
+               rc = LDAP_INVALID_DN_SYNTAX;
+               goto modrdn_return;
+       }
+
+#ifdef NEW_LOGGING
+       LDAP_LOG ( OPERATION, RESULTS, 
+               "backsql_modrdn: new_rdn_type=\"%s\", "
+               "new_rdn_val=\"%s\"\n",
+               new_rdn[ 0 ][ 0 ]->la_attr.bv_val, 
+               new_rdn[ 0 ][ 0 ]->la_value.bv_val, 0 );
+#else
+       Debug( LDAP_DEBUG_TRACE,
+               "backsql_modrdn: new_rdn_type=\"%s\", "
+               "new_rdn_val=\"%s\"\n",
+               new_rdn[ 0 ][ 0 ]->la_attr.bv_val,
+               new_rdn[ 0 ][ 0 ]->la_value.bv_val, 0 );
+#endif
+
+       if ( deleteoldrdn ) {
+               if ( ldap_bv2rdn( dn, &old_rdn, (char **)&text,
+                       LDAP_DN_FORMAT_LDAP ) ) {
+#ifdef NEW_LOGGING
+                       LDAP_LOG ( OPERATION, ERR, 
+                               "backsql_modrdn: can't figure out "
+                               "type(s)/values(s) of old_rdn\n", 
+                               0, 0, 0 );
+#else
+                       Debug( LDAP_DEBUG_TRACE,
+                               "backsql_modrdn: can't figure out "
+                               "the old_rdn type(s)/value(s)\n", 
+                               0, 0, 0 );
+#endif
+                       rc = LDAP_OTHER;
+                       goto modrdn_return;             
+               }
+       }
+
+       res = slap_modrdn2mods( NULL, NULL, NULL, NULL, 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 ) {
+               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 );
+
+modrdn_return:
+       SQLFreeStmt( sth, SQL_DROP );
+
+       if ( new_dn.bv_val ) {
+               ch_free( new_dn.bv_val );
+       }
+       
+       if ( new_ndn.bv_val ) {
+               ch_free( new_ndn.bv_val );
+       }
+       
+       /* LDAP v2 supporting correct attribute handling. */
+       if ( new_rdn != NULL ) {
+               ldap_rdnfree( new_rdn );
+       }
+       if ( old_rdn != NULL ) {
+               ldap_rdnfree( old_rdn );
+       }
+       if( mod != NULL ) {
+               Modifications *tmp;
+               for (; mod; mod=tmp ) {
+                       tmp = mod->sml_next;
+                       free( mod );
+               }
+       }
+
+       send_ldap_result( conn, op, res, "", NULL, NULL, NULL );
+
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_modrdn()\n", 0, 0, 0 );
+       return 0;
+}
+
+int
+backsql_add(
+       BackendDB       *be,
+       Connection      *conn,
+       Operation       *op,
+       Entry           *e )
+{
+       backsql_info            *bi = (backsql_info*)be->be_private;
+       SQLHDBC                 dbh;
+       SQLHSTMT                sth;
+       unsigned long           new_keyval = 0;
+       long                    i;
+       RETCODE                 rc;
+       backsql_oc_map_rec      *oc = NULL;
+       backsql_at_map_rec      *at_rec = NULL;
+       backsql_entryID         e_id, parent_id;
+       int                     res;
+       Attribute               *at;
+       struct berval           *at_val;
+       struct berval           pdn;
+       /* first parameter no, 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 );
+
+       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 ( oc == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                       "cannot determine objectclass of entry -- aborting\n",
+                       0, 0, 0 );
+               send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, "",
+                               "operation not permitted within namingContext",
+                               NULL, NULL );
+               return 1;
+       }
+
+       if ( oc->create_proc == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                       "create procedure is not defined for this objectclass "
+                       "- 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 );
+       if ( prc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                       "could not get connection handle - exiting\n", 
+                       0, 0, 0 );
+               send_ldap_result( conn, op, prc, "",
+                               prc == LDAP_OTHER ?  "SQL-backend error" : "",
+                               NULL, NULL );
+               return 1;
+       }
+
+       /*
+        * Check if entry exists
+        */
+       res = backsql_dn2id( bi, &e_id, dbh, &e->e_name );
+       if ( res == LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                       "entry '%s' exists\n",
+                       e->e_name.bv_val, 0, 0 );
+               send_ldap_result( conn, op, LDAP_ALREADY_EXISTS, "", 
+                               NULL, NULL, NULL );
+               return 1;
+       }
+
+       /*
+        * Check if parent exists
+        */
+       dnParent( &e->e_name, &pdn );
+       res = backsql_dn2id( bi, &parent_id, dbh, &pdn );
+       if ( res != LDAP_SUCCESS ) {
+               
+               /*
+                * NO SUCH OBJECT seems more appropriate
+                */
+               Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                       "could not lookup parent entry for new record '%s'\n",
+                       pdn.bv_val, 0, 0 );
+
+               if ( res != LDAP_NO_SUCH_OBJECT ) {
+                       send_ldap_result( conn, op, res, "", NULL, NULL, NULL );
+                       return 1;
+               }
+
+               /*
+                * Look for matched
+                */
+               while ( 1 ) {
+                       struct berval   dn;
+                       char            *matched = "";
+
+                       dn = pdn;
+                       dnParent( &dn, &pdn );
+
+                       /*
+                        * Empty DN ("") defaults to LDAP_SUCCESS
+                        */
+                       res = backsql_dn2id( bi, &parent_id, dbh, &pdn );
+                       switch ( res ) {
+                       case LDAP_NO_SUCH_OBJECT:
+                               if ( pdn.bv_len > 0 ) {
+                                       break;
+                               }
+                               /* fail over to next case */
+                               
+                       case LDAP_SUCCESS:
+                               matched = pdn.bv_val;
+                               /* fail over to next case */
+
+                       default:
+                               send_ldap_result( conn, op, res, matched, 
+                                               NULL, NULL, NULL );
+                               return 1;
+                       } 
+               }
+       }
+
+       /*
+        * create_proc is executed; if expect_return is set, then
+        * an output parameter is bound, which should contain 
+        * the id of the added row; otherwise the procedure
+        * is expected to return the id as the first column of a select
+        */
+
+#ifndef BACKSQL_REALLOC_STMT
+       rc = SQLAllocStmt( dbh, &sth );
+#else /* BACKSQL_REALLOC_STMT */
+       rc = backsql_Prepare( dbh, &sth, oc->create_proc, 0 );
+#endif /* BACKSQL_REALLOC_STMT */
+       if ( rc != SQL_SUCCESS ) {
+               send_ldap_result( conn, op, LDAP_OTHER, "",
+                               "SQL-backend error", NULL, NULL );
+               return 1;
+       }
+
+       if ( BACKSQL_IS_ADD( oc->expect_return ) ) {
+               SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT, SQL_C_ULONG, 
+                               SQL_INTEGER, 0, 0, &new_keyval, 0, 0 );
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_add(): executing '%s'\n",
+               oc->create_proc, 0, 0 );
+#ifndef BACKSQL_REALLOC_STMT
+       rc = SQLExecDirect( sth, oc->create_proc, SQL_NTS );
+#else /* BACKSQL_REALLOC_STMT */
+       rc = SQLExecute( sth );
+#endif /* BACKSQL_REALLOC_STMT */
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                       "create_proc execution failed\n", 0, 0, 0 );
+               backsql_PrintErrors( bi->db_env, dbh, sth, rc);
+               SQLFreeStmt( sth, SQL_DROP );
+               send_ldap_result( conn, op, LDAP_OTHER, "",
+                               "SQL-backend error", NULL, NULL );
+               return 1;
+       }
+
+       if ( !BACKSQL_IS_ADD( oc->expect_return ) ) {
+               SWORD           ncols;
+               SQLINTEGER      is_null;
+
+               /*
+                * the query to know the id of the inserted entry
+                * must be embedded in the create procedure
+                */
+               rc = SQLNumResultCols( sth, &ncols );
+               if ( rc != SQL_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                               "create_proc result evaluation failed\n",
+                               0, 0, 0 );
+                       backsql_PrintErrors( bi->db_env, dbh, sth, rc);
+                       SQLFreeStmt( sth, SQL_DROP );
+                       send_ldap_result( conn, op, LDAP_OTHER, "",
+                                       "SQL-backend error", NULL, NULL );
+                       return 1;
+
+               } else if ( ncols != 1 ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                               "create_proc result is bogus\n",
+                               0, 0, 0 );
+                       backsql_PrintErrors( bi->db_env, dbh, sth, rc);
+                       SQLFreeStmt( sth, SQL_DROP );
+                       send_ldap_result( conn, op, LDAP_OTHER, "",
+                                       "SQL-backend error", NULL, NULL );
+                       return 1;
+               }
+
+#if 0
+               {
+                       SQLCHAR         colname[ 64 ];
+                       SQLSMALLINT     name_len, col_type, col_scale, col_null;
+                       UDWORD          col_prec;
+
+                       /*
+                        * FIXME: check whether col_type is compatible,
+                        * if it can be null and so on ...
+                        */
+                       rc = SQLDescribeCol( sth, (SQLUSMALLINT)1, 
+                                       &colname[ 0 ], 
+                                       (SQLUINTEGER)( sizeof( colname ) - 1 ),
+                                       &name_len, &col_type,
+                                       &col_prec, &col_scale, &col_null );
+               }
+#endif
+
+               rc = SQLBindCol( sth, (SQLUSMALLINT)1, SQL_C_ULONG,
+                               (SQLPOINTER)&new_keyval, 
+                               (SQLINTEGER)sizeof( new_keyval ), 
+                               &is_null );
+
+               rc = SQLFetch( sth );
+
+#if 0
+               /*
+                * FIXME: what does is_null mean?
+                */
+               if ( is_null ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                               "create_proc result is null\n",
+                               0, 0, 0 );
+                       backsql_PrintErrors( bi->db_env, dbh, sth, rc);
+                       SQLFreeStmt( sth, SQL_DROP );
+                       send_ldap_result( conn, op, LDAP_OTHER, "",
+                                       "SQL-backend error", NULL, NULL );
+                       return 1;
+               }
+#endif
+       }
+
+#ifndef BACKSQL_REALLOC_STMT
+       SQLFreeStmt( sth, SQL_RESET_PARAMS );
+#else /* BACKSQL_REALLOC_STMT */
+       SQLFreeStmt( sth, SQL_DROP );
+#endif /* BACKSQL_REALLOC_STMT */
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+               "create_proc returned keyval=%ld\n", new_keyval, 0, 0 );
+
+       for ( at = e->e_attrs; at != NULL; at = at->a_next ) {
+               SQLUSMALLINT    currpos;
+
+               if ( at->a_vals[ 0 ].bv_val == NULL ) {
+                       continue;
+               }
+
+               at_rec = backsql_ad2at( oc, at->a_desc ); 
+  
+               if ( at_rec == NULL ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                               "attribute '%s' is not registered "
+                               "in objectclass '%s'\n",
+                               at->a_desc->ad_cname.bv_val,
+                               oc->name.bv_val, 0 );
+                       continue;
+               }
+               
+               if ( at_rec->add_proc == NULL ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                               "add procedure is not defined "
+                               "for attribute '%s'\n",
+                               at->a_desc->ad_cname.bv_val, 0, 0 );
+                       continue;
+               }
+
+#ifdef BACKSQL_REALLOC_STMT
+               rc = backsql_Prepare( dbh, &sth, at_rec->add_proc, 0 );
+               if ( rc != SQL_SUCCESS ) {
+                       continue;
+               }
+#endif /* BACKSQL_REALLOC_STMT */
+
+               if ( BACKSQL_IS_ADD( at_rec->expect_return ) ) {
+                       pno = 1;
+                       SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT,
+                                       SQL_C_ULONG, SQL_INTEGER,
+                                       0, 0, &prc, 0, 0 );
+               } else {
+                       pno = 0;
+               }
+
+               po = ( BACKSQL_IS_ADD( at_rec->param_order ) ) > 0;
+               currpos = pno + 1 + po;
+               SQLBindParameter( sth, currpos,
+                               SQL_PARAM_INPUT, SQL_C_ULONG,
+                               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;
+                               i++, at_val = &at->a_vals[ i ] ) {
+
+                       /*
+                        * check for syntax needed here 
+                        * maybe need binary bind?
+                        */
+
+                       backsql_BindParamStr( sth, currpos,
+                                       at_val->bv_val, at_val->bv_len + 1 );
+#ifdef SECURITY_PARANOID
+                       Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                               "executing '%s', id=%ld\n", 
+                               at_rec->add_proc, new_keyval, 0 );
+#else
+                       Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                               "executing '%s' with val='%s', id=%ld\n", 
+                               at_rec->add_proc, at_val->bv_val, new_keyval );
+#endif
+#ifndef BACKSQL_REALLOC_STMT
+                       rc = SQLExecDirect( sth, at_rec->add_proc, SQL_NTS );
+#else /* BACKSQL_REALLOC_STMT */
+                       rc = SQLExecute( sth );
+#endif /* BACKSQL_REALLOC_STMT */
+                       if ( rc != SQL_SUCCESS ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                                       "add_proc execution failed\n", 
+                                       0, 0, 0 );
+                               backsql_PrintErrors( bi->db_env, dbh, sth, rc );
+                       }
+               }
+#ifndef BACKSQL_REALLOC_STMT
+               SQLFreeStmt( sth, SQL_RESET_PARAMS ); 
+#else /* BACKSQL_REALLOC_STMT */
+               SQLFreeStmt( sth, SQL_DROP );
+#endif /* BACKSQL_REALLOC_STMT */
+       }
+
+#ifdef BACKSQL_REALLOC_STMT
+       rc = backsql_Prepare( dbh, &sth, bi->insentry_query, 0 );
+       if ( rc != SQL_SUCCESS ) {
+               send_ldap_result( conn, op, LDAP_OTHER, "",
+                               "SQL-backend error", NULL, NULL );
+               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 );
+       SQLBindParameter( sth, 3, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
+                       0, 0, &parent_id.id, 0, 0 );
+       SQLBindParameter( sth, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
+                       0, 0, &new_keyval, 0, 0 );
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_add(): executing '%s' for dn '%s'\n",
+                       bi->insentry_query, e->e_name.bv_val, 0 );
+       Debug( LDAP_DEBUG_TRACE, " for oc_map_id=%ld, parent_id=%ld, "
+                       "keyval=%ld\n", oc->id, parent_id.id, new_keyval );
+#ifndef BACKSQL_REALLOC_STMT
+       rc = SQLExecDirect( sth, bi->insentry_query, SQL_NTS );
+#else /* BACKSQL_REALLOC_STMT */
+       rc = SQLExecute( sth );
+#endif /* BACKSQL_REALLOC_STMT */
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+                       "could not insert ldap_entries record\n", 0, 0, 0 );
+               backsql_PrintErrors( bi->db_env, dbh, sth, rc );
+               
+               /*
+                * execute delete_proc to delete data added !!!
+                */
+               SQLFreeStmt( sth, SQL_DROP );
+               send_ldap_result( conn, op, LDAP_OTHER, "", 
+                               "SQL-backend error", NULL, NULL );
+               return 1;
+       }
+       
+       SQLFreeStmt( sth, SQL_DROP );
+
+       /*
+        * Commit only if all operations succeed
+        *
+        * FIXME: backsql_add() does not fail if add operations 
+        * are not available for some attributes, 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 );
+
+       send_ldap_result( conn, op, LDAP_SUCCESS, "",
+                       NULL, NULL, NULL );
+       return 0;
+}
+
+int
+backsql_delete(
+       BackendDB       *be,
+       Connection      *conn,
+       Operation       *op,
+       struct berval   *dn,
+       struct berval   *ndn )
+{
+       backsql_info            *bi = (backsql_info*)be->be_private;
+       SQLHDBC                 dbh;
+       SQLHSTMT                sth;
+       RETCODE                 rc;
+       backsql_oc_map_rec      *oc = NULL;
+       backsql_entryID         e_id;
+       int                     res;
+       /* first parameter no */
+       SQLUSMALLINT            pno;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_delete(): deleting entry '%s'\n",
+                       ndn->bv_val, 0, 0 );
+       res = backsql_get_db_conn( be, conn, &dbh );
+       if ( res != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
+                       "could not get connection handle - exiting\n", 
+                       0, 0, 0 );
+               send_ldap_result( conn, op, res, "", 
+                               res == LDAP_OTHER ? "SQL-backend error" : "",
+                               NULL, NULL );
+               return 1;
+       }
+       
+       res = backsql_dn2id( bi, &e_id, dbh, ndn );
+       if ( res != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
+                       "could not lookup entry id\n", 0, 0, 0 );
+               send_ldap_result( conn, op, res, "", 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 );
+               return 1;
+       }
+
+       if ( oc->delete_proc == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "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", 
+                               NULL, NULL );
+               return 1;
+       }
+
+       SQLAllocStmt( dbh, &sth );
+       if ( BACKSQL_IS_DEL( oc->expect_return ) ) {
+               pno = 1;
+               SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT, SQL_C_ULONG,
+                               SQL_INTEGER, 0, 0, &rc, 0, 0 );
+       } else {
+               pno = 0;
+       }
+
+       SQLBindParameter( sth, pno + 1, SQL_PARAM_INPUT, 
+                       SQL_C_ULONG, SQL_INTEGER, 0, 0, &e_id.keyval, 0, 0 );
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_delete(): executing '%s'\n",
+                       oc->delete_proc, 0, 0 );
+       rc = SQLExecDirect( sth, oc->delete_proc, SQL_NTS );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
+                       "delete_proc execution failed\n", 0, 0, 0 );
+               backsql_PrintErrors( bi->db_env, dbh, sth, rc );
+               SQLFreeStmt( sth, SQL_DROP );
+               send_ldap_result( conn, op, LDAP_OTHER, "",
+                               "SQL-backend error", NULL, NULL );
+               return 1;
+       }
+#ifndef BACKSQL_REALLOC_STMT
+       SQLFreeStmt( sth, SQL_RESET_PARAMS );
+#else /* BACKSQL_REALLOC_STMT */
+       SQLFreeStmt( sth, SQL_DROP );
+       SQLAllocStmt( dbh, &sth );
+#endif /* BACKSQL_REALLOC_STMT */
+
+       SQLBindParameter( sth, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
+                       0, 0, &e_id.id, 0, 0 );
+       rc = SQLExecDirect( sth, bi->delentry_query, SQL_NTS );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
+                       "failed to delete record from ldap_entries\n", 
+                       0, 0, 0 );
+               backsql_PrintErrors( bi->db_env, dbh, sth, rc );
+               SQLFreeStmt( sth, SQL_DROP );
+               send_ldap_result( conn, op, LDAP_OTHER, "",
+                               "SQL-backend error", NULL, NULL );
+               return 1;
+       }
+       
+       SQLFreeStmt( sth, SQL_DROP );
+
+       /*
+        * Commit only if all operations succeed
+        *
+        * FIXME: backsql_add() does not fail if add operations 
+        * are not available for some attributes, 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 );
+
+       send_ldap_result( conn, op, LDAP_SUCCESS, "", NULL, NULL, NULL );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n", 0, 0, 0 );
+       return 0;
+}
+
+#endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/other.c b/servers/slapd/back-sql/other.c
new file mode 100644 (file)
index 0000000..57c16d5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "slap.h"
+#include "back-sql.h"
+#include "sql-wrap.h"
+
+int
+backsql_compare(
+       BackendDB       *bd,
+       Connection      *conn,
+       Operation       *op,
+       struct berval   *dn,
+       struct berval   *ndn,
+       AttributeAssertion *ava )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_compare() - not implemented\n",
+                       0, 0, 0 );
+       return 1;
+}
+
+int
+backsql_abandon(
+       BackendDB       *be,
+       Connection      *conn, 
+       Operation       *op, 
+       int             msgid )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_abandon()\n", 0, 0, 0 );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_abandon()\n", 0, 0, 0 );
+       return 0;
+}
+
+#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
new file mode 100644 (file)
index 0000000..ca19bb1
--- /dev/null
@@ -0,0 +1,65 @@
+drop table ldap_oc_mappings;
+create table ldap_oc_mappings
+ (
+       id integer not null primary key,
+       name varchar(64) not null,
+       keytbl varchar(64) not null,
+       keycol varchar(64) not null,
+       create_proc varchar(255),
+       delete_proc varchar(255),
+       expect_return integer not null
+);
+
+drop table ldap_attr_mappings;
+create table ldap_attr_mappings
+ (
+       id integer not null primary key,
+       oc_map_id integer not null references ldap_oc_mappings(id),
+       name varchar(255) not null,
+       sel_expr varchar(255) not null,
+       sel_expr_u varchar(255),
+       from_tbls varchar(255) not null,
+       join_where varchar(255),
+       add_proc varchar(255),
+       delete_proc varchar(255),
+       param_order integer not null,
+       expect_return integer not null
+);
+
+drop table ldap_entries;
+create table ldap_entries
+ (
+       id integer not null primary key,
+       dn varchar(255) not null,
+       oc_map_id integer not null references ldap_oc_mappings(id),
+       parent int NOT NULL ,
+       keyval int NOT NULL 
+);
+
+alter table ldap_entries add 
+       constraint unq1_ldap_entries unique
+       (
+               oc_map_id,
+               keyval
+       );  
+
+alter table ldap_entries add
+       constraint unq2_ldap_entries unique
+       (
+               dn
+       );  
+
+drop table ldap_referrals;
+create table ldap_referrals
+ (
+       entry_id integer not null references ldap_entries(id),
+       url varchar(256) not null
+);
+
+drop table ldap_entry_objclasses;
+create table ldap_entry_objclasses
+ (
+       entry_id integer not null references ldap_entries(id),
+       oc_name varchar(64)
+ );
+
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql
new file mode 100644 (file)
index 0000000..49e7e3a
--- /dev/null
@@ -0,0 +1,5 @@
+DROP TABLE ldap_referrals;
+DROP TABLE ldap_entry_objclasses;
+DROP TABLE ldap_attr_mappings;
+DROP TABLE ldap_entries;
+DROP TABLE ldap_oc_mappings;
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf b/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf
new file mode 100644 (file)
index 0000000..86661da
--- /dev/null
@@ -0,0 +1,33 @@
+# $OpenLDAP$
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include                /usr/local/etc/openldap/schema/core.schema
+include                /usr/local/etc/openldap/schema/cosine.schema
+include                /usr/local/etc/openldap/schema/inetorgperson.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral      ldap://root.openldap.org
+
+pidfile                /usr/local/var/slapd.pid
+argsfile       /usr/local/var/slapd.args
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database       sql
+suffix         "o=sql,c=RU"
+rootdn         "cn=root,o=sql,c=RU"
+rootpw         secret
+dbname         ldap_db2
+dbuser         db2inst1
+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"
+
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql
new file mode 100644 (file)
index 0000000..a1da4a1
--- /dev/null
@@ -0,0 +1,66 @@
+drop table persons;
+CREATE TABLE persons (
+       id int NOT NULL,
+       name varchar(255) NOT NULL
+);
+
+drop table institutes;
+CREATE TABLE institutes (
+       id int NOT NULL,
+       name varchar(255)
+);
+
+drop table documents;
+CREATE TABLE documents (
+       id int NOT NULL,
+       title varchar(255) NOT NULL,
+       abstract varchar(255)
+);
+
+drop table authors_docs;
+CREATE TABLE authors_docs (
+       pers_id int NOT NULL,
+       doc_id int NOT NULL
+);
+
+drop table phones;
+CREATE TABLE phones (
+       id int NOT NULL ,
+       phone varchar(255) NOT NULL ,
+       pers_id int NOT NULL 
+);
+
+
+
+ALTER TABLE authors_docs  ADD 
+       CONSTRAINT PK_authors_docs PRIMARY KEY  
+       (
+               pers_id,
+               doc_id
+       );
+
+ALTER TABLE documents  ADD 
+       CONSTRAINT PK_documents PRIMARY KEY  
+       (
+               id
+       ); 
+
+ALTER TABLE institutes  ADD 
+       CONSTRAINT PK_institutes PRIMARY KEY  
+       (
+               id
+       );  
+
+
+ALTER TABLE persons  ADD 
+       CONSTRAINT PK_persons PRIMARY KEY  
+       (
+               id
+       ); 
+
+ALTER TABLE phones  ADD 
+       CONSTRAINT PK_phones PRIMARY KEY  
+       (
+               id
+       ); 
+
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql
new file mode 100644 (file)
index 0000000..f141f41
--- /dev/null
@@ -0,0 +1,16 @@
+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 phones (id,phone,pers_id) values (1,'332-2334',1);
+insert into phones (id,phone,pers_id) values (2,'222-3234',1);
+insert into phones (id,phone,pers_id) values (3,'545-4563',2);
+
+insert into documents (id,abstract,title) values (1,'abstract1','book1');
+insert into documents (id,abstract,title) values (2,'abstract2','book2');
+
+insert into authors_docs (pers_id,doc_id) values (1,1);
+insert into authors_docs (pers_id,doc_id) values (1,2);
+insert into authors_docs (pers_id,doc_id) values (2,1);
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql
new file mode 100644 (file)
index 0000000..17b12af
--- /dev/null
@@ -0,0 +1,5 @@
+DROP TABLE persons;
+DROP TABLE institutes;
+DROP TABLE documents;
+DROP TABLE authors_docs;
+DROP TABLE phones;
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql
new file mode 100644 (file)
index 0000000..c38036b
--- /dev/null
@@ -0,0 +1,74 @@
+--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,delete_proc,expect_return)
+values (2,'document','documents','id',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_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);
+
+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);
+
+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);
+
+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 (5,2,'documentTitle','documents.title','documents',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 (6,2,'documentAuthor','persons.name','persons,documents,authors_docs',
+--         'persons.id=authors_docs.pers_id AND documents.id=authors_docs.doc_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 (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',
+        '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);
+
+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 (9,2,'documentAuthor','ldap_entries.dn','ldap_entries,documents,authors_docs,persons',
+        'ldap_entries.keyval=persons.id AND ldap_entries.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+       NULL,NULL,3,0);
+       
+-- entries
+       
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (1,'o=sql,c=RU',3,0,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (2,'cn=Mitya Kovalev,o=sql,c=RU',1,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (3,'cn=Torvlobnor Puzdoy,o=sql,c=RU',1,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (4,'cn=Akakiy Zinberstein,o=sql,c=RU',1,1,3);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (5,'documentTitle=book1,o=sql,c=RU',2,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (6,'documentTitle=book2,o=sql,c=RU',2,1,2);
+       
+       
+-- referrals
+
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (4,'referral');
+
+insert into ldap_referrals (entry_id,url)
+values (4,'http://localhost');
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql
new file mode 100644 (file)
index 0000000..65aea73
--- /dev/null
@@ -0,0 +1,57 @@
+drop table ldap_oc_mappings;
+drop sequence ldap_oc_mappings_id_seq;
+create table ldap_oc_mappings
+ (
+       id serial not null primary key,
+       name varchar(64) not null,
+       keytbl varchar(64) not null,
+       keycol varchar(64) not null,
+       create_proc varchar(255),
+       delete_proc varchar(255),
+       expect_return int not null
+);
+
+drop table ldap_attr_mappings;
+drop sequence ldap_attr_mappings_id_seq;
+create table ldap_attr_mappings
+ (
+       id serial not null primary key,
+       oc_map_id integer not null references ldap_oc_mappings(id),
+       name varchar(255) not null,
+       sel_expr varchar(255) not null,
+       sel_expr_u varchar(255),
+       from_tbls varchar(255) not null,
+       join_where varchar(255),
+       add_proc varchar(255),
+       delete_proc varchar(255),
+       param_order int not null,
+       expect_return int not null
+);
+
+drop table ldap_entries;
+drop sequence ldap_entries_id_seq;
+create table ldap_entries
+ (
+       id serial not null primary key,
+       dn varchar(255) not null,
+       oc_map_id integer not null references ldap_oc_mappings(id),
+       parent int NOT NULL,
+       keyval int NOT NULL,
+       UNIQUE ( oc_map_id, keyval ),
+       UNIQUE ( dn )
+);
+
+drop table ldap_referrals;
+create table ldap_referrals
+ (
+       entry_id integer not null references ldap_entries(id),
+       url text not null
+);
+
+drop table ldap_entry_objclasses;
+create table ldap_entry_objclasses
+ (
+       entry_id integer not null references ldap_entries(id),
+       oc_name varchar(64)
+ );
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql
new file mode 100644 (file)
index 0000000..b497eb9
--- /dev/null
@@ -0,0 +1,9 @@
+DROP TABLE IF EXISTS ldap_referrals;
+
+DROP TABLE IF EXISTS ldap_entry_objclasses;
+
+DROP TABLE IF EXISTS ldap_attr_mappings;
+
+DROP TABLE IF EXISTS ldap_entries;
+
+DROP TABLE IF EXISTS ldap_oc_mappings;
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf b/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf
new file mode 100644 (file)
index 0000000..3a5dbb5
--- /dev/null
@@ -0,0 +1,35 @@
+# $OpenLDAP$
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include                /usr/local/etc/openldap/schema/core.schema
+include                /usr/local/etc/openldap/schema/cosine.schema
+include                /usr/local/etc/openldap/schema/inetorgperson.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral      ldap://root.openldap.org
+
+pidfile                /usr/local/var/slapd.pid
+argsfile       /usr/local/var/slapd.args
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database       sql
+suffix         "o=sql,c=RU"
+rootdn         "cn=root,o=sql,c=RU"
+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"
+has_ldapinfo_dn_ru     no
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql
new file mode 100644 (file)
index 0000000..6a3c0f7
--- /dev/null
@@ -0,0 +1,38 @@
+drop table persons;
+drop sequence persons_id_seq;
+create table persons (
+       id serial not null primary key,
+       name varchar(255) not null,
+       surname varchar(255) not null
+);
+
+drop table institutes;
+drop sequence institutes_id_seq;
+create table institutes (
+       id serial not null primary key,
+       name varchar(255)
+);
+
+drop table documents;
+drop sequence documents_id_seq;
+create table documents (
+       id serial not null primary key,
+       title varchar(255) not null,
+       abstract varchar(255)
+);
+
+drop table authors_docs;
+create table authors_docs (
+       pers_id int not null,
+       doc_id int not null,
+       primary key ( pers_id, doc_id )
+);
+
+drop table phones;
+drop sequence phones_id_seq;
+create table phones (
+       id serial not null primary key,
+       phone varchar(255) not null ,
+       pers_id int not null
+);
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql
new file mode 100644 (file)
index 0000000..b20733d
--- /dev/null
@@ -0,0 +1,17 @@
+insert into institutes (id,name) values (1,'sql');
+
+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);
+insert into phones (id,phone,pers_id) values (3,'545-4563',2);
+
+insert into documents (id,abstract,title) values (1,'abstract1','book1');
+insert into documents (id,abstract,title) values (2,'abstract2','book2');
+
+insert into authors_docs (pers_id,doc_id) values (1,1);
+insert into authors_docs (pers_id,doc_id) values (1,2);
+insert into authors_docs (pers_id,doc_id) values (2,1);
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql
new file mode 100644 (file)
index 0000000..7c5e9e7
--- /dev/null
@@ -0,0 +1,5 @@
+DROP TABLE IF EXISTS persons;
+DROP TABLE IF EXISTS institutes;
+DROP TABLE IF EXISTS documents;
+DROP TABLE IF EXISTS authors_docs;
+DROP TABLE IF EXISTS phones;
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql
new file mode 100644 (file)
index 0000000..7100a26
--- /dev/null
@@ -0,0 +1,100 @@
+--mappings 
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return) values (1,'inetOrgPerson','persons','id','select create_person()','select delete_person(?)',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,delete_proc,expect_return) values (3,'organization','institutes','id',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','text(persons.name||'' ''||persons.surname)','persons',NULL,'select update_person_cn(?,?)',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','select add_phone(?,?)','select delete_phone(?,?)',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,'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 (6,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 (4,2,'description','documents.abstract','documents',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 (5,2,'documentTitle','documents.title','documents',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 (6,2,'documentAuthor','persons.name','persons,documents,authors_docs',
+--         'persons.id=authors_docs.pers_id AND documents.id=authors_docs.doc_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 (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,'documentIdentifier','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);
+
+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 (9,2,'documentAuthor','ldap_entries.dn','ldap_entries,documents,authors_docs,persons','ldap_entries.keyval=persons.id AND ldap_entries.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',NULL,NULL,3,0);
+       
+-- entries
+       
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (1,'o=sql,c=RU',3,0,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (2,'cn=Mitya Kovalev,o=sql,c=RU',1,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (3,'cn=Torvlobnor Puzdoy,o=sql,c=RU',1,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (4,'cn=Akakiy Zinberstein,o=sql,c=RU',1,1,3);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (5,'documentTitle=book1,o=sql,c=RU',2,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (6,'documentTitle=book2,o=sql,c=RU',2,1,2);
+       
+       
+-- referrals
+
+insert into ldap_entry_objclasses (entry_id,oc_name) values (4,'referral');
+
+insert into ldap_referrals (entry_id,url) values (4,'http://localhost');
+
+-- procedures
+
+create function create_person () returns int
+as '
+       select setval (''persons_id_seq'', (select max(id) from persons));
+       insert into persons (id,name,surname) 
+               values (nextval(''persons_id_seq''),'''','''');
+       select max(id) from persons
+' language 'sql';
+
+create function update_person_cn (varchar, int) returns int
+as '
+       update persons set name = (
+               select case 
+                       when position('' '' in $1) = 0 then $1 
+                       else substr($1, 1, position('' '' in $1) - 1)
+               end
+       ),surname = (
+               select case 
+                       when position('' '' in $1) = 0 then ''''
+                       else substr($1, position('' '' in $1) + 1) 
+               end
+       ) where id = $2;
+       select $2 as return
+' language 'sql';
+
+create function delete_person (int) returns int
+as '
+       delete from phones where pers_id = $1;
+       delete from authors_docs where pers_id = $1;
+       delete from persons where id = $1;
+       select $1 as return
+' language 'sql';
+
+create function add_phone (varchar, int) returns int
+as '
+       select setval (''phones_id_seq'', (select max(id) from phones));
+       insert into phones (id,phone,pers_id)
+               values (nextval(''phones_id_seq''),$1,$2);
+       select max(id) from phones
+' language 'sql';
+
+create function delete_phone (varchar, int) returns int
+as '
+       delete from phones where phone = $1 and pers_id = $2;
+       select $2 as result
+' language 'sql';
+
diff --git a/servers/slapd/back-sql/schema-map.c b/servers/slapd/back-sql/schema-map.c
new file mode 100644 (file)
index 0000000..cf765ad
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include "slap.h"
+#include "ldap_pvt.h"
+#include "back-sql.h"
+#include "sql-wrap.h"
+#include "schema-map.h"
+#include "util.h"
+
+/*
+ * Deprecated
+ */
+#if 0
+static int
+backsql_cmp_oc_name( backsql_oc_map_rec *m1, backsql_oc_map_rec *m2 )
+{
+       return BACKSQL_NCMP( &m1->name, &m2->name );
+}
+#endif
+
+/*
+ * Uses the pointer to the ObjectClass structure
+ */
+static int
+backsql_cmp_oc( backsql_oc_map_rec *m1, backsql_oc_map_rec *m2 )
+{
+       return ( m1->oc < m2->oc ? -1 : ( m1->oc > m2->oc ? 1 : 0 ) );
+}
+
+static int
+backsql_cmp_oc_id( backsql_oc_map_rec *m1, backsql_oc_map_rec *m2 )
+{
+       return ( m1->id < m2->id ? -1 : ( m1->id > m2->id ? 1 : 0 ) );
+}
+
+/*
+ * Deprecated
+ */
+#if 0
+static int
+backsql_cmp_attr_name( backsql_at_map_rec *m1, backsql_at_map_rec *m2 )
+{
+       return BACKSQL_NCMP( &m1->name, &m2->name );
+}
+#endif
+
+/*
+ * Uses the pointer to the AttributeDescription structure
+ */
+static int
+backsql_cmp_attr( backsql_at_map_rec *m1, backsql_at_map_rec *m2 )
+{
+       return ( m1->ad < m2->ad ? -1 : ( m1->ad > m2->ad ? 1 : 0 ) );
+}
+
+static int
+backsql_make_attr_query( 
+       backsql_oc_map_rec      *oc_map,
+       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 );
+       }
+
+       at_map->query = tmps.bv_val;
+       
+       return 0;
+}
+
+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;
+
+       snprintf( s, sizeof( s ), "%ld", oc_map->id );
+
+       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 );
+
+       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->add_proc = NULL;
+       at_map->delete_proc = NULL;
+       at_map->param_order = 0;
+       at_map->expect_return = 0;
+       backsql_make_attr_query( oc_map, at_map );
+       avl_insert( &oc_map->attrs, at_map, 
+                       (AVL_CMP)backsql_cmp_attr, NULL );
+
+       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 );
+
+       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->add_proc = NULL;
+       at_map->delete_proc = NULL;
+       at_map->param_order = 0;
+       at_map->expect_return = 0;
+       backsql_make_attr_query( oc_map, at_map );
+       avl_insert( &oc_map->attrs, at_map, 
+                       (AVL_CMP)backsql_cmp_attr, NULL );
+
+       return 1;
+}
+
+int
+backsql_load_schema_map( backsql_info *si, SQLHDBC dbh )
+{
+       SQLHSTMT                oc_sth, at_sth;
+       RETCODE                 rc;
+       BACKSQL_ROW_NTS         oc_row, at_row;
+       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 ) {
+               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 */
+                       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 */
+               }
+
+               SQLFreeStmt( oc_sth, SQL_DROP );
+       }
+
+
+       rc = backsql_Prepare( dbh, &oc_sth, si->oc_query, 0 );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+                       "error preparing oc_query: '%s'\n", 
+                       si->oc_query, 0, 0 );
+               backsql_PrintErrors( si->db_env, dbh, oc_sth, rc );
+               return LDAP_OTHER;
+       }
+       Debug( LDAP_DEBUG_TRACE, "load_schema_map(): at_query '%s'\n", 
+                       si->at_query, 0, 0 );
+
+       rc = backsql_Prepare( dbh, &at_sth, si->at_query, 0 );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+                       "error preparing at_query: '%s'\n", 
+                       si->at_query, 0, 0 );
+               backsql_PrintErrors( si->db_env, dbh, at_sth, rc );
+               return LDAP_OTHER;
+       }
+
+       rc = backsql_BindParamID( at_sth, 1, &oc_id );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+                       "error binding param for at_query: \n", 0, 0, 0 );
+               backsql_PrintErrors( si->db_env, dbh, at_sth, rc );
+               return LDAP_OTHER;
+       }
+
+       rc = SQLExecute( oc_sth );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+                       "error executing oc_query: \n", 0, 0, 0 );
+               backsql_PrintErrors( si->db_env, dbh, oc_sth, rc );
+               return LDAP_OTHER;
+       }
+
+       backsql_BindRowAsStrings( oc_sth, &oc_row );
+       rc = SQLFetch( oc_sth );
+       for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( oc_sth ) ) {
+               oc_map = (backsql_oc_map_rec *)ch_calloc( 1,
+                               sizeof( backsql_oc_map_rec ) );
+
+               oc_map->id = atoi( oc_row.cols[ 0 ] );
+
+               ber_str2bv( oc_row.cols[ 1 ], 0, 1, &oc_map->name );
+               oc_map->oc = oc_bvfind( &oc_map->name );
+               if ( oc_map->oc == NULL ) {
+                       Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+                               "objectClass '%s' is not defined in schema\n", 
+                               oc_map->name.bv_val, 0, 0 );
+                       return LDAP_OTHER;      /* undefined objectClass ? */
+               }
+               
+               oc_map->keytbl = ch_strdup( oc_row.cols[ 2 ] );
+               oc_map->keycol = ch_strdup( oc_row.cols[ 3 ] );
+               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 ] );
+
+               /*
+                * FIXME: first attempt to check for offending
+                * instructions in {create|delete}_proc
+                */
+
+               oc_map->attrs = NULL;
+               avl_insert( &si->oc_by_oc, oc_map,
+                               (AVL_CMP)backsql_cmp_oc, NULL );
+               avl_insert( &si->oc_by_id, oc_map,
+                               (AVL_CMP)backsql_cmp_oc_id, NULL );
+               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 );
+               if ( oc_map->create_proc ) {
+                       Debug( LDAP_DEBUG_TRACE, "create_proc='%s'\n",
+                               oc_map->create_proc, 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",
+                       BACKSQL_IS_ADD( oc_map->expect_return ), 
+                       BACKSQL_IS_DEL( oc_map->expect_return ), 0 );
+
+               Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+                       "autoadding 'objectClass' and 'ref' mappings\n",
+                       0, 0, 0 );
+               backsql_add_sysmaps( oc_map );
+               rc = SQLExecute( at_sth );
+               if ( rc != SQL_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+                               "error executing at_query: \n", 0, 0, 0 );
+                       backsql_PrintErrors( SQL_NULL_HENV, dbh, at_sth, rc );
+                       return LDAP_OTHER;
+               }
+
+               backsql_BindRowAsStrings( at_sth, &at_row );
+               rc = SQLFetch( at_sth );
+               for ( ; BACKSQL_SUCCESS(rc); rc = SQLFetch( at_sth ) ) {
+                       const char      *text = NULL;
+
+                       Debug( LDAP_DEBUG_TRACE, "********'%s'\n",
+                               at_row.cols[ 0 ], 0, 0 );
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "name='%s',sel_expr='%s' from='%s'",
+                               at_row.cols[ 0 ], at_row.cols[ 1 ],
+                               at_row.cols[ 2 ] );
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "join_where='%s',add_proc='%s'",
+                               at_row.cols[ 3 ], at_row.cols[ 4 ], 0 );
+                       Debug( LDAP_DEBUG_TRACE, "delete_proc='%s'\n",
+                                       at_row.cols[ 5 ], 0, 0 );
+                       /* TimesTen */
+                       Debug( LDAP_DEBUG_TRACE, "sel_expr_u='%s'\n",
+                                       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 );
+                       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, 
+                                       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;
+                       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 ] );
+                       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 ] );
+                       backsql_make_attr_query( oc_map, at_map );
+                       Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+                               "preconstructed query '%s'\n",
+                               at_map->query, 0, 0 );
+                       avl_insert( &oc_map->attrs, at_map, 
+                                       (AVL_CMP)backsql_cmp_attr, NULL );
+               }
+               backsql_FreeRow( &at_row );
+               SQLFreeStmt( at_sth, SQL_CLOSE );
+       }
+       backsql_FreeRow( &oc_row );
+       SQLFreeStmt( at_sth, SQL_DROP );
+       SQLFreeStmt( oc_sth, SQL_DROP );
+       si->schema_loaded = 1;
+       Debug( LDAP_DEBUG_TRACE, "<==load_schema_map()\n", 0, 0, 0 );
+       return LDAP_SUCCESS;
+}
+
+backsql_oc_map_rec *
+backsql_oc2oc( backsql_info *si, ObjectClass *oc )
+{
+       backsql_oc_map_rec      tmp, *res;
+
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_oc2oc(): "
+               "searching for objectclass with name='%s'\n",
+               objclass, 0, 0 );
+#endif
+
+       tmp.oc = oc;
+       res = (backsql_oc_map_rec *)avl_find( si->oc_by_oc, &tmp,
+                       (AVL_CMP)backsql_cmp_oc );
+#if 0
+       if ( res != NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): "
+                       "found name='%s', id=%d\n", res->name, res->id, 0 );
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): "
+                       "not found\n", 0, 0, 0 );
+       }
+#endif
+       return res;
+}
+
+/*
+ * Deprecated
+ */
+backsql_oc_map_rec *
+backsql_name2oc( backsql_info *si, struct berval *oc_name )
+{
+       backsql_oc_map_rec      tmp, *res;
+
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "==>oc_with_name(): "
+               "searching for objectclass with name='%s'\n",
+               objclass, 0, 0 );
+#endif
+
+       tmp.oc = oc_bvfind( oc_name );
+       if ( tmp.oc == NULL ) {
+               return NULL;
+       }
+
+       res = (backsql_oc_map_rec *)avl_find( si->oc_by_oc, &tmp,
+                       (AVL_CMP)backsql_cmp_oc );
+#if 0
+       if ( res != NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
+                       "found name='%s', id=%d\n", res->name, res->id, 0 );
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
+                       "not found\n", 0, 0, 0 );
+       }
+#endif
+       return res;
+}
+
+backsql_oc_map_rec *
+backsql_id2oc( backsql_info *si, unsigned long id )
+{
+       backsql_oc_map_rec      tmp, *res;
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "==>oc_with_id(): "
+               "searching for objectclass with id='%d'\n", id, 0, 0 );
+#endif
+
+       tmp.id = id;
+       res = (backsql_oc_map_rec *)avl_find( si->oc_by_id, &tmp,
+                       (AVL_CMP)backsql_cmp_oc_id );
+
+#if 0
+       if ( res != NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
+                       "found name='%s', id=%d\n", res->name, res->id, 0 );
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
+                       "not found\n", 0, 0, 0 );
+       }
+#endif
+       
+       return res;
+}
+
+backsql_at_map_rec *
+backsql_ad2at( backsql_oc_map_rec* objclass, AttributeDescription *ad )
+{
+       backsql_at_map_rec      tmp, *res;
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_ad2at(): "
+               "searching for attribute '%s' for objectclass '%s'\n",
+               attr, objclass->name, 0 );
+#endif
+       tmp.ad = ad;
+       res = (backsql_at_map_rec *)avl_find( objclass->attrs, &tmp,
+                       (AVL_CMP)backsql_cmp_attr );
+
+#if 0
+       if ( res != NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): "
+                       "found name='%s', sel_expr='%s'\n",
+                       res->name, res->sel_expr, 0 );
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): "
+                       "not found\n", 0, 0, 0 );
+       }
+#endif
+
+       return res;
+}
+
+/*
+ * Deprecated
+ */
+backsql_at_map_rec *
+backsql_name2at( backsql_oc_map_rec* objclass, struct berval *attr )
+{
+       backsql_at_map_rec      tmp, *res;
+       const char              *text = NULL;
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_name2at(): "
+               "searching for attribute '%s' for objectclass '%s'\n",
+               attr, objclass->name, 0 );
+#endif
+
+       if ( slap_bv2ad( attr, &tmp.ad, &text ) != LDAP_SUCCESS ) {
+               return NULL;
+       }
+
+       res = (backsql_at_map_rec *)avl_find( objclass->attrs, &tmp,
+                       (AVL_CMP)backsql_cmp_attr );
+
+#if 0
+       if ( res != NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_name2at(): "
+                       "found name='%s', sel_expr='%s'\n",
+                       res->name, res->sel_expr, 0 );
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<==backsql_name2at(): "
+                       "not found\n", 0, 0, 0 );
+       }
+#endif
+
+       return res;
+}
+
+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 );
+       }
+       if ( at->join_where != NULL ) {
+               ch_free( at->join_where );
+       }
+       if ( at->add_proc != NULL ) {
+               ch_free( at->add_proc );
+       }
+       if ( at->delete_proc != NULL ) {
+               ch_free( at->delete_proc );
+       }
+       if ( at->query ) {
+               ch_free( at->query );
+       }
+
+       /* TimesTen */
+       if ( at->sel_expr_u ) {
+               ch_free( at->sel_expr_u );
+       }
+       
+       ch_free( at );
+
+       Debug( LDAP_DEBUG_TRACE, "<==free_attr()\n", 0, 0, 0 );
+}
+
+static void
+backsql_free_oc( backsql_oc_map_rec *oc )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>free_oc(): '%s'\n", 
+                       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 );
+       if ( oc->create_proc != NULL ) {
+               ch_free( oc->create_proc );
+       }
+       if ( oc->delete_proc != NULL ) {
+               ch_free( oc->delete_proc );
+       }
+       ch_free( oc );
+
+       Debug( LDAP_DEBUG_TRACE, "<==free_oc()\n", 0, 0, 0 );
+}
+
+int
+backsql_destroy_schema_map( backsql_info *si )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>destroy_schema_map()\n", 0, 0, 0 );
+       avl_free( si->oc_by_oc, NULL );
+       avl_free( si->oc_by_id, (AVL_FREE)backsql_free_oc );
+       Debug( LDAP_DEBUG_TRACE, "<==destroy_schema_map()\n", 0, 0, 0 );
+       return 0;
+}
+
+#endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/schema-map.h b/servers/slapd/back-sql/schema-map.h
new file mode 100644 (file)
index 0000000..27f0727
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef __BACKSQL_SCHEMA_MAP_H__
+#define __BACKSQL_SCHEMA_MAP_H__
+
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+
+typedef struct {
+       struct berval   name;
+       ObjectClass     *oc;
+       char            *keytbl;
+       char            *keycol;
+       /* expected to return keyval of newly created entry */
+       char            *create_proc;
+       /* supposed to expect keyval as parameter and delete 
+        * all the attributes as well */
+       char            *delete_proc;
+       /* flags whether delete_proc is a function (whether back-sql 
+        * should bind first parameter as output for return code) */
+       int             expect_return;
+       unsigned long   id;
+       Avlnode         *attrs;
+} backsql_oc_map_rec;
+
+typedef struct {
+       /* literal name of corresponding LDAP attribute type */
+       struct berval   name;
+       AttributeDescription    *ad;
+       char            *from_tbls;
+       char            *join_where;
+       char            *sel_expr;
+       /* supposed to expect 2 binded values: entry keyval 
+        * and attr. value to add, like "add_name(?,?,?)" */
+       char            *add_proc;
+       /* supposed to expect 2 binded values: entry keyval 
+        * and attr. value to delete */
+       char            *delete_proc;
+       /* for optimization purposes attribute load query 
+        * 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) */
+       /* order of parameters for procedures above; 
+        * 1 means "data then keyval", 0 means "keyval then data" */
+       int             param_order;
+       /* flags whether one or more of procedures is a function 
+        * (whether back-sql should bind first parameter as output 
+        * for return code) */
+       int             expect_return;
+       /* TimesTen */
+       char            *sel_expr_u;
+} backsql_at_map_rec;
+
+/* defines to support bitmasks above */
+#define BACKSQL_ADD    0x1
+#define BACKSQL_DEL    0x2
+
+#define BACKSQL_IS_ADD(x)      ( BACKSQL_ADD & (x) )
+#define BACKSQL_IS_DEL(x)      ( BACKSQL_DEL & (x) )
+
+#define BACKSQL_NCMP(v1,v2)    ber_bvcmp((v1),(v2))
+
+int backsql_load_schema_map( backsql_info *si, SQLHDBC dbh );
+/* Deprecated */
+backsql_oc_map_rec *backsql_name2oc( backsql_info *si, struct berval *oc_name );
+backsql_oc_map_rec *backsql_oc2oc( backsql_info *si, ObjectClass *oc );
+backsql_oc_map_rec *backsql_id2oc( backsql_info *si, unsigned long id );
+/* Deprecated */
+backsql_at_map_rec *backsql_name2at( backsql_oc_map_rec *objclass,
+               struct berval *at_name );
+backsql_at_map_rec *backsql_ad2at( backsql_oc_map_rec *objclass,
+               AttributeDescription *ad );
+int backsql_destroy_schema_map( backsql_info *si );
+
+#endif /* __BACKSQL_SCHEMA_MAP_H__ */
+
index 2dca484832374d894fbdfefd902a7cf738688a0f..b8f5e143b0cb368e1fb967405f28f4f15975e7b5 100644 (file)
 
 #include <stdio.h>
 #include <sys/types.h>
-#include <string.h>
+#include "ac/string.h"
 #include "slap.h"
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
 #include "back-sql.h"
 #include "sql-wrap.h"
 #include "schema-map.h"
 #include "entry-id.h"
 #include "util.h"
 
-int backsql_attrlist_add(backsql_srch_info *bsi,char *at_name)
+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 )
 {
- char **p=bsi->attrs;
- int n_attrs=0;
-
- if (bsi->attrs==NULL)
-  return 1;
-
- while(*p)
- {
-  Debug(LDAP_DEBUG_TRACE,"==>backsql_attrlist_add(): attribute '%s' is in list\n",*p,0,0);
-  if (!strcasecmp(*p,at_name))
-   return 1;
-  n_attrs++;
-  p++;
- }
- Debug(LDAP_DEBUG_TRACE,"==>backsql_attrlist_add(): adding '%s' to list\n",at_name,0,0);
- bsi->attrs=(char**)ch_realloc(bsi->attrs,(n_attrs+2)*sizeof(char*));
- bsi->attrs[n_attrs]=ch_strdup(at_name);
- bsi->attrs[n_attrs+1]=NULL;
- return 1;
+       int             n_attrs = 0;
+       AttributeName   *an = NULL;
+
+       if ( bsi->attrs == NULL ) {
+               return 1;
+       }
+
+       for ( ; bsi->attrs[ n_attrs ].an_name.bv_val; n_attrs++ ) {
+               an = &bsi->attrs[ n_attrs ];
+               
+               Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
+                       "attribute '%s' is in list\n", 
+                       an->an_name.bv_val, 0, 0 );
+               /*
+                * We can live with strcmp because the attribute 
+                * list has been normalized before calling be_search
+                */
+               if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
+                       return 1;
+               }
+       }
+       
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
+               "adding '%s' to list\n", ad->ad_cname.bv_val, 0, 0 );
+
+       an = (AttributeName *)ch_realloc( bsi->attrs,
+                       sizeof( AttributeName ) * ( n_attrs + 2 ) );
+       if ( an == NULL ) {
+               return -1;
+       }
+
+       an[ n_attrs ].an_name = ad->ad_cname;
+       an[ n_attrs ].an_desc = ad;
+       an[ n_attrs + 1 ].an_name.bv_val = NULL;
+       an[ n_attrs + 1 ].an_name.bv_len = 0;
+
+       bsi->attrs = an;
+       
+       return 1;
 }
 
-void backsql_init_search(backsql_srch_info *bsi,backsql_info *bi,char *nbase,int scope,
-                                                int slimit,int tlimit,time_t stoptime,Filter *filter,
-                                                SQLHDBC dbh,BackendDB *be,Connection *conn,Operation *op,AttributeName *attrs)
+void
+backsql_init_search(
+       backsql_srch_info       *bsi, 
+       backsql_info            *bi,
+       struct berval           *nbase, 
+       int                     scope, 
+       int                     slimit,
+       int                     tlimit,
+       time_t                  stoptime, 
+       Filter                  *filter, 
+       SQLHDBC                 dbh,
+       BackendDB               *be, 
+       Connection              *conn, 
+       Operation               *op,
+       AttributeName           *attrs )
 {
- AttributeName *p;
- bsi->base_dn=nbase;
- bsi->scope=scope;
- bsi->slimit=slimit;
- bsi->tlimit=tlimit;
- bsi->filter=filter;
- bsi->dbh=dbh;
- bsi->be=be;
- bsi->conn=conn;
- bsi->op=op;
- if (attrs!=NULL)
- {
-  bsi->attrs=(char**)ch_calloc(1,sizeof(char*));
-  bsi->attrs[0]=NULL;
-  for(p=attrs;p->an_name.bv_val;p++)
-   backsql_attrlist_add(bsi,p->an_name.bv_val);
- }
- else
-  bsi->attrs=NULL;
- bsi->abandon=0;
- bsi->id_list=NULL;
- bsi->stoptime=stoptime;
- bsi->bi=bi;
- bsi->sel=NULL; bsi->from=NULL; bsi->join_where=NULL; bsi->flt_where=NULL;
- bsi->sel_len=0; bsi->from_len=0; bsi->jwhere_len=0; bsi->fwhere_len=0;
+       AttributeName           *p;
+       
+       bsi->base_dn = nbase;
+       bsi->scope = scope;
+       bsi->slimit = slimit;
+       bsi->tlimit = tlimit;
+       bsi->filter = filter;
+       bsi->dbh = dbh;
+       bsi->be = be;
+       bsi->conn = conn;
+       bsi->op = op;
+
+       /*
+        * FIXME: need to discover how to deal with 1.1 (NoAttrs)
+        */
+       
+       /*
+        * handle "*"
+        */
+       if ( attrs == NULL || an_find( attrs, &AllUser ) ) {
+               bsi->attrs = NULL;
+
+       } else {
+               bsi->attrs = (AttributeName *)ch_calloc( 1, 
+                               sizeof( AttributeName ) );
+               bsi->attrs[ 0 ].an_name.bv_val = NULL;
+               bsi->attrs[ 0 ].an_name.bv_len = 0;
+               
+               for ( p = attrs; p->an_name.bv_val; p++ ) {
+                       /*
+                        * ignore "+"
+                        */
+                       if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 
+                                       || BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
+                               continue;
+                       }
+
+                       backsql_attrlist_add( bsi, p->an_desc );
+               }
+       }
+
+       bsi->abandon = 0;
+       bsi->id_list = NULL;
+       bsi->n_candidates = 0;
+       bsi->stoptime = stoptime;
+       bsi->bi = bi;
+       bsi->sel.bv_val = NULL;
+       bsi->sel.bv_len = 0;
+       bsi->sel_len = 0;
+       bsi->from.bv_val = NULL;
+       bsi->from.bv_len = 0;
+       bsi->from_len = 0;
+       bsi->join_where.bv_val = NULL;
+       bsi->join_where.bv_len = 0;
+       bsi->jwhere_len = 0;
+       bsi->flt_where.bv_val = NULL;
+       bsi->flt_where.bv_len = 0;
+       bsi->fwhere_len = 0;
+
+       bsi->status = LDAP_SUCCESS;
 }
 
-int backsql_process_filter_list(backsql_srch_info *bsi,Filter *f,int op)
+int
+backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
 {
- char *sub_clause=NULL;
- int len=0,res;
-
- if (!f)
-  return 0;
- bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",NULL);
- while(1)
- {
-  res=backsql_process_filter(bsi,f);
-  if (res < 0)
-    return -1;    /* TimesTen : If the query has no answers,
-                   don't bother to run the query. */
+       int             res;
+
+       if ( !f ) {
+               return 0;
+       }
+       
+       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, "(", NULL );
+
+       while ( 1 ) {
+               res = backsql_process_filter( bsi, f );
+               if ( res < 0 ) {
+                       /*
+                        * TimesTen : If the query has no answers,
+                        * don't bother to run the query.
+                        */
+                       return -1;
+               }
  
-  f=f->f_next;
-  if (f==NULL)
-   break;
-
-  switch (op)
-  {
-   case LDAP_FILTER_AND:
-                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," AND ",NULL);
+               f = f->f_next;
+               if ( f == NULL ) {
                        break;
-   case LDAP_FILTER_OR:
-                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," OR ",NULL);
+               }
+
+               switch ( op ) {
+               case LDAP_FILTER_AND:
+                       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                                       " AND ", NULL );
                        break;
-  }
- }
 
- bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,")",NULL);
- return 1;
+               case LDAP_FILTER_OR:
+                       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                                       " OR ", NULL );
+                       break;
+               }
+       }
+
+       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, /* ( */ ")", NULL );
+
+       return 1;
 }
 
-int backsql_process_sub_filter(backsql_srch_info *bsi,Filter *f)
+int
+backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f )
 {
- int i;
- backsql_at_map_rec *at=backsql_at_with_name(bsi->oc,f->f_sub_desc->ad_cname.bv_val);
+       int                     i;
+       backsql_at_map_rec      *at;
+
+       if ( !f ) {
+               return 0;
+       }
+
+       at = backsql_ad2at( bsi->oc, f->f_sub_desc );
+
+       /*
+        * 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 );
+
+       /* 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 ) {
+               /*
+                * 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);
+               } else {
+                       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                                       bsi->bi->upper_func,
+                                       "(", at->sel_expr, ")", 
+                                       " LIKE '", NULL );
+               }
+       } else {
+               backsql_strcat( &bsi->flt_where, &bsi->fwhere_len,
+                               at->sel_expr, " LIKE '", NULL );
+       }
  
- if (!f)
-  return 0;
-
- bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",NULL);
- /* 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)
- {
-   /* If a pre-upper-cased version of the column exists, use it. */
-   if (at->sel_expr_u) {
-     bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,
-                   at->sel_expr_u," LIKE '",NULL);
-   }
-   else {
-      bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,
-            bsi->bi->upper_func,"(",at->sel_expr,")",
-                " LIKE '",NULL);
-   }
- }
- else
- {
-  bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,at->sel_expr,
-                               " LIKE '",NULL);
- }
- if (f->f_sub_initial!=NULL)
- {
-  if (bsi->bi->upper_func)
-   {
-    bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,ldap_pvt_str2upper(f->f_sub_initial->bv_val),NULL);
-   }
-   else
-    bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_initial->bv_val,NULL);
- }
-
- bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"%",NULL);
-
- if (f->f_sub_any!=NULL)
-  for(i=0;f->f_sub_any[i]!=NULL;i++)
-  {
-   /*Debug(LDAP_DEBUG_TRACE,"==>backsql_process_sub_filter(): sub_any='%s'\n",f->f_sub_any[i]->bv_val,0,0);*/
-   if (bsi->bi->upper_func)
-   {
-    bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,ldap_pvt_str2upper(f->f_sub_any[i]->bv_val),"%",NULL);
-   }
-   else
-    bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_any[i]->bv_val,"%",NULL);    
-  }
-
- if (f->f_sub_final!=NULL)
-  if (bsi->bi->upper_func)
-   {
-    bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,ldap_pvt_str2upper(f->f_sub_final->bv_val),NULL);
-   }
-   else
-    bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_final->bv_val,NULL);
-
- bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"')",NULL);
+       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 ) {
+                       ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
+               }
+       }
+
+       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, "%", NULL );
+
+       if ( f->f_sub_any != NULL ) {
+               for ( i = 0; f->f_sub_any[ i ].bv_val != NULL; i++ ) {
+                       size_t  start;
+
+#if 0
+                       Debug( LDAP_DEBUG_TRACE, 
+                               "==>backsql_process_sub_filter(): "
+                               "sub_any='%s'\n", f->f_sub_any[ i ].bv_val,
+                               0, 0 );
+#endif
+
+                       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) {
+                               /*
+                                * Note: toupper('%') = '%'
+                                */
+                               ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
+                       }
+               }
+
+               if ( f->f_sub_final.bv_val != NULL ) {
+                       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 ) {
+                               ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
+                       }
+               }
+       }
+
+       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, /* ( */ "')", NULL );
  
- return 1;
      return 1;
 }
 
-int backsql_process_filter(backsql_srch_info *bsi,Filter *f)
+int
+backsql_process_filter( backsql_srch_info *bsi, Filter *f )
 {
- backsql_at_map_rec *at;
- backsql_at_map_rec oc_attr={"objectClass","","",NULL,NULL,NULL,NULL};
- char *at_name=NULL;
- int done=0,len=0;
- int rc=0; /* TimesTen */
-
- Debug(LDAP_DEBUG_TRACE,"==>backsql_process_filter()\n",0,0,0);
- if (f==NULL || f->f_choice==SLAPD_FILTER_COMPUTED)
-  {
-   return 0;
-  }
-  
- switch(f->f_choice)
- {
-  case LDAP_FILTER_OR:
-                       rc = backsql_process_filter_list(bsi,f->f_or,LDAP_FILTER_OR);
-                       done=1;
-                       break;
-  case LDAP_FILTER_AND:
-                       rc = backsql_process_filter_list(bsi,f->f_and,LDAP_FILTER_AND);
-                       done=1;
-                       break;
-  case LDAP_FILTER_NOT:
-                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"NOT (",NULL);
-                       rc = backsql_process_filter(bsi,f->f_not);
-                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,")",NULL);
-                       done=1;
-                       break;
-  case LDAP_FILTER_PRESENT:
-                       at_name=f->f_desc->ad_cname.bv_val;
-                       break;
-  default:
-                        at_name=f->f_av_desc->ad_cname.bv_val;
-                       break;
- }
+       backsql_at_map_rec      *at;
+       backsql_at_map_rec      oc_attr = { BER_BVC("objectClass"),
+               slap_schema.si_ad_objectClass, "", "", NULL, NULL, NULL, NULL };
+       AttributeDescription    *ad = NULL;
+       int                     done = 0, len = 0;
+       /* TimesTen */
+       int                     rc = 0;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
+       if ( f == NULL || f->f_choice == SLAPD_FILTER_COMPUTED ) {
+               return 0;
+       }
+
+       switch( f->f_choice ) {
+       case LDAP_FILTER_OR:
+               rc = backsql_process_filter_list( bsi, f->f_or, 
+                               LDAP_FILTER_OR );
+               done = 1;
+               break;
+               
+       case LDAP_FILTER_AND:
+               rc = backsql_process_filter_list( bsi, f->f_and,
+                               LDAP_FILTER_AND);
+               done = 1;
+               break;
 
- if (rc == -1)
-   goto impossible;     /* TimesTen : Don't run the query */
+       case LDAP_FILTER_NOT:
+               backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                               "NOT (", NULL );
+               rc = backsql_process_filter( bsi, f->f_not );
+               backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, ")", NULL );
+               done = 1;
+               break;
+
+       case LDAP_FILTER_PRESENT:
+               ad = f->f_desc;
+               break;
+               
+       default:
+               ad = f->f_av_desc;
+               break;
+       }
+
+       if ( rc == -1 ) {
+               /* TimesTen : Don't run the query */
+               goto impossible;
+       }
  
- if (done)
-  goto done;
-
- if (strcasecmp(at_name,"objectclass"))
-  at=backsql_at_with_name(bsi->oc,at_name);
- else
- {
-  at=&oc_attr;
-  at->sel_expr=backsql_strcat(at->sel_expr,&len,"'",bsi->oc->name,"'",NULL);
- }
- if (at==NULL)
- {
-  Debug(LDAP_DEBUG_TRACE,"backsql_process_filter(): attribute '%s' is not defined for objectclass '%s'\n",
-                      at_name,bsi->oc->name,0);
-  bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," 1=0 ",NULL);
-  goto impossible;
- }
-                       
- 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,at_name);
+       if ( done ) {
+               goto done;
+       }
+
+       if ( strcasecmp( ad->ad_cname.bv_val, "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;
+       }
+       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 );
+               goto impossible;
+       }
+
+       backsql_merge_from_clause( &bsi->from.bv_val, &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 0  
+       if ( at != &oc_attr ) {
+               backsql_strcat( &bsi->sel, &bsi->sel_len,
+                               ",", at->sel_expr, " AS ", 
+                               at->name.bv_val, NULL );
+       }
+#endif
+
+       switch ( f->f_choice ) {
+       case LDAP_FILTER_EQUALITY:
+               /*
+                * maybe we should check type of at->sel_expr here somehow,
+                * to know whether upper_func is applicable, but for now
+                * upper_func stuff is made for Oracle, where UPPER is
+                * safely applicable to NUMBER etc.
+                */
+               if ( bsi->bi->upper_func ) {
+                       size_t  start;
+
+                       if ( at->sel_expr_u ) {
+                               backsql_strcat( &bsi->flt_where,
+                                               &bsi->fwhere_len, "(",
+                                               at->sel_expr_u, "='", NULL );
+                       } else {
+                               backsql_strcat( &bsi->flt_where,
+                                               &bsi->fwhere_len, "(",
+                                               bsi->bi->upper_func, "(",
+                                               at->sel_expr, ")='", NULL );
+                       }
 
- if (at->join_where != NULL && strstr(bsi->join_where,at->join_where)==NULL)
-  bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len," AND ",at->join_where,NULL);
+                       start = bsi->flt_where.bv_len;
 
- /*if (at!=&oc_attr)
-  bsi->sel=backsql_strcat(bsi->sel,&bsi->sel_len,",",at->sel_expr," AS ",at->name,NULL);
- */
+                       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                                       f->f_av_value.bv_val, "')", NULL );
 
- switch(f->f_choice)
- {
-  case LDAP_FILTER_EQUALITY:
-                       /*maybe we should check type of at->sel_expr here somehow,
-                       * to know whether upper_func is applicable, but for now
-                       * upper_func stuff is made for Oracle, where UPPER is
-                       * safely applicable to NUMBER etc.
-                       */
-                       if (bsi->bi->upper_func) {
-                               if (at->sel_expr_u)
-                                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",
-                      at->sel_expr_u,"='",
-                      ldap_pvt_str2upper(f->f_av_value->bv_val),"')",
-                      NULL);
-                       else
-                                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",
-                                       bsi->bi->upper_func,"(",at->sel_expr,")='",
-                                       ldap_pvt_str2upper(f->f_av_value->bv_val),"')",NULL);
-                       }
-                       else
-                        bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,"='",
-                                                       f->f_av_value->bv_val,"')",NULL);
+                       ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
 
-                       break;
-  case LDAP_FILTER_GE:
-                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,">=",
-                                                       f->f_av_value->bv_val,")",NULL);
+               } else {
+                       backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                                       "(", at->sel_expr, "='",
+                                       f->f_av_value.bv_val, "')", NULL );
+               }
+               break;
 
-                       break;
-  case LDAP_FILTER_LE:
-                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,"<=",
-                                                       f->f_av_value->bv_val,")",NULL);
-                       break;
-  case LDAP_FILTER_PRESENT:
-                       bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"NOT (",at->sel_expr,
-                                               " IS NULL)",NULL);
-                       break;
-  case LDAP_FILTER_SUBSTRINGS:
-                       backsql_process_sub_filter(bsi,f);
-                       break;
- }
+       case LDAP_FILTER_GE:
+               backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                               "(", at->sel_expr, ">=", 
+                               f->f_av_value.bv_val, ")", NULL );
+               break;
+               
+       case LDAP_FILTER_LE:
+               backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                               "(", at->sel_expr, "<=", 
+                               f->f_av_value.bv_val, ")", NULL );
+               break;
+
+       case LDAP_FILTER_PRESENT:
+               backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, 
+                               "NOT (", at->sel_expr, " IS NULL)", NULL );
+               break;
+
+       case LDAP_FILTER_SUBSTRINGS:
+               backsql_process_sub_filter( bsi, f );
+               break;
+       }
 
 done:
- if (oc_attr.sel_expr!=NULL)
-  free(oc_attr.sel_expr);
- Debug(LDAP_DEBUG_TRACE,"<==backsql_process_filter()\n",0,0,0);
- return 1;
+       if ( oc_attr.sel_expr != NULL ) {
+               free( oc_attr.sel_expr );
+       }
+       
+       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);
- Debug(LDAP_DEBUG_TRACE,"<==backsql_process_filter() returns -1\n",0,0,0);
- return -1;
-
+       if ( oc_attr.sel_expr != NULL ) {
+               free( oc_attr.sel_expr );
+       }
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter() returns -1\n",
+                       0, 0, 0 );
+       return -1;
 }
 
-char* backsql_srch_query(backsql_srch_info *bsi)
+static int
+backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
 {
- char *query=NULL;
- int q_len=0;
- int rc;
-
- Debug(LDAP_DEBUG_TRACE,"==>backsql_srch_query()\n",0,0,0);
- bsi->sel=NULL;
- bsi->from=NULL;
- bsi->join_where=NULL;
- bsi->flt_where=NULL;
- bsi->sel_len=bsi->from_len=bsi->jwhere_len=bsi->fwhere_len=0;
-
- bsi->sel=backsql_strcat(bsi->sel,&bsi->sel_len,
-                               "SELECT DISTINCT ldap_entries.id,",bsi->oc->keytbl,".",bsi->oc->keycol,
-                               ", '",bsi->oc->name,"' AS objectClass",
-                               ", ldap_entries.dn AS dn",
-                               NULL);
- bsi->from=backsql_strcat(bsi->from,&bsi->from_len," FROM ldap_entries,",bsi->oc->keytbl,NULL);
- bsi->join_where=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);
-
- switch(bsi->scope)
- {
-  case LDAP_SCOPE_BASE:
-        if (bsi->bi->upper_func)
-                {
-                 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
-                        bsi->bi->upper_func,"(","ldap_entries.dn)=(?)",NULL);
-                }
-               else
-                {
-                 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
-                               "ldap_entries.dn=?",NULL);
-                }
+       backsql_info    *bi = (backsql_info *)bsi->be->be_private;
+       int             q_len = 0;
+       int             rc;
+
+       assert( query );
+       query->bv_val = NULL;
+       query->bv_len = 0;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
+       bsi->sel.bv_val = NULL;
+       bsi->sel.bv_len = 0;
+       bsi->sel_len = 0;
+       bsi->from.bv_val = NULL;
+       bsi->from.bv_len = 0;
+       bsi->from_len = 0;
+       bsi->join_where.bv_val = NULL;
+       bsi->join_where.bv_len = 0;
+       bsi->jwhere_len = 0;
+       bsi->flt_where.bv_val = NULL;
+       bsi->flt_where.bv_len = 0;
+       bsi->fwhere_len = 0;
+#if 0
+       backsql_strcat( &bsi->sel, &bsi->sel_len,
+                       "SELECT DISTINCT ldap_entries.id,", 
+                       bsi->oc->keytbl, ".", bsi->oc->keycol,
+                       ",'", 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 );
+       } else {
+               backsql_strcat( &bsi->sel, &bsi->sel_len,
+                               "'", bsi->oc->name.bv_val, "'", NULL );
+       }
+       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 );
+
+       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 );
+               } else {
+                       backsql_strcat( &bsi->join_where, &bsi->jwhere_len, 
+                                       "ldap_entries.dn=?", NULL );
+               }
                break;
-  case LDAP_SCOPE_ONELEVEL:
-               bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
-                               "ldap_entries.parent=?",NULL);
+               
+       case LDAP_SCOPE_ONELEVEL:
+               backsql_strcat( &bsi->join_where, &bsi->jwhere_len, 
+                               "ldap_entries.parent=?", NULL );
                break;
-  case LDAP_SCOPE_SUBTREE:
-               bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
-                               bsi->bi->subtree_cond,NULL);
+
+       case LDAP_SCOPE_SUBTREE:
+               backsql_strcat( &bsi->join_where, &bsi->jwhere_len,
+                               bsi->bi->subtree_cond, NULL );
                break;
- }
-
- rc = backsql_process_filter(bsi, bsi->filter);
- if (rc>0) {
-  query=backsql_strcat(query,&q_len,bsi->sel,bsi->from,bsi->join_where," AND ",bsi->flt_where,NULL);
- }
- else if (rc < 0) {
-    /* Indicates that there's no possible way the filter matches
-      anything.  No need to issue the query. */
-
-   Debug(LDAP_DEBUG_TRACE,"<==backsql_srch_query() returns NULL\n",0,0,0);
-   free(query);
-   query = NULL;
- }
+
+       default:
+               assert( 0 );
+       }
+
+       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 );
+
+       } else if ( rc < 0 ) {
+               /* 
+                * Indicates that there's no possible way the filter matches
+                * anything.  No need to issue the query
+                */
+               Debug( LDAP_DEBUG_TRACE,
+                       "<==backsql_srch_query() returns NULL\n", 0, 0, 0 );
+               free( query->bv_val );
+               query->bv_val = NULL;
+       }
  
- free(bsi->sel);
- free(bsi->from);
- free(bsi->join_where);
- free(bsi->flt_where);
- bsi->sel_len=bsi->from_len=bsi->jwhere_len=bsi->fwhere_len=0;
- Debug(LDAP_DEBUG_TRACE,"<==backsql_srch_query()\n",0,0,0);
- return query;
+       free( bsi->sel.bv_val );
+       bsi->sel.bv_len = 0;
+       bsi->sel_len = 0;
+       free( bsi->from.bv_val );
+       bsi->from.bv_len = 0;
+       bsi->from_len = 0;
+       free( bsi->join_where.bv_val );
+       bsi->join_where.bv_len = 0;
+       bsi->jwhere_len = 0;
+       free( bsi->flt_where.bv_val );
+       bsi->flt_where.bv_len = 0;
+       bsi->fwhere_len = 0;
+       
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query()\n", 0, 0, 0 );
+       
+       return ( query->bv_val == NULL ? 1 : 0 );
 }
 
-int backsql_oc_get_candidates(backsql_oc_map_rec *oc,backsql_srch_info *bsi)
+int
+backsql_oc_get_candidates( backsql_oc_map_rec *oc, backsql_srch_info *bsi )
 {
- char *query=NULL;
- SQLHSTMT sth;
- RETCODE rc;
- backsql_entryID base_id,*res,*c_id;
- /*Entry *e;*/
- BACKSQL_ROW_NTS row;
- int i;
- int j;
- char temp_base_dn[BACKSQL_MAX_DN_LEN+1]; /* TimesTen*/
+       struct berval           query;
+       SQLHSTMT                sth;
+       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,0,0);
- bsi->oc=oc;
- query=backsql_srch_query(bsi);
- if (query==NULL)
- {
-  Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): could not construct query for objectclass\n",0,0,0);
-  return 1;
- }
-
- Debug(LDAP_DEBUG_TRACE,"Constructed query: %s\n",query,0,0);
-
- if ((rc=backsql_Prepare(bsi->dbh,&sth,query,0)) != SQL_SUCCESS)
-  {
-   Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error preparing query\n",0,0,0);
-   backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
-   free(query);
-   return 1;
-  }
- free(query);
-
- if (backsql_BindParamID(sth,1,&bsi->oc->id) != SQL_SUCCESS)
- {
-  Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding objectclass id parameter\n",0,0,0);
-  return 1;
- }
- switch(bsi->scope)
- {
-  case LDAP_SCOPE_BASE:
-               if ((rc=backsql_BindParamStr(sth,2,bsi->base_dn,BACKSQL_MAX_DN_LEN)) != SQL_SUCCESS)
-               {
-         Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding base_dn parameter\n",0,0,0);
-                backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
-         return 1;
+       Debug(  LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc='%s'\n",
+                       oc->name.bv_val, 0, 0 );
+       bsi->oc = oc;
+       if ( backsql_srch_query( bsi, &query ) ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+                       "could not construct query for objectclass\n",
+                       0, 0, 0 );
+               return 1;
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n", 
+                       query.bv_val, 0, 0 );
+
+       rc = backsql_Prepare( bsi->dbh, &sth, query.bv_val, 0 );
+       free( query.bv_val );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+                       "error preparing query\n", 0, 0, 0 );
+               backsql_PrintErrors( bsi->bi->db_env, bsi->dbh, sth, rc );
+               return 1;
+       }
+
+       if ( backsql_BindParamID( sth, 1, &bsi->oc->id ) != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+                       "error binding objectclass id parameter\n", 0, 0, 0 );
+               return 1;
+       }
+
+       switch ( bsi->scope ) {
+       case LDAP_SCOPE_BASE:
+               rc = backsql_BindParamStr( sth, 2, bsi->base_dn->bv_val,
+                               BACKSQL_MAX_DN_LEN );
+               if ( rc != SQL_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+                               "error binding base_dn parameter\n", 0, 0, 0 );
+                       backsql_PrintErrors( bsi->bi->db_env, bsi->dbh, 
+                                       sth, rc );
+                       return 1;
                }
                break;
 
-  case LDAP_SCOPE_SUBTREE:
-    /* Sets the parameters for the SQL built earlier */
-    /* NOTE that all the databases could actually use the TimesTen version,
-       which would be cleaner and would also eliminate the need for the
-       subtree_cond line in the configuration file.  For now, I'm leaving
-       it the way it is, so non-TimesTen databases use the original code.
-       But at some point this should get cleaned up. */
-     /* If "dn" is being used, do a suffix search.
-     If "dn_ru" is being used, do a prefix search. */
-
-    if (bsi->bi->has_ldapinfo_dn_ru) {
-       temp_base_dn[0] = '\0';
-       for ((i=0, j=strlen(bsi->base_dn)-1); j >= 0; (i++, j--)) {
-         *(temp_base_dn+i) = toupper(*(bsi->base_dn+j));
-       }
-       *(temp_base_dn+i) = '%';
-       *(temp_base_dn+i+1) = '\0';
-    }
-    else {
-        strcpy(temp_base_dn, "%");
-        for (i = 0; *(bsi->base_dn+i); i++) {
-          *(temp_base_dn+i+1) = toupper(*(bsi->base_dn+i));
-        }
-        *(temp_base_dn+i+1) = '\0';
-    }
-    Debug(LDAP_DEBUG_TRACE, "dn '%s'\n", temp_base_dn, 0, 0);
-
-    if ((rc=backsql_BindParamStr(sth,2,temp_base_dn,BACKSQL_MAX_DN_LEN)) !=
-SQL_SUCCESS)
-    {
-         Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding base_dn parameter (2)\n",0,0,0);
-         backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
-         return 1;
-    }
-       break;
-
-  case LDAP_SCOPE_ONELEVEL:
-               res=backsql_dn2id(bsi->bi,&base_id,bsi->dbh,bsi->base_dn);
-               if (res==NULL)
-               {
-                Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): could not retrieve base_dn id - no such entry\n",0,0,0);
-                bsi->status=LDAP_NO_SUCH_OBJECT;
-                return 0;
+       case LDAP_SCOPE_SUBTREE:
+               /* 
+                * Sets the parameters for the SQL built earlier
+                * NOTE that all the databases could actually use 
+                * the TimesTen version, which would be cleaner 
+                * and would also eliminate the need for the
+                * subtree_cond line in the configuration file.  
+                * For now, I'm leaving it the way it is, 
+                * so non-TimesTen databases use the original code.
+                * But at some point this should get cleaned up.
+                *
+                * If "dn" is being used, do a suffix search.
+                * If "dn_ru" is being used, do a prefix search.
+                */
+               if ( bsi->bi->has_ldapinfo_dn_ru ) {
+                       temp_base_dn[ 0 ] = '\0';
+                       for ( i = 0, j = bsi->base_dn->bv_len - 1;
+                                       j >= 0; i++, j--) {
+                               temp_base_dn[ i ] = bsi->base_dn->bv_val[ j ];
+                       }
+                       temp_base_dn[ i ] = '%';
+                       temp_base_dn[ i + 1 ] = '\0';
+                       ldap_pvt_str2upper( temp_base_dn );
+
+               } else {
+                       temp_base_dn[ 0 ] = '%';
+                       AC_MEMCPY( &temp_base_dn[ 1 ], bsi->base_dn->bv_val,
+                               bsi->base_dn->bv_len + 1 );
+                       ldap_pvt_str2upper( &temp_base_dn[ 1 ] );
+               }
+
+               Debug( LDAP_DEBUG_TRACE, "dn '%s'\n", temp_base_dn, 0, 0 );
+
+               rc = backsql_BindParamStr( sth, 2, temp_base_dn, 
+                               BACKSQL_MAX_DN_LEN );
+               if ( rc != SQL_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+                               "error binding base_dn parameter (2)\n",
+                               0, 0, 0 );
+                       backsql_PrintErrors( bsi->bi->db_env, bsi->dbh, 
+                                       sth, rc );
+                       return 1;
                }
-               if (backsql_BindParamID(sth,2,&base_id.id) != SQL_SUCCESS)
-               {
-                Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding base id parameter\n",0,0,0);
-                free(base_id.dn);
-                return 1;
-               }               
-               free(base_id.dn);
                break;
- }
- if ((rc=SQLExecute(sth)) != SQL_SUCCESS && rc!= SQL_SUCCESS_WITH_INFO)
-  {
-   Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error executing query\n",0,0,0);
-   backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
-   SQLFreeStmt(sth,SQL_DROP);
-   return 1;
-  }
-
- backsql_BindRowAsStrings(sth,&row);
- while ((rc=SQLFetch(sth)) == SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO)
-  {
-   /*
-   e=(Entry*)ch_calloc(1,sizeof(Entry)); 
-   for (i=1;i<row.ncols;i++)
-    {
-     if (row.is_null[i]>0)
-      {
-       backsql_entry_addattr(e,row.col_names[i],row.cols[i],row.col_prec[i]);
-       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);
-    }
-   */
-
-   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->oc_id=bsi->oc->id;
-   c_id->dn=ch_strdup(row.cols[3]);
-   c_id->next=bsi->id_list;
-   bsi->id_list=c_id;
-   Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): added entry id=%d, keyval=%d dn='%s'\n",
-               c_id->id,c_id->keyval,row.cols[3]);
-  }
- backsql_FreeRow(&row);
- SQLFreeStmt(sth,SQL_DROP);
-
- Debug(LDAP_DEBUG_TRACE,"<==backsql_oc_get_candidates()\n",0,0,0);
- return 1;
+
+       case LDAP_SCOPE_ONELEVEL:
+               res = backsql_dn2id( bsi->bi, &base_id, 
+                               bsi->dbh, bsi->base_dn );
+               if ( res != LDAP_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+                               "could not retrieve base_dn id%s\n",
+                               res == LDAP_NO_SUCH_OBJECT ? ": no such entry"
+                               : "", 0, 0 );
+                       bsi->status = res;
+                       return 0;
+               }
+               
+               rc = backsql_BindParamID( sth, 2, &base_id.id );
+               backsql_free_entryID( &base_id, 0 );
+               if ( rc != SQL_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+                               "error binding base id parameter\n", 0, 0, 0 );
+                       return 1;
+               }
+               break;
+       }
+       
+       rc = SQLExecute( sth );
+       if ( !BACKSQL_SUCCESS( rc ) ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+                       "error executing query\n", 0, 0, 0 );
+               backsql_PrintErrors( bsi->bi->db_env, bsi->dbh, sth, rc );
+               SQLFreeStmt( sth, SQL_DROP );
+               return 1;
+       }
+
+       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->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++;
+               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 ] );
+       }
+       backsql_FreeRow( &row );
+       SQLFreeStmt( sth, SQL_DROP );
+
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates()\n", 0, 0, 0 );
+
+       return 1;
 }
 
-int backsql_search(BackendDB *be,Connection *conn,Operation *op,
-       const char *base, const char *nbase, int scope,int deref,int slimit,int tlimit,
-       Filter *filter, const char *filterstr,AttributeName *attrs,int attrsonly)
+int
+backsql_search(
+       BackendDB       *be,
+       Connection      *conn,
+       Operation       *op,
+       struct berval   *base,
+       struct berval   *nbase,
+       int             scope,
+       int             deref,
+       int             slimit,
+       int             tlimit,
+       Filter          *filter,
+       struct berval   *filterstr,
+       AttributeName   *attrs,
+       int             attrsonly )
 {
- backsql_info *bi=(backsql_info*)be->be_private;
- SQLHDBC dbh;
- int sres;
- int nentries;
- Entry *entry,*res;
- int manageDSAit = get_manageDSAit( op );
- struct berval **v2refs = NULL;
- time_t        stoptime;
- backsql_srch_info srch_info;
- backsql_entryID *eid=NULL;
-
- Debug(LDAP_DEBUG_TRACE,"==>backsql_search(): base='%s', filter='%s', scope=%d,",
-                     nbase,filterstr,scope);
- Debug(LDAP_DEBUG_TRACE," deref=%d, attrsonly=%d, attributes to load: %s\n",
-        deref,attrsonly,attrs==NULL?"all":"custom list");
- dbh=backsql_get_db_conn(be,conn);
-
- if (!dbh)
- {
-  Debug(LDAP_DEBUG_TRACE,"backsql_search(): could not get connection handle - exiting\n",0,0,0);
-  send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
-  return 1;
- }
-
- /* TimesTen : Pass it along to the lower level routines */ 
- srch_info.isTimesTen = bi->isTimesTen; 
+       backsql_info            *bi = (backsql_info *)be->be_private;
+       SQLHDBC                 dbh;
+       int                     sres;
+       int                     nentries;
+       Entry                   *entry, *res;
+       int                     manageDSAit = get_manageDSAit( op );
+       BerVarray               v2refs = NULL;
+       time_t                  stoptime = 0;
+       backsql_srch_info       srch_info;
+       backsql_entryID         *eid = NULL;
+       struct slap_limits_set  *limit = NULL;
+       int                     isroot = 0;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
+               "base='%s', filter='%s', scope=%d,", 
+               nbase->bv_val, filterstr->bv_val, scope );
+       Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
+               "attributes to load: %s\n",
+               deref, attrsonly, attrs == NULL ? "all" : "custom list" );
+
+       sres = backsql_get_db_conn( be, conn, &dbh );
+       if ( sres != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+                       "could not get connection handle - exiting\n", 
+                       0, 0, 0 );
+               send_ldap_result( conn, op, sres, "",
+                               sres == LDAP_OTHER ?  "SQL-backend error" : "",
+                               NULL, NULL );
+               return 1;
+       }
+
+       /* TimesTen : Pass it along to the lower level routines */ 
+       srch_info.isTimesTen = bi->isTimesTen; 
  
- if (tlimit == 0 && be_isroot(be,&op->o_ndn))
-  {
-   tlimit = -1;        /* allow root to set no limit */
-  } 
- else
-  {
-   tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
-                   be->be_timelimit : tlimit;
-   stoptime = op->o_time + tlimit;
-  }
-  
- if (slimit == 0 && be_isroot(be,&op->o_ndn))
-  {
-   slimit = -1;        /* allow root to set no limit */
-  }
- else
-  {
-   slimit = (slimit > be->be_sizelimit || slimit < 1) ?
-                   be->be_sizelimit : slimit;
-  }
-
- backsql_init_search(&srch_info,bi,(char*)nbase,scope,slimit,tlimit,stoptime,filter,dbh,
-                be,conn,op,attrs);
-
- /*for each objectclass we try to construct query which gets IDs
-  *of entries matching LDAP query filter and scope (or at least candidates),
-  *and get the IDs
-*/
- avl_apply(bi->oc_by_name,(AVL_APPLY)backsql_oc_get_candidates,&srch_info,0,AVL_INORDER);
-            
- nentries=0;
- /*now we load candidate entries (only those attrubutes 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)
-  {
-   /* check for abandon */
-   if (op->o_abandon)
-    {
-     break;
-    }
-
-   /* check time limit */
-   if ( tlimit != -1 && slap_get_time() > stoptime)
-    {
-        send_search_result( conn, op, LDAP_TIMELIMIT_EXCEEDED,
+       /* if not root, get appropriate limits */
+       if ( be_isroot( be, &op->o_ndn ) ) {
+               isroot = 1;
+       } else {
+               ( void ) get_limits( be, &op->o_ndn, &limit );
+       }
+
+       /* The time/size limits come first because they require very little
+        * effort, so there's no chance the candidates are selected and then 
+        * the request is not honored only because of time/size constraints */
+
+       /* if no time limit requested, use soft limit (unless root!) */
+       if ( isroot ) {
+               if ( tlimit == 0 ) {
+                       tlimit = -1;    /* allow root to set no limit */
+               }
+
+               if ( slimit == 0 ) {
+                       slimit = -1;
+               }
+
+       } else {
+               /* if no limit is required, use soft limit */
+               if ( tlimit <= 0 ) {
+                       tlimit = limit->lms_t_soft;
+
+               /* if requested limit higher than hard limit, abort */
+               } else if ( tlimit > limit->lms_t_hard ) {
+                       /* no hard limit means use soft instead */
+                       if ( limit->lms_t_hard == 0 && tlimit > limit->lms_t_soft ) {
+                               tlimit = limit->lms_t_soft;
+
+                       /* positive hard limit means abort */
+                       } else if ( limit->lms_t_hard > 0 ) {
+                               send_search_result( conn, op, 
+                                               LDAP_UNWILLING_TO_PERFORM,
+                                               NULL, NULL, NULL, NULL, 0 );
+                               return 0;
+                       }
+               
+                       /* negative hard limit means no limit */
+               }
+               
+               /* if no limit is required, use soft limit */
+               if ( slimit <= 0 ) {
+                       slimit = limit->lms_s_soft;
+
+               /* if requested limit higher than hard limit, abort */
+               } else if ( slimit > limit->lms_s_hard ) {
+                       /* no hard limit means use soft instead */
+                       if ( limit->lms_s_hard == 0 && slimit > limit->lms_s_soft ) {
+                               slimit = limit->lms_s_soft;
+
+                       /* positive hard limit means abort */
+                       } else if ( limit->lms_s_hard > 0 ) {
+                               send_search_result( conn, op, 
+                                               LDAP_UNWILLING_TO_PERFORM,
+                                               NULL, NULL, NULL, NULL, 0 );
+                               return 0;
+                       }
+                       
+                       /* negative hard limit means no limit */
+               }
+       }
+
+       /* compute it anyway; root does not use it */
+       stoptime = op->o_time + tlimit;
+
+       backsql_init_search( &srch_info, bi, nbase, scope,
+                       slimit, tlimit, stoptime, filter, dbh,
+                       be, conn, op, attrs );
+
+       /*
+        * for each objectclass we try to construct query which gets IDs
+        * of entries matching LDAP query filter and scope (or at least 
+        * candidates), and get the IDs
+        */
+       avl_apply( bi->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 ) {
+                       send_search_result( conn, op,
+                                       LDAP_ADMINLIMIT_EXCEEDED,
+                                       NULL, NULL, NULL, NULL, 0 );
+                       goto done;
+               }
+       }
+       
+       nentries = 0;
+       /*
+        * now we load candidate entries (only those attributes 
+        * 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 ) {
+
+               /* check for abandon */
+               if ( op->o_abandon ) {
+                       break;
+               }
+
+               /* check time limit */
+               if ( tlimit != -1 && slap_get_time() > stoptime ) {
+                       send_search_result( conn, op, LDAP_TIMELIMIT_EXCEEDED,
                                NULL, NULL, v2refs, NULL, nentries );
+                       break;
+               }
+
+               Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
+                       "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
+                       eid->id, eid->oc_id, eid->keyval );
+
+               entry = (Entry *)ch_calloc( sizeof( Entry ), 1 );
+               res = backsql_id2entry( &srch_info, entry, eid );
+               if ( res == NULL ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+                               "error in backsql_id2entry() "
+                               "- skipping entry\n", 0, 0, 0 );
+                       continue;
+               }
+
+               if ( !manageDSAit && scope != LDAP_SCOPE_BASE &&
+                       is_entry_referral( entry ) ) {
+                       BerVarray refs = get_entry_referrals( be, conn,
+                                       op, entry );
+
+                       send_search_reference( be, conn, op, entry, refs, 
+                                       NULL, &v2refs );
+                       ber_bvarray_free( refs );
+                       continue;
+               }
+
+               if ( test_filter( be, conn, op, entry, filter ) 
+                               == LDAP_COMPARE_TRUE ) {
+                       sres = send_search_entry( be, conn, op, entry,
+                                       attrs, attrsonly, NULL );
+                       if ( sres == -1 ) {
+                               Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+                                       "connection lost\n", 0, 0, 0 );
+                               break;
+                       }
+                       nentries += !sres;                                      
+               }
+               entry_free( entry );
+
+               if ( slimit != -1 && nentries > slimit ) {
+                       send_search_result( conn, op, LDAP_SIZELIMIT_EXCEEDED,
+                               NULL, NULL, v2refs, NULL, nentries );
+                       break;
+               }
      
-     break;
-    }
-     
-   Debug(LDAP_DEBUG_TRACE,"backsql_search(): loading data for entry id=%d, oc_id=%d, keyval=%d\n",
-               eid->id,eid->oc_id,eid->keyval);
-   
-   entry=(Entry *)ch_calloc(sizeof(Entry),1);
-   res=backsql_id2entry(&srch_info,entry,eid);
-   if (res==NULL)
-    {
-     Debug(LDAP_DEBUG_TRACE,"backsql_search(): error in backsql_id2entry() - skipping entry\n",0,0,0);
-     continue;
-    }
-
-   if ( !manageDSAit && scope != LDAP_SCOPE_BASE &&
-                       is_entry_referral( entry ) )
-    {
-     struct berval **refs = get_entry_referrals(be,conn,op,entry);
-
-     send_search_reference( be, conn, op, entry, refs, scope, NULL, &v2refs );
-     ber_bvecfree( refs );
-     continue;
-    }
-
-   if (test_filter(be,conn,op,entry,filter)==LDAP_COMPARE_TRUE)
-    {
-     if ((sres=send_search_entry(be,conn,op,entry,attrs,attrsonly,NULL))==-1)
-      {
-       Debug(LDAP_DEBUG_TRACE,"backsql_search(): connection lost\n",0,0,0);
-       break;
-      }
-     nentries+=!sres;                                  
-    }
-   entry_free(entry);
-  }
-
- for(eid=srch_info.id_list;eid!=NULL;eid=backsql_free_entryID(eid));
-
- charray_free(srch_info.attrs);
-
- if (nentries>0)
-  send_search_result( conn, op,
-               v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
-               NULL, NULL, v2refs, NULL, nentries );
- else
-  send_ldap_result(conn,op,LDAP_NO_SUCH_OBJECT,NULL,NULL,NULL,0);
- Debug(LDAP_DEBUG_TRACE,"<==backsql_search()\n",0,0,0);
- return 0;
+       }
+
+       if ( nentries > 0 ) {
+               send_search_result( conn, op,
+                       v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
+                       NULL, NULL, v2refs, NULL, nentries );
+       } else {
+               send_ldap_result( conn, op, srch_info.status,
+                               NULL, NULL, NULL, 0 );
+       }
+       
+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 );
+       return 0;
 }
 
 #endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/sql-types.h b/servers/slapd/back-sql/sql-types.h
new file mode 100644 (file)
index 0000000..f3eee45
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef __BACKSQL_SQL_TYPES_H__
+#define __BACKSQL_SQL_TYPES_H__
+
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include <sql.h>
+#include <sqlext.h>
+
+typedef struct {
+       SWORD           ncols;
+       BerVarray       col_names;
+       UDWORD          *col_prec;
+       char            **cols;
+       SQLINTEGER      *is_null;
+} BACKSQL_ROW_NTS;
+
+#endif /* __BACKSQL_SQL_TYPES_H__ */
+
diff --git a/servers/slapd/back-sql/sql-wrap.c b/servers/slapd/back-sql/sql-wrap.c
new file mode 100644 (file)
index 0000000..f5c63d9
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include "ac/string.h"
+#include <sys/types.h>
+#include "slap.h"
+#include "ldap_pvt.h"
+#include "back-sql.h"
+#include "sql-types.h"
+#include "sql-wrap.h"
+#include "schema-map.h"
+
+#define MAX_ATTR_LEN 16384
+
+typedef struct backsql_conn {
+       int             ldap_cid;
+       SQLHDBC         dbh;
+} backsql_db_conn;
+
+void
+backsql_PrintErrors( SQLHENV henv, SQLHDBC hdbc, SQLHSTMT sth, int rc )
+{
+       SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH];            /* msg. buffer    */
+       SQLCHAR state[SQL_SQLSTATE_SIZE];               /* statement buf. */
+       SDWORD  iSqlCode;                               /* return code    */
+       SWORD   len = SQL_MAX_MESSAGE_LENGTH - 1;       /* return length  */ 
+
+       Debug( LDAP_DEBUG_TRACE, "Return code: %d\n", rc, 0, 0 );
+
+
+       rc = SQLError( henv, hdbc, sth, state, &iSqlCode, msg,
+                       SQL_MAX_MESSAGE_LENGTH - 1, &len );
+       for ( ; BACKSQL_SUCCESS( rc ); ) {
+               Debug( LDAP_DEBUG_TRACE, "Native error code: %d\n", 
+                               (int)iSqlCode, 0, 0 );
+               Debug( LDAP_DEBUG_TRACE, "SQL engine state: %s\n", 
+                               state, 0, 0 );
+               Debug( LDAP_DEBUG_TRACE, "Message: %s\n", msg, 0, 0 );
+               rc = SQLError( henv, hdbc, sth, state, &iSqlCode, msg,
+                               SQL_MAX_MESSAGE_LENGTH - 1, &len );
+       }
+}
+
+RETCODE
+backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, char *query, int timeout )
+{
+       RETCODE         rc;
+       char            drv_name[ 30 ];
+       SWORD           len;
+
+       rc = SQLAllocStmt( dbh, sth );
+       if ( rc != SQL_SUCCESS ) {
+               return rc;
+       }
+
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "==>_SQLPrepare()\n", 0, 0, 0 );
+#endif
+
+       SQLGetInfo( dbh, SQL_DRIVER_NAME, drv_name, sizeof( drv_name ), &len );
+
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "_SQLPrepare(): driver name='%s'\n",
+                       drv_name, 0, 0 );
+#endif
+
+       ldap_pvt_str2upper( drv_name );
+       if ( !strncmp( drv_name, "SQLSRV32.DLL", sizeof( drv_name ) ) ) {
+               /*
+                * stupid default result set in MS SQL Server
+                * does not support multiple active statements
+                * on the same connection -- so we are trying 
+                * to make it not to use default result set...
+                */
+               Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
+                       "enabling MS SQL Server default result "
+                       "set workaround\n", 0, 0, 0 );
+               rc = SQLSetStmtOption( *sth, SQL_CONCURRENCY, 
+                               SQL_CONCUR_ROWVER );
+               if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) {
+                       Debug( LDAP_DEBUG_TRACE, "_SQLPrepare(): "
+                               "SQLSetStmtOption(SQL_CONCURRENCY,"
+                               "SQL_CONCUR_ROWVER) failed:\n", 
+                               0, 0, 0 );
+                       backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
+               }
+       }
+
+       if ( timeout > 0 ) {
+               Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
+                       "setting query timeout to %d sec.\n", 
+                       timeout, 0, 0 );
+               rc = SQLSetStmtOption( *sth, SQL_QUERY_TIMEOUT, timeout );
+               if ( rc != SQL_SUCCESS ) {
+                       backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
+               }
+       }
+
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "<==_SQLPrepare() calling SQLPrepare()\n",
+                       0, 0, 0 );
+#endif
+
+       return SQLPrepare( *sth, query, SQL_NTS );
+}
+
+#if 0
+/*
+ * Turned into macros --- see sql-wrap.h
+ */
+RETCODE
+backsql_BindParamStr( SQLHSTMT sth, int par_ind, char *str, int maxlen )
+{
+       RETCODE         rc;
+
+       rc = SQLBindParameter( sth, (SQLUSMALLINT)par_ind, SQL_PARAM_INPUT,
+                       SQL_C_CHAR, SQL_VARCHAR,
+                       (SQLUINTEGER)maxlen, 0, (SQLPOINTER)str,
+                       (SQLUINTEGER)maxlen, NULL );
+       return rc;
+}
+
+RETCODE
+backsql_BindParamID( SQLHSTMT sth, int par_ind, unsigned long *id )
+{
+       return SQLBindParameter( sth, (SQLUSMALLINT)par_ind,
+                       SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
+                       0, 0, (SQLPOINTER)id, 0, (SQLINTEGER*)NULL );
+}
+#endif
+
+RETCODE
+backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row )
+{
+       RETCODE         rc;
+       SQLCHAR         colname[ 64 ];
+       SQLSMALLINT     name_len, col_type, col_scale, col_null;
+       UDWORD          col_prec;
+       int             i;
+
+       if ( row == NULL ) {
+               return SQL_ERROR;
+       }
+
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "==> backsql_BindRowAsStrings()\n", 0, 0, 0 );
+#endif
+       
+       rc = SQLNumResultCols( sth, &row->ncols );
+       if ( rc != SQL_SUCCESS ) {
+#if 0
+               Debug( LDAP_DEBUG_TRACE, "_SQLBindRowAsStrings(): "
+                       "SQLNumResultCols() failed:\n", 0, 0, 0 );
+#endif
+               
+               backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC, sth, rc );
+       } else {
+#if 0
+               Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
+                       "ncols=%d\n", (int)row->ncols, 0, 0 );
+#endif
+
+               row->col_names = (BerVarray)ch_calloc( row->ncols + 1, 
+                               sizeof( struct berval ) );
+               row->cols = (char **)ch_calloc( row->ncols + 1, 
+                               sizeof( char * ) );
+               row->col_prec = (UDWORD *)ch_calloc( row->ncols,
+                               sizeof( UDWORD ) );
+               row->is_null = (SQLINTEGER *)ch_calloc( row->ncols,
+                               sizeof( SQLINTEGER ) );
+               for ( i = 1; i <= row->ncols; i++ ) {
+                       rc = SQLDescribeCol( sth, (SQLSMALLINT)i, &colname[ 0 ],
+                                       (SQLUINTEGER)( sizeof( colname ) - 1 ),
+                                       &name_len, &col_type,
+                                       &col_prec, &col_scale, &col_null );
+                       ber_str2bv( colname, 0, 1, &row->col_names[ i - 1 ] );
+#if 0
+                       Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
+                               "col_name=%s, col_prec[%d]=%d\n",
+                               colname, (int)i, (int)col_prec );
+#endif
+                       if ( col_type == SQL_LONGVARCHAR 
+                                       || col_type == SQL_LONGVARBINARY) {
+#if 0
+                               row->cols[ i - 1 ] = NULL;
+                               row->col_prec[ i - 1 ] = -1;
+
+                               /*
+                                * such fields must be handled 
+                                * in some other way since they return 2G 
+                                * as their precision (at least it does so 
+                                * with MS SQL Server w/native driver)
+                                * for now, we just set fixed precision 
+                                * for such fields - dirty hack, but...
+                                * no time to deal with SQLGetData()
+                                */
+#endif
+                               col_prec = MAX_ATTR_LEN;
+                               row->cols[ i - 1 ] = (char *)ch_calloc( col_prec + 1, sizeof( char ) );
+                               row->col_prec[ i - 1 ] = col_prec;
+                               rc = SQLBindCol( sth, (SQLUSMALLINT)i,
+                                               SQL_C_CHAR,
+                                               (SQLPOINTER)row->cols[ i - 1 ],
+                                               col_prec + 1,
+                                               &row->is_null[ i - 1 ] );
+                       } else {
+                               row->cols[ i - 1 ] = (char *)ch_calloc( col_prec + 1, sizeof( char ) );
+                               row->col_prec[ i - 1 ] = col_prec;
+                               rc = SQLBindCol( sth, (SQLUSMALLINT)i,
+                                               SQL_C_CHAR,
+                                               (SQLPOINTER)row->cols[ i - 1 ],
+                                               col_prec + 1,
+                                               &row->is_null[ i - 1 ] );
+                       }
+               }
+
+               row->col_names[ i - 1 ].bv_val = NULL;
+               row->col_names[ i - 1 ].bv_len = 0;
+               row->cols[ i - 1 ] = NULL;
+       }
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "<== backsql_BindRowAsStrings()\n", 0, 0, 0 );
+#endif
+
+       return rc;
+}
+
+RETCODE
+backsql_FreeRow( BACKSQL_ROW_NTS *row )
+{
+       if ( row->cols == NULL ) {
+               return SQL_ERROR;
+       }
+
+       ber_bvarray_free( row->col_names );
+       charray_free( row->cols );
+       free( row->col_prec );
+       free( row->is_null );
+
+       return SQL_SUCCESS;
+}
+
+static int
+backsql_cmp_connid( backsql_db_conn *c1, backsql_db_conn *c2 )
+{
+       if ( c1->ldap_cid > c2->ldap_cid ) {
+               return 1;
+       }
+       
+       if ( c1->ldap_cid < c2->ldap_cid ) {
+               return -1;
+       }
+       
+       return 0;
+}
+
+static int
+backsql_close_db_conn( backsql_db_conn *conn )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_close_db_conn()\n", 0, 0, 0 );
+
+       /*
+        * Default transact is SQL_ROLLBACK; commit is required only
+        * by write operations, and it is explicitly performed after
+        * each atomic operation succeeds.
+        */
+
+       /* TimesTen */
+       SQLTransact( SQL_NULL_HENV, conn->dbh, SQL_ROLLBACK );
+       SQLDisconnect( conn->dbh );
+       SQLFreeConnect( conn->dbh );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_close_db_conn()\n", 0, 0, 0 );
+       return 1;
+}
+
+int
+backsql_init_db_env( backsql_info *si )
+{
+       RETCODE         rc;
+       
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_init_db_env()\n", 0, 0, 0 );
+       rc = SQLAllocEnv( &si->db_env );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "init_db_env: SQLAllocEnv failed:\n",
+                               0, 0, 0 );
+               backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC,
+                               SQL_NULL_HENV, rc );
+       }
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_init_db_env()\n", 0, 0, 0 );
+       return SQL_SUCCESS;
+}
+
+int
+backsql_free_db_env( backsql_info *si )
+{
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_env()\n", 0, 0, 0 );
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "free_db_env(): delete AVL tree here!!!\n",
+                       0, 0, 0 );
+#endif
+
+       /*
+        * stop, if frontend waits for all threads to shutdown 
+        * before calling this -- then what are we going to delete?? 
+        * everything is already deleted...
+        */
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_env()\n", 0, 0, 0 );
+       return SQL_SUCCESS;
+}
+
+static int
+backsql_open_db_conn( backsql_info *si, int ldap_cid, backsql_db_conn **pdbc )
+{
+       /* TimesTen */
+       char                    DBMSName[ 32 ];
+       backsql_db_conn         *dbc;
+       int                     rc;
+
+       assert( pdbc );
+       *pdbc = NULL;
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_open_db_conn()\n", 0, 0, 0 );
+       dbc = (backsql_db_conn *)ch_calloc( 1, sizeof( backsql_db_conn ) );
+       dbc->ldap_cid = ldap_cid;
+       rc = SQLAllocConnect( si->db_env, &dbc->dbh );
+       if ( !BACKSQL_SUCCESS( rc ) ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
+                       "SQLAllocConnect() failed:\n", 0, 0, 0 );
+               backsql_PrintErrors( si->db_env, SQL_NULL_HDBC,
+                               SQL_NULL_HENV, rc );
+               return LDAP_UNAVAILABLE;
+       }
+
+       rc = SQLConnect( dbc->dbh, si->dbname, SQL_NTS, si->dbuser, 
+                       SQL_NTS, si->dbpasswd, SQL_NTS );
+       if ( rc != SQL_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
+                       "SQLConnect() to database '%s' as user '%s' "
+                       "%s:\n", si->dbname, si->dbuser,
+                       rc == SQL_SUCCESS_WITH_INFO ?
+                       "succeeded with info" : "failed" );
+               backsql_PrintErrors( si->db_env, dbc->dbh, SQL_NULL_HENV, rc );
+               if ( rc != SQL_SUCCESS_WITH_INFO ) {
+                       return LDAP_UNAVAILABLE;
+               }
+       }
+
+       /* 
+        * TimesTen : Turn off autocommit.  We must explicitly
+        * commit any transactions. 
+        */
+       SQLSetConnectOption( dbc->dbh, SQL_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF );
+
+       /* 
+        * See if this connection is to TimesTen.  If it is,
+        * remember that fact for later use.
+        */
+       si->isTimesTen = 0;     /* Assume until proven otherwise */
+       DBMSName[ 0 ] = '\0';
+       rc = SQLGetInfo( dbc->dbh, SQL_DBMS_NAME, (PTR)&DBMSName,
+                       sizeof( DBMSName ), NULL );
+       if ( rc == SQL_SUCCESS ) {
+               if ( strcmp( DBMSName, "TimesTen" ) == 0 ||
+                               strcmp( DBMSName, "Front-Tier" ) == 0 ) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
+                               "TimesTen database!\n", 0, 0, 0 );
+                       si->isTimesTen = 1;
+               }
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: "
+                       "SQLGetInfo() failed:\n", 0, 0, 0 );
+               backsql_PrintErrors( si->db_env, dbc->dbh, SQL_NULL_HENV, rc );
+       }
+       /* end TimesTen */
+
+       Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn(): "
+               "connected, adding to tree\n", 0, 0, 0 );
+       ldap_pvt_thread_mutex_lock( &si->dbconn_mutex );
+       avl_insert( &si->db_conns, dbc, (AVL_CMP)backsql_cmp_connid, NULL );
+       ldap_pvt_thread_mutex_unlock( &si->dbconn_mutex );
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_open_db_conn()\n", 0, 0, 0 );
+
+       *pdbc = dbc;
+
+       return LDAP_SUCCESS;
+}
+
+int
+backsql_free_db_conn( Backend *be, Connection *ldapc )
+{
+       backsql_info            *si = (backsql_info *)be->be_private;
+       backsql_db_conn         tmp, *conn;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_conn()\n", 0, 0, 0 );
+       tmp.ldap_cid = ldapc->c_connid;
+       ldap_pvt_thread_mutex_lock( &si->dbconn_mutex );
+       conn = (backsql_db_conn *)avl_delete( &si->db_conns, &tmp,
+                       (AVL_CMP)backsql_cmp_connid );
+       ldap_pvt_thread_mutex_unlock( &si->dbconn_mutex );
+
+       /*
+        * we have one thread per connection, as I understand -- so we can
+        * get this out of critical section
+        */
+       if ( conn != NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_free_db_conn(): "
+                       "closing db connection\n", 0, 0, 0 );
+               backsql_close_db_conn( conn );
+       }
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_conn()\n", 0, 0, 0 );
+       return SQL_SUCCESS;
+}
+
+int
+backsql_get_db_conn( Backend *be, Connection *ldapc, SQLHDBC *dbh )
+{
+       backsql_info            *si = (backsql_info *)be->be_private;
+       backsql_db_conn         *dbc;
+       backsql_db_conn         tmp;
+       int                     rc = LDAP_SUCCESS;
+
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_get_db_conn()\n", 0, 0, 0 );
+
+       assert( dbh );
+       *dbh = SQL_NULL_HDBC;
+
+       tmp.ldap_cid = ldapc->c_connid;
+
+       /*
+        * we have one thread per connection, as I understand -- 
+        * so we do not need locking here
+        */
+       dbc = (backsql_db_conn *)avl_find( si->db_conns, &tmp,
+                       (AVL_CMP)backsql_cmp_connid );
+       if ( !dbc ) {
+               rc = backsql_open_db_conn( si, ldapc->c_connid, &dbc );
+               if ( rc != LDAP_SUCCESS) {
+                       Debug( LDAP_DEBUG_TRACE, "backsql_get_db_conn(): "
+                               "could not get connection handle "
+                               "-- returning NULL\n", 0, 0, 0 );
+                       return rc;
+               }
+       }
+
+       ldap_pvt_thread_mutex_lock( &si->schema_mutex );
+       if ( !si->schema_loaded ) {
+               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 ) {
+                       backsql_free_db_conn( be, ldapc );
+                       return rc;
+               }
+       }
+       ldap_pvt_thread_mutex_unlock( &si->schema_mutex );
+
+       *dbh = dbc->dbh;
+
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_get_db_conn()\n", 0, 0, 0 );
+
+       return LDAP_SUCCESS;
+}
+
+#endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/sql-wrap.h b/servers/slapd/back-sql/sql-wrap.h
new file mode 100644 (file)
index 0000000..964a953
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef __BACKSQL_SQL_WRAP_H__
+#define __BACKSQL_SQL_WRAP_H__
+
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "back-sql.h"
+#include "sql-types.h"
+
+RETCODE backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, char* query, int timeout );
+
+#define backsql_BindParamStr( sth, par_ind, str, maxlen )              \
+       SQLBindParameter( (sth), (SQLUSMALLINT)(par_ind),               \
+                       SQL_PARAM_INPUT,                                \
+                       SQL_C_CHAR, SQL_VARCHAR,                        \
+                       (SQLUINTEGER)(maxlen), 0, (SQLPOINTER)(str),    \
+                       (SQLUINTEGER)(maxlen), NULL )
+
+#define backsql_BindParamID( sth, par_ind, id )                                \
+       SQLBindParameter( (sth), (SQLUSMALLINT)(par_ind),               \
+                       SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,      \
+                       0, 0, (SQLPOINTER)(id), 0, (SQLINTEGER*)NULL )
+
+RETCODE backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row );
+RETCODE backsql_FreeRow( BACKSQL_ROW_NTS *row );
+void backsql_PrintErrors( SQLHENV henv, SQLHDBC hdbc, SQLHSTMT sth, int rc );
+
+int backsql_init_db_env( backsql_info *si );
+int backsql_free_db_env( backsql_info *si );
+int backsql_get_db_conn( Backend *be, Connection *ldapc, SQLHDBC *dbh );
+int backsql_free_db_conn( Backend *be, Connection *ldapc );
+
+#endif /* __BACKSQL_SQL_WRAP_H__ */
+
index 00e1d33889e6ffce2c509c180d20e50216abfcba..48701c6aa4975d578e271886534e41a2e77c3be3 100644 (file)
 
 #include <stdio.h>
 #include <sys/types.h>
-#include <string.h>
-#include <stdarg.h>
+#include "ac/string.h"
+#include "ac/ctype.h"
+#include "ac/stdarg.h"
 #include "slap.h"
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
 #include "back-sql.h"
 #include "schema-map.h"
 #include "util.h"
 
 
-char backsql_def_oc_query[]="SELECT id,name,keytbl,keycol,create_proc,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 WHERE oc_map_id=?";
-char backsql_def_delentry_query[]="DELETE FROM ldap_entries WHERE id=?";
-char backsql_def_insentry_query[]="INSERT INTO ldap_entries (dn,oc_map_id,parent,keyval) VALUES (?,?,?,?)";
-char backsql_def_subtree_cond[]="ldap_entries.dn LIKE CONCAT('%',?)";
-char backsql_id_query[]="SELECT id,keyval,oc_map_id FROM ldap_entries WHERE ";
-
-/* TimesTen*/
+char backsql_def_oc_query[] = 
+       "SELECT id,name,keytbl,keycol,create_proc,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 "
+       "WHERE oc_map_id=?";
+char backsql_def_delentry_query[] = "DELETE FROM ldap_entries WHERE id=?";
+char backsql_def_insentry_query[] = 
+       "INSERT INTO ldap_entries (dn,oc_map_id,parent,keyval) "
+       "VALUES (?,?,?,?)";
+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 ";
+
+/* TimesTen */
 char backsql_check_dn_ru_query[] = "SELECT dn_ru from ldap_entries";
 
-char* backsql_strcat(char* dest,int *buflen, ...)
+/*
+ * 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, ... )
 {
- va_list strs;
- int cdlen,cslen,grow;
- char *cstr;
+       va_list         strs;
+       int             cdlen, cslen, grow;
+       char            *cstr;
+
+       assert( dest );
+       assert( dest->bv_val == NULL 
+                       || dest->bv_len == strlen( dest->bv_val ) );
  
- /*Debug(LDAP_DEBUG_TRACE,"==>my_strcat()\n");*/
- va_start(strs,buflen);
- if (dest==NULL || *buflen<=0)
-  {
-   dest=(char*)ch_calloc(BACKSQL_STR_GROW,sizeof(char));
-   *buflen=BACKSQL_STR_GROW;
-  }
- cdlen=strlen(dest)+1;
- while ((cstr=va_arg(strs,char*)) != NULL)
-  {
-   cslen=strlen(cstr);
-   grow=BACKSQL_MAX(BACKSQL_STR_GROW,cslen);
-   if (*buflen-cdlen < cslen)
-    {
-     /*Debug(LDAP_DEBUG_TRACE,"my_strcat(): buflen=%d, cdlen=%d, cslen=%d -- reallocating dest\n",
-                           *buflen,cdlen,cslen); */
-     dest=(char*)ch_realloc(dest,(*buflen)+grow*sizeof(char));
-     if (dest == NULL)
-      {
-       Debug(LDAP_DEBUG_ANY,"my_strcat(): could not reallocate string buffer.\n",0,0,0);
-      }
-     *buflen+=grow;
-     /*Debug(LDAP_DEBUG_TRACE,"my_strcat(): new buflen=%d, dest=%p\n",*buflen,dest,0);*/
-    }
-   strcat(dest,cstr);
-   cdlen+=cslen;
-  }
- va_end(strs);
- /*Debug(LDAP_DEBUG_TRACE,"<==my_strcat() (dest='%s')\n",dest,0,0);*/
- return dest;
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "==>backsql_strcat()\n" );
+#endif
+
+       va_start( strs, buflen );
+       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;
+       while ( ( cstr = va_arg( strs, char * ) ) != NULL ) {
+               cslen = strlen( cstr );
+               grow = BACKSQL_MAX( BACKSQL_STR_GROW, cslen );
+               if ( *buflen - cdlen <= cslen ) {
+                       char    *tmp_dest;
+
+#if 0
+                       Debug( LDAP_DEBUG_TRACE, "backsql_strcat(): "
+                               "buflen=%d, cdlen=%d, cslen=%d "
+                               "-- reallocating dest\n",
+                               *buflen, cdlen + 1, cslen );
+#endif
+                       tmp_dest = (char *)ch_realloc( dest->bv_val,
+                                       ( *buflen ) + grow * sizeof( char ) );
+                       if ( tmp_dest == NULL ) {
+                               Debug( LDAP_DEBUG_ANY, "backsql_strcat(): "
+                                       "could not reallocate string buffer.\n",
+                                       0, 0, 0 );
+                               return NULL;
+                       }
+                       dest->bv_val = tmp_dest;
+                       *buflen += grow;
+#if 0
+                       Debug( LDAP_DEBUG_TRACE, "backsql_strcat(): "
+                               "new buflen=%d, dest=%p\n", *buflen, dest, 0 );
+#endif
+               }
+               AC_MEMCPY( dest->bv_val + cdlen, cstr, cslen + 1 );
+               cdlen += cslen;
+       }
+       va_end( strs );
+
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_strcat() (dest='%s')\n", 
+                       dest, 0, 0 );
+#endif
+
+       dest->bv_len = cdlen;
+
+       return dest;
 } 
 
-int backsql_entry_addattr(Entry *e,char *at_name,char *at_val,unsigned int at_val_len)
+int
+backsql_entry_addattr(
+       Entry           *e,
+       struct berval   *at_name,
+       struct berval   *at_val )
 {
- Attribute *c_at=e->e_attrs;
- struct berval* add_val[2];
- struct berval cval;
- AttributeDescription *ad;
- int rc;
- const char *text;
- Debug(LDAP_DEBUG_TRACE,"backsql_entry_addattr(): at_name='%s', at_val='%s'\n",at_name,at_val,0);
- cval.bv_val=at_val;
- cval.bv_len=at_val_len;
- add_val[0]=&cval;
- add_val[1]=NULL;
- ad=NULL;
- rc = slap_str2ad( at_name, &ad, &text );
- if( rc != LDAP_SUCCESS ) 
-  {
-   Debug(LDAP_DEBUG_TRACE,"backsql_entry_addattr(): failed to find AttributeDescription for '%s'\n",at_name,0,0);
-   return 0;
-  }
-  
- rc = attr_merge(e,ad,add_val);
-
- if( rc != 0 )
-  {
-   Debug(LDAP_DEBUG_TRACE,"backsql_entry_addattr(): failed to merge value '%s' for attribute '%s'\n",at_val,at_name,0);
-   return 0;
-  }
- Debug(LDAP_DEBUG_TRACE,"<==backsql_query_addattr()\n",0,0,0);
- return 1;
+       struct berval           add_val[ 2 ];
+       AttributeDescription    *ad;
+       int                     rc;
+       const char              *text;
+
+       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;
+
+       ad = NULL;
+       rc = slap_bv2ad( at_name, &ad, &text );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_entry_addattr(): "
+                       "failed to find AttributeDescription for '%s'\n",
+                       at_name->bv_val, 0, 0 );
+               return 0;
+       }
+
+       rc = attr_merge( e, ad, add_val );
+
+       if ( rc != 0 ) {
+               Debug( LDAP_DEBUG_TRACE, "backsql_entry_addattr(): "
+                       "failed to merge value '%s' for attribute '%s'\n",
+                       at_val->bv_val, at_name->bv_val, 0 );
+               return 0;
+       }
+       
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_query_addattr()\n", 0, 0, 0 );
+       return 1;
 }
 
-char* backsql_get_table_spec(char **p)
+char *
+backsql_get_table_spec( char **p )
 {
- char *s,*q;
- char *res=NULL;
- int res_len=0;
-
- s=*p;
- while(**p && **p!=',') (*p)++;
- if (**p)
-  *(*p)++='\0';
-
+       char            *s, *q;
+       struct berval   res = { 0, NULL };
+       int             res_len = 0;
+
+       s = *p;
+       while ( **p && **p != ',' ) {
+               (*p)++;
+       }
+
+       if ( **p ) {
+               *(*p)++ = '\0';
+       }
+       
 #define BACKSQL_NEXT_WORD { \
-  while (*s && isspace((unsigned char)*s)) s++; \
-  if (!*s) return res; \
-  q=s; \
-  while (*q && !isspace((unsigned char)*q)) q++; \
-  if (*q) *q++='\0'; \
- }
-
- BACKSQL_NEXT_WORD;
- res=backsql_strcat(res,&res_len,s,NULL);/*table name*/
- s=q;
-
- BACKSQL_NEXT_WORD;
- if (!strcasecmp(s,"as"))
- {
-  s=q;
-  BACKSQL_NEXT_WORD;
- }
- /*res=backsql_strcat(res,&res_len," AS ",s,NULL);
-  *oracle doesn't understand AS :(
-  */
- res=backsql_strcat(res,&res_len," ",s,NULL);/*table alias*/
- return res;
+               while ( *s && isspace( (unsigned char)*s ) ) s++; \
+               if ( !*s ) return res.bv_val; \
+               q = s; \
+               while ( *q && !isspace( (unsigned char)*q ) ) q++; \
+               if ( *q ) *q++='\0'; \
+       }
+
+       BACKSQL_NEXT_WORD;
+       /* table name */
+       backsql_strcat( &res, &res_len, s, NULL );
+       s = q;
+
+       BACKSQL_NEXT_WORD;
+       if ( !strcasecmp( s, "as" ) ) {
+               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);
+       return res.bv_val;
 }
 
-int backsql_merge_from_clause(char **dest_from,int *dest_len,char *src_from)
+int
+backsql_merge_from_clause( char **dest_from, int *dest_len, char *src_from )
 {
- char *s,*p,*srcc,*pos,e;
-
- /*Debug(LDAP_DEBUG_TRACE,"==>backsql_merge_from_clause(): dest_from='%s',src_from='%s'\n",
-                               dest_from,src_from,0); */
- srcc=ch_strdup(src_from);
- p=srcc;
- while(*p)
- {
-  s=backsql_get_table_spec(&p);
- /* Debug(LDAP_DEBUG_TRACE,"backsql_merge_from_clause(): p='%s' s='%s'\n",p,s,0);  */
-  if (*dest_from==NULL)
-   *dest_from=backsql_strcat(*dest_from,dest_len,s,NULL);
-  else
-       if((pos=strstr(*dest_from,s))==NULL)
-     *dest_from=backsql_strcat(*dest_from,dest_len,",",s,NULL);
-    else if((e=pos[strlen(s)])!='\0' && e!=',')
-      *dest_from=backsql_strcat(*dest_from,dest_len,",",s,NULL);
-  if (s)
-       ch_free(s);
- }
-/* Debug(LDAP_DEBUG_TRACE,"<==backsql_merge_from_clause()\n",0,0,0);*/
- free(srcc);
- return 1;
+       char            *s, *p, *srcc, *pos, e;
+       struct berval   res = { 0 , NULL };
+
+#if 0
+       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 );
+       p = srcc;
+
+       if ( *dest_from != NULL ) {
+               res.bv_val = *dest_from;
+               res.bv_len = strlen( *dest_from );
+       }
+       
+       while ( *p ) {
+               s = backsql_get_table_spec( &p );
+#if 0
+               Debug( LDAP_DEBUG_TRACE, "backsql_merge_from_clause(): "
+                       "p='%s' s='%s'\n", p, s, 0 );
+#endif
+               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 );
+                       } else if ( ( e = pos[ strlen( s ) ] ) != '\0' && e != ',' ) {
+                               backsql_strcat( &res, dest_len, ",", s, NULL );
+                       }
+               }
+               
+               if ( s ) {
+                       ch_free( s );
+               }
+       }
+#if 0
+       Debug( LDAP_DEBUG_TRACE, "<==backsql_merge_from_clause()\n", 0, 0, 0 );
+#endif
+       free( srcc );
+       *dest_from = res.bv_val;
+
+       return 1;
 }
 
 #endif /* SLAPD_SQL */
+
diff --git a/servers/slapd/back-sql/util.h b/servers/slapd/back-sql/util.h
new file mode 100644 (file)
index 0000000..adc5911
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef __BACKSQL_UTIL_H__
+#define __BACKSQL_UTIL_H__
+
+/*
+ *      Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *      Redistribution and use in source and binary forms are permitted only
+ *      as authorized by the OpenLDAP Public License.  A copy of this
+ *      license is available at http://www.OpenLDAP.org/license.html or
+ *      in file LICENSE in the top-level directory of the distribution.
+ */
+
+
+#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_STR_GROW 64
+
+extern struct berval 
+       bv_n_objectclass,
+       bv_n_0_10;
+
+struct berval *backsql_strcat( struct berval *dest, int *buflen, ... );
+
+int backsql_entry_addattr( Entry *e, struct berval *at_name, 
+               struct berval *at_val );
+
+typedef struct __backsql_srch_info {
+       struct berval           *base_dn;
+       int                     scope;
+       Filter                  *filter;
+       int                     slimit, tlimit;
+       time_t                  stoptime;
+       backsql_entryID         *id_list, *c_eid;
+       int                     n_candidates;
+       int                     abandon;
+       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;
+       SQLHDBC                 dbh;
+       int                     status;
+       Backend                 *be;
+       Connection              *conn;
+       Operation               *op;
+       AttributeName           *attrs;
+       Entry                   *e;
+       /* 1 if the db is TimesTen; 0 if it's not */
+       int                     isTimesTen; 
+} backsql_srch_info;
+
+int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
+void backsql_init_search( backsql_srch_info *bsi, backsql_info *bi,
+               struct berval *nbase, int scope, int slimit, int tlimit,
+               time_t stoptime, Filter *filter, SQLHDBC dbh,
+               BackendDB *be, Connection *conn, Operation *op,
+               AttributeName *attrs );
+Entry *backsql_id2entry( backsql_srch_info *bsi, Entry *e, 
+               backsql_entryID *id );
+
+extern char 
+       backsql_def_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[];
+extern char 
+       backsql_check_dn_ru_query[];
+
+int backsql_merge_from_clause( char **dest_from, int *dest_len, 
+               char *src_from );
+
+
+#endif /* __BACKSQL_UTIL_H__ */
+