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