From 8e303f716932c61fb83945a80d439922da735920 Mon Sep 17 00:00:00 2001 From: Kurt Zeilenga Date: Fri, 16 Aug 2002 19:19:49 +0000 Subject: [PATCH] Sync with HEAD --- servers/slapd/back-ldap/search.c | 17 +- servers/slapd/back-meta/search.c | 19 +- servers/slapd/back-sql/back-sql.h | 56 + servers/slapd/back-sql/bind.c | 131 ++ servers/slapd/back-sql/config.c | 211 +++ servers/slapd/back-sql/docs/install | 86 + servers/slapd/back-sql/entry-id.c | 321 ++++ servers/slapd/back-sql/entry-id.h | 28 + servers/slapd/back-sql/init.c | 310 ++++ servers/slapd/back-sql/modify.c | 1247 ++++++++++++++ servers/slapd/back-sql/other.c | 47 + .../rdbms_depend/ibmdb2/backsql_create.sql | 65 + .../rdbms_depend/ibmdb2/backsql_drop.sql | 5 + .../back-sql/rdbms_depend/ibmdb2/slapd.conf | 33 + .../rdbms_depend/ibmdb2/testdb_create.sql | 66 + .../rdbms_depend/ibmdb2/testdb_data.sql | 16 + .../rdbms_depend/ibmdb2/testdb_drop.sql | 5 + .../rdbms_depend/ibmdb2/testdb_metadata.sql | 74 + .../rdbms_depend/pgsql/backsql_create.sql | 57 + .../rdbms_depend/pgsql/backsql_drop.sql | 9 + .../back-sql/rdbms_depend/pgsql/slapd.conf | 35 + .../rdbms_depend/pgsql/testdb_create.sql | 38 + .../rdbms_depend/pgsql/testdb_data.sql | 17 + .../rdbms_depend/pgsql/testdb_drop.sql | 5 + .../rdbms_depend/pgsql/testdb_metadata.sql | 100 ++ servers/slapd/back-sql/schema-map.c | 574 +++++++ servers/slapd/back-sql/schema-map.h | 82 + servers/slapd/back-sql/search.c | 1485 ++++++++++------- servers/slapd/back-sql/sql-types.h | 25 + servers/slapd/back-sql/sql-wrap.c | 476 ++++++ servers/slapd/back-sql/sql-wrap.h | 40 + servers/slapd/back-sql/util.c | 345 ++-- servers/slapd/back-sql/util.h | 80 + 33 files changed, 5386 insertions(+), 719 deletions(-) create mode 100644 servers/slapd/back-sql/back-sql.h create mode 100644 servers/slapd/back-sql/bind.c create mode 100644 servers/slapd/back-sql/config.c create mode 100644 servers/slapd/back-sql/docs/install create mode 100644 servers/slapd/back-sql/entry-id.c create mode 100644 servers/slapd/back-sql/entry-id.h create mode 100644 servers/slapd/back-sql/init.c create mode 100644 servers/slapd/back-sql/modify.c create mode 100644 servers/slapd/back-sql/other.c create mode 100644 servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf create mode 100644 servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf create mode 100644 servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql create mode 100644 servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql create mode 100644 servers/slapd/back-sql/schema-map.c create mode 100644 servers/slapd/back-sql/schema-map.h create mode 100644 servers/slapd/back-sql/sql-types.h create mode 100644 servers/slapd/back-sql/sql-wrap.c create mode 100644 servers/slapd/back-sql/sql-wrap.h create mode 100644 servers/slapd/back-sql/util.h diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c index 45c5bd43df..c2e278d5c7 100644 --- a/servers/slapd/back-ldap/search.c +++ b/servers/slapd/back-ldap/search.c @@ -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; } diff --git a/servers/slapd/back-meta/search.c b/servers/slapd/back-meta/search.c index a670ef415c..4bab7a31f8 100644 --- a/servers/slapd/back-meta/search.c +++ b/servers/slapd/back-meta/search.c @@ -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 index 0000000000..bd349175a4 --- /dev/null +++ b/servers/slapd/back-sql/back-sql.h @@ -0,0 +1,56 @@ +#ifndef __BACKSQL_H__ +#define __BACKSQL_H__ + +/* + * Copyright 1999, Dmitry Kovalev , 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 index 0000000000..30c060f5c4 --- /dev/null +++ b/servers/slapd/back-sql/bind.c @@ -0,0 +1,131 @@ +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include +#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 index 0000000000..7403cc1d6e --- /dev/null +++ b/servers/slapd/back-sql/config.c @@ -0,0 +1,211 @@ +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include +#include +#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 index 0000000000..230bf0ad62 --- /dev/null +++ b/servers/slapd/back-sql/docs/install @@ -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 --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 index 0000000000..552d23946e --- /dev/null +++ b/servers/slapd/back-sql/entry-id.c @@ -0,0 +1,321 @@ +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include +#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 index 0000000000..bc52fa8669 --- /dev/null +++ b/servers/slapd/back-sql/entry-id.h @@ -0,0 +1,28 @@ +#ifndef __BACKSQL_ENTRYID_H__ +#define __BACKSQL_ENTRYID_H__ + +/* + * Copyright 1999, Dmitry Kovalev , 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 index 0000000000..79d8dec530 --- /dev/null +++ b/servers/slapd/back-sql/init.c @@ -0,0 +1,310 @@ +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include +#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 index 0000000000..2757d33e4c --- /dev/null +++ b/servers/slapd/back-sql/modify.c @@ -0,0 +1,1247 @@ +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include +#include +#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 index 0000000000..57c16d546e --- /dev/null +++ b/servers/slapd/back-sql/other.c @@ -0,0 +1,47 @@ +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include +#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 index 0000000000..ca19bb1ed1 --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql @@ -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 index 0000000000..49e7e3acd1 --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql @@ -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 index 0000000000..86661da25d --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf @@ -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 index 0000000000..a1da4a1d5b --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql @@ -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 index 0000000000..f141f414eb --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql @@ -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 index 0000000000..17b12afd42 --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql @@ -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 index 0000000000..c38036ba20 --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql @@ -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 index 0000000000..65aea733cb --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql @@ -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 index 0000000000..b497eb9d54 --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql @@ -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 index 0000000000..3a5dbb5ed8 --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf @@ -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 index 0000000000..6a3c0f7f66 --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql @@ -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 index 0000000000..b20733da0e --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql @@ -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 index 0000000000..7c5e9e759e --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql @@ -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 index 0000000000..7100a26884 --- /dev/null +++ b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql @@ -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 index 0000000000..cf765adc93 --- /dev/null +++ b/servers/slapd/back-sql/schema-map.c @@ -0,0 +1,574 @@ +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include +#include +#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 index 0000000000..27f07278ef --- /dev/null +++ b/servers/slapd/back-sql/schema-map.h @@ -0,0 +1,82 @@ +#ifndef __BACKSQL_SCHEMA_MAP_H__ +#define __BACKSQL_SCHEMA_MAP_H__ + +/* + * Copyright 1999, Dmitry Kovalev , 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__ */ + diff --git a/servers/slapd/back-sql/search.c b/servers/slapd/back-sql/search.c index 2dca484832..b8f5e143b0 100644 --- a/servers/slapd/back-sql/search.c +++ b/servers/slapd/back-sql/search.c @@ -13,637 +13,978 @@ #include #include -#include +#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 : "", 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:"",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;i0) - { - 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 index 0000000000..f3eee45ef4 --- /dev/null +++ b/servers/slapd/back-sql/sql-types.h @@ -0,0 +1,25 @@ +#ifndef __BACKSQL_SQL_TYPES_H__ +#define __BACKSQL_SQL_TYPES_H__ + +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include + +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 index 0000000000..f5c63d9026 --- /dev/null +++ b/servers/slapd/back-sql/sql-wrap.c @@ -0,0 +1,476 @@ +/* + * Copyright 1999, Dmitry Kovalev , 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 +#include "ac/string.h" +#include +#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 index 0000000000..964a953d2d --- /dev/null +++ b/servers/slapd/back-sql/sql-wrap.h @@ -0,0 +1,40 @@ +#ifndef __BACKSQL_SQL_WRAP_H__ +#define __BACKSQL_SQL_WRAP_H__ + +/* + * Copyright 1999, Dmitry Kovalev , 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__ */ + diff --git a/servers/slapd/back-sql/util.c b/servers/slapd/back-sql/util.c index 00e1d33889..48701c6aa4 100644 --- a/servers/slapd/back-sql/util.c +++ b/servers/slapd/back-sql/util.c @@ -13,158 +13,239 @@ #include #include -#include -#include +#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 index 0000000000..adc5911caf --- /dev/null +++ b/servers/slapd/back-sql/util.h @@ -0,0 +1,80 @@ +#ifndef __BACKSQL_UTIL_H__ +#define __BACKSQL_UTIL_H__ + +/* + * Copyright 1999, Dmitry Kovalev , 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__ */ + -- 2.39.5