From f11c6b27e7f09dc6ea476e892dbaa577cfcac843 Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Fri, 23 Aug 2002 08:54:08 +0000 Subject: [PATCH] Final run of changes to back-sql; IBM db2 support has been tested. Now related ITSes need be audited and possibly closed. Enhancements: - re-styled code for better readability - upgraded backend API to reflect recent changes - LDAP schema is checked when loading SQL/LDAP mapping - AttributeDescription/ObjectClass pointers used for more efficient mapping lookup - bervals used where string length is required often - atomized write operations by committing at the end of each operation and defaulting connection closure to rollback - added LDAP access control to write operations - fully implemented modrdn (with rdn attrs change, deleteoldrdn, access check, parent/children check and more) - added parent access control, children control to delete operation - added structuralObjectClass operational attribute check and value return on search - added hasSubordinate operational attribute on demand - search limits are appropriately enforced - function backsql_strcat() has been made more efficient - concat function has been made configurable by means of a pattern - added config switches: - fail_if_no_mapping write operations fail if there is no mapping - has_ldapinfo_dn_ru overrides autodetect - concat_pattern a string containing two '?' is used (note that "?||?" should be more portable than builtin function "CONCAT(?,?)") - strcast_func cast of string constants in "SELECT DISTINCT statements (needed by PostgreSQL) - upper_needs_cast cast the argument of upper when required (basically when building dn substring queries) Todo: - add security checks for SQL statements that can be injected (?) - re-test with previously supported RDBMs - replace dn_ru and so with normalized dn (no need for upper() and so in dn match) - implement a backsql_normalize() function to replace the upper() conversion routines - note that subtree deletion, subtree renaming and so could be easily implemented (rollback and consistency checks are available :) - implement "lastmod" and other operational stuff (ldap_entries table ?) --- doc/man/man5/slapd-sql.5 | 70 ++- servers/slapd/back-sql/back-sql.h | 117 ++++- servers/slapd/back-sql/config.c | 161 +++++- servers/slapd/back-sql/entry-id.c | 143 ++++- servers/slapd/back-sql/entry-id.h | 2 + servers/slapd/back-sql/external.h | 2 + servers/slapd/back-sql/init.c | 237 +++++++-- servers/slapd/back-sql/modify.c | 494 +++++++++++++++--- servers/slapd/back-sql/other.c | 66 +++ .../rdbms_depend/ibmdb2/backsql_create.sql | 1 + .../back-sql/rdbms_depend/ibmdb2/slapd.conf | 3 + .../rdbms_depend/ibmdb2/testdb_create.sql | 3 +- .../rdbms_depend/ibmdb2/testdb_data.sql | 6 +- .../rdbms_depend/ibmdb2/testdb_metadata.sql | 21 +- .../back-sql/rdbms_depend/pgsql/slapd.conf | 2 +- servers/slapd/back-sql/schema-map.c | 246 +++++---- servers/slapd/back-sql/schema-map.h | 28 +- servers/slapd/back-sql/search.c | 456 +++++++++------- servers/slapd/back-sql/sql-wrap.c | 46 +- servers/slapd/back-sql/util.c | 309 +++++++++-- servers/slapd/back-sql/util.h | 33 +- 21 files changed, 1886 insertions(+), 560 deletions(-) diff --git a/doc/man/man5/slapd-sql.5 b/doc/man/man5/slapd-sql.5 index 6e52f64acb..c0b55fc623 100644 --- a/doc/man/man5/slapd-sql.5 +++ b/doc/man/man5/slapd-sql.5 @@ -63,7 +63,9 @@ These three options are generally unneeded, because this information is already taken from the datasource. Use them if you need to override datasource settings. Also, several RDBMS' drivers tend to require explicit passing of user/password, -even if those are given in datasource. +even if those are given in datasource (Note: +.B dbhost +is currently ignored). .RE .TP .B subtree_cond @@ -92,18 +94,74 @@ adding and deleting entries to ldap_entries, etc. All these and subtree_cond should have the given default values. For the current value it is recommended to look at the sources, or in the log output when slapd starts with "-d 5" or greater. +Note that the parameter number and order must not be changed. .TP .B upper_func Specifies the name of a function that converts a given value to uppercase. This is used for CIS matching when the RDBMS is case sensitive. .TP +.B upper_needs_cast { yes | no } +Set this directive to +.B yes +if +.B upper_func +needs an explicit cast when applied to literal strings. The form +.B cast ( as varchar()) +is used, where +.B +is builtin. +This is +.B experimental +and may change in future releases. +.TP +.B concat_pattern +This statement defines the +.B pattern +to be used to concatenate strings. The +.B pattern +MUST contain two question marks, '?', that will be replaced +by the two strings that must be concatenated. The default value is +.BR "CONCAT(?,?)"; +a form that is known to be highly portable is +.BR "?||?", +but an explicit cast may be required when operating on literal strings: +.BR "cast(?||? as varchar())". +On some RDBMSes the form +.B "?+?" +is known to work. +Carefully check the documentation of your RDBMS or stay with the examples +for supported ones. +This is +.B experimental +and may change in future releases. +.TP .B strcast_func Specifies the name of a function that converts a given value to a string -for appropriate ordering. This is used when selecting distinct data. +for appropriate ordering. This is used in "SELECT DISTINCT" statements +for strongly typed RDBMSes with little implicit casting (like PostgreSQL), +when a literal string is specified. +This is +.B experimental +and may change in future releases. .TP .B has_ldapinfo_dn_ru { yes | no } -Explicitly inform the backend whether the SQL schema has dn_ru or not. -Overrides automatic check (required by PostgreSQL). +Explicitly inform the backend whether the SQL schema has dn_ru column +(dn in reverse uppercased form) or not. +Overrides automatic check (required by PostgreSQL/unixODBC). +This is +.B experimental +and may change in future releases. + +.TP +.B fail_if_no_mapping { yes | no } +When set to +.B yes +it forces write operations to fail if no appropriate mapping between LDAP +attributes and SQL data is available. The default behavior is to ignore +those changes that cannot be mapped correctly. +This is +.B experimental +and may change in future releases. .SH METAINFORMATION USED .LP @@ -217,7 +275,7 @@ Keytbl and keycol thus contain "persons" (name of the table), and "id" ldap_attr_mappings (some columns are not listed for clarity) ----------- id=1 - oc_id=1 + oc_map_id=1 name="cn" sel_expr="CONCAT(persons.first_name,' ',persons.last_name)" from_tbls="persons" @@ -356,7 +414,7 @@ information on this matter - they are self-explanatory for those familiar with concept expressed above. .LP .SH common techniques (referrals, multiclassing etc.) -First of all, lets remember that among other major differences to the +First of all, let's remember that among other major differences to the complete LDAP data model, the concept above does not directly support such things as multiple objectclasses per entry, and referrals. Fortunately, they are easy to adopt in this scheme. diff --git a/servers/slapd/back-sql/back-sql.h b/servers/slapd/back-sql/back-sql.h index bd349175a4..a456351479 100644 --- a/servers/slapd/back-sql/back-sql.h +++ b/servers/slapd/back-sql/back-sql.h @@ -1,6 +1,3 @@ -#ifndef __BACKSQL_H__ -#define __BACKSQL_H__ - /* * Copyright 1999, Dmitry Kovalev , 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 index f016dfabb2..a45ac6f002 100644 --- a/servers/slapd/back-sql/external.h +++ b/servers/slapd/back-sql/external.h @@ -34,6 +34,8 @@ extern BI_op_add backsql_add; extern BI_op_delete backsql_delete; extern BI_op_abandon backsql_abandon; +extern BI_operational backsql_operational; + extern BI_connection_destroy backsql_connection_destroy; LDAP_END_DECL diff --git a/servers/slapd/back-sql/init.c b/servers/slapd/back-sql/init.c index 79d8dec530..3d4e30242f 100644 --- a/servers/slapd/back-sql/init.c +++ b/servers/slapd/back-sql/init.c @@ -74,6 +74,7 @@ sql_back_initialize( bi->bi_acl_group = 0; bi->bi_acl_attribute = 0; bi->bi_chk_referrals = 0; + bi->bi_operational = backsql_operational; bi->bi_connection_init = 0; bi->bi_connection_destroy = backsql_connection_destroy; @@ -100,10 +101,10 @@ backsql_db_init( Debug( LDAP_DEBUG_TRACE, "==>backsql_db_init()\n", 0, 0, 0 ); si = (backsql_info *)ch_calloc( 1, sizeof( backsql_info ) ); + memset( si, '\0', sizeof( backsql_info ) ); ldap_pvt_thread_mutex_init( &si->dbconn_mutex ); ldap_pvt_thread_mutex_init( &si->schema_mutex ); backsql_init_db_env( si ); - si->has_ldapinfo_dn_ru = -1; bd->be_private = si; Debug( LDAP_DEBUG_TRACE, "<==backsql_db_init()\n", 0, 0, 0 ); @@ -120,11 +121,11 @@ backsql_db_destroy( ldap_pvt_thread_mutex_lock( &si->dbconn_mutex ); backsql_free_db_env( si ); ldap_pvt_thread_mutex_unlock( &si->dbconn_mutex ); + ldap_pvt_thread_mutex_destroy( &si->dbconn_mutex ); ldap_pvt_thread_mutex_lock( &si->schema_mutex ); backsql_destroy_schema_map( si ); ldap_pvt_thread_mutex_unlock( &si->schema_mutex ); ldap_pvt_thread_mutex_destroy( &si->schema_mutex ); - ldap_pvt_thread_mutex_destroy( &si->dbconn_mutex ); free( si->dbname ); free( si->dbuser ); if ( si->dbpasswd ) { @@ -133,11 +134,13 @@ backsql_db_destroy( if ( si->dbhost ) { free( si->dbhost ); } - if ( si->upper_func ) { - free( si->upper_func ); + if ( si->upper_func.bv_val ) { + free( si->upper_func.bv_val ); + free( si->upper_func_open.bv_val ); + free( si->upper_func_close.bv_val ); } - free( si->subtree_cond ); + free( si->subtree_cond.bv_val ); free( si->oc_query ); free( si->at_query ); free( si->insentry_query ); @@ -155,7 +158,7 @@ backsql_db_open( backsql_info *si = (backsql_info*)bd->be_private; Connection tmp; SQLHDBC dbh; - int idq_len; + ber_len_t idq_len; struct berval bv; Debug( LDAP_DEBUG_TRACE, "==>backsql_db_open(): " @@ -163,52 +166,194 @@ backsql_db_open( if ( si->dbname == NULL ) { Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "datasource name not specified " - "(use dbname directive in slapd.conf)\n", 0, 0, 0 ); + "(use \"dbname\" directive in slapd.conf)\n", 0, 0, 0 ); return 1; } + + if ( si->concat_func == NULL ) { + Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " + "concat func not specified (use \"concat_pattern\" " + "directive in slapd.conf)\n", 0, 0, 0 ); + + if ( backsql_split_pattern( backsql_def_concat_func, + &si->concat_func, 2 ) ) { + Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " + "unable to parse pattern '%s'", + backsql_def_concat_func, 0, 0 ); + return 1; + } + } + + /* + * Prepare cast string as required + */ + if ( si->upper_func.bv_val ) { + char buf[1024]; + + if ( BACKSQL_UPPER_NEEDS_CAST( si ) ) { + snprintf( buf, sizeof( buf ), + "%s(cast (" /* ? as varchar(%d))) */ , + si->upper_func.bv_val ); + ber_str2bv( buf, 0, 1, &si->upper_func_open ); + + snprintf( buf, sizeof( buf ), + /* (cast(? */ " as varchar(%d)))", + BACKSQL_MAX_DN_LEN ); + ber_str2bv( buf, 0, 1, &si->upper_func_close ); + + } else { + snprintf( buf, sizeof( buf ), "%s(" /* ?) */ , + si->upper_func.bv_val ); + ber_str2bv( buf, 0, 1, &si->upper_func_open ); + + ber_str2bv( /* (? */ ")", 0, 1, &si->upper_func_close ); + } + } if ( si->dbuser == NULL ) { Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "user name not specified " - "(use dbuser directive in slapd.conf)\n", 0, 0, 0 ); + "(use \"dbuser\" directive in slapd.conf)\n", 0, 0, 0 ); return 1; } - if ( si->subtree_cond == NULL ) { + if ( si->subtree_cond.bv_val == NULL ) { + /* + * Prepare concat function for subtree search condition + */ + struct berval concat; + ber_len_t len = 0; + struct berval values[] = { + { sizeof( "'%'" ) - 1, "'%'" }, + { sizeof( "?" ) - 1, "?" }, + { 0, NULL } + }; + + if ( backsql_prepare_pattern( si->concat_func, values, + &concat ) ) { + Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " + "unable to prepare CONCAT pattern", 0, 0, 0 ); + return 1; + } + Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "subtree search SQL condition not specified " - "(use subtree_cond directive in slapd.conf)\n", + "(use \"subtree_cond\" directive in slapd.conf)\n", + 0, 0, 0); + + si->subtree_cond.bv_val = NULL; + si->subtree_cond.bv_len = 0; + + if ( si->upper_func.bv_val ) { + + /* + * UPPER(ldap_entries.dn) LIKE UPPER(CONCAT('%',?)) + */ + + backsql_strfcat( &si->subtree_cond, &len, "blbbb", + &si->upper_func, + (ber_len_t)sizeof( "(ldap_entries.dn) LIKE " ) - 1, + "(ldap_entries.dn) LIKE ", + &si->upper_func_open, + &concat, + &si->upper_func_close ); + + } else { + + /* + * ldap_entries.dn LIKE CONCAT('%',?) + */ + + backsql_strfcat( &si->subtree_cond, &len, "lb", + (ber_len_t)sizeof( "ldap_entries.dn LIKE " ) - 1, + "ldap_entries.dn LIKE ", + &concat ); + } + + Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " + "setting '%s' as default\n", + si->subtree_cond.bv_val, 0, 0 ); + } + + if ( si->children_cond.bv_val == NULL ) { + /* + * Prepare concat function for children search condition + */ + struct berval concat; + ber_len_t len = 0; + struct berval values[] = { + { sizeof( "'%,'" ) - 1, "'%,'" }, + { sizeof( "?" ) - 1, "?" }, + { 0, NULL } + }; + + if ( backsql_prepare_pattern( si->concat_func, values, + &concat ) ) { + Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " + "unable to prepare CONCAT pattern", 0, 0, 0 ); + return 1; + } + + Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " + "children search SQL condition not specified " + "(use \"children_cond\" directive in slapd.conf)\n", 0, 0, 0); - if ( si->upper_func ) { - struct berval bv = { 0, NULL }; - int len = 0; - backsql_strcat( &bv, &len, si->upper_func, - backsql_def_upper_subtree_cond, NULL ); - si->subtree_cond = bv.bv_val; + si->children_cond.bv_val = NULL; + si->children_cond.bv_len = 0; + + if ( si->upper_func.bv_val ) { + + /* + * UPPER(ldap_entries.dn) LIKE UPPER(CONCAT('%,',?)) + */ + + backsql_strfcat( &si->children_cond, &len, "blbbb", + &si->upper_func, + (ber_len_t)sizeof( "(ldap_entries.dn) LIKE " ) - 1, + "(ldap_entries.dn) LIKE ", + &si->upper_func_open, + &concat, + &si->upper_func_close ); + } else { - si->subtree_cond = ch_strdup( backsql_def_subtree_cond ); + + /* + * ldap_entries.dn LIKE CONCAT('%,',?) + */ + + backsql_strfcat( &si->children_cond, &len, "lb", + (ber_len_t)sizeof( "ldap_entries.dn LIKE " ) - 1, + "ldap_entries.dn LIKE ", + &concat ); } Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "setting '%s' as default\n", - si->subtree_cond, 0, 0 ); + si->children_cond.bv_val, 0, 0 ); } if ( si->oc_query == NULL ) { + if ( BACKSQL_CREATE_NEEDS_SELECT( si ) ) { + si->oc_query = + ch_strdup( backsql_def_needs_select_oc_query ); + + } else { + si->oc_query = ch_strdup( backsql_def_oc_query ); + } + Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "objectclass mapping SQL statement not specified " - "(use oc_query directive in slapd.conf)\n", 0, 0, 0 ); + "(use \"oc_query\" directive in slapd.conf)\n", + 0, 0, 0 ); Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " - "setting '%s' by default\n", - backsql_def_oc_query, 0, 0 ); - si->oc_query = ch_strdup( backsql_def_oc_query ); + "setting '%s' by default\n", si->oc_query, 0, 0 ); } if ( si->at_query == NULL ) { Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "attribute mapping SQL statement not specified " - "(use at_query directive in slapd.conf)\n", + "(use \"at_query\" directive in slapd.conf)\n", 0, 0, 0 ); Debug(LDAP_DEBUG_TRACE, "backsql_db_open(): " "setting '%s' by default\n", @@ -219,7 +364,7 @@ backsql_db_open( if ( si->insentry_query == NULL ) { Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "entry insertion SQL statement not specified " - "(use insentry_query directive in slapd.conf)\n", + "(use \"insentry_query\" directive in slapd.conf)\n", 0, 0, 0 ); Debug(LDAP_DEBUG_TRACE, "backsql_db_open(): " "setting '%s' by default\n", @@ -230,14 +375,15 @@ backsql_db_open( if ( si->delentry_query == NULL ) { Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "entry deletion SQL statement not specified " - "(use delentry_query directive in slapd.conf)\n", + "(use \"delentry_query\" directive in slapd.conf)\n", 0, 0, 0 ); Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "setting '%s' by default\n", backsql_def_delentry_query, 0, 0 ); si->delentry_query = ch_strdup( backsql_def_delentry_query ); } - + + tmp.c_connid =- 1; if ( backsql_get_db_conn( bd, &tmp, &dbh ) != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " @@ -245,36 +391,55 @@ backsql_db_open( return 1; } + /* + * Prepare ID selection query + */ si->id_query = NULL; idq_len = 0; bv.bv_val = NULL; bv.bv_len = 0; - if ( si->upper_func == NULL ) { + if ( si->upper_func.bv_val == NULL ) { backsql_strcat( &bv, &idq_len, backsql_id_query, "dn=?", NULL ); } else { - if ( si->has_ldapinfo_dn_ru ) { + if ( BACKSQL_HAS_LDAPINFO_DN_RU( si ) ) { backsql_strcat( &bv, &idq_len, backsql_id_query, "dn_ru=?", NULL ); } else { - if ( si->isTimesTen ) { - backsql_strcat( &bv, &idq_len, + if ( BACKSQL_USE_REVERSE_DN( si ) ) { + backsql_strfcat( &bv, &idq_len, "sbl", backsql_id_query, - si->upper_func, "(dn)=?", - NULL ); + &si->upper_func, + (ber_len_t)sizeof( "(dn)=?" ) - 1, "(dn)=?" ); } else { - backsql_strcat( &bv, &idq_len, + backsql_strfcat( &bv, &idq_len, "sblbcb", backsql_id_query, - si->upper_func, "(dn)=", - si->upper_func, "(?)", NULL ); + &si->upper_func, + (ber_len_t)sizeof( "(dn)=" ) - 1, "(dn)=", + &si->upper_func_open, + '?', + &si->upper_func_close ); } } } si->id_query = bv.bv_val; + + /* + * Prepare children ID selection query + */ + si->has_children_query = NULL; + idq_len = 0; + + bv.bv_val = NULL; + bv.bv_len = 0; + backsql_strfcat( &bv, &idq_len, "sb", + "select count(*) from ldap_entries where ", + &si->children_cond ); + si->has_children_query = bv.bv_val; backsql_free_db_conn( bd, &tmp ); - if ( !si->schema_loaded ) { + if ( !BACKSQL_SCHEMA_LOADED( si ) ) { Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): " "test failed, schema map not loaded - exiting\n", 0, 0, 0 ); diff --git a/servers/slapd/back-sql/modify.c b/servers/slapd/back-sql/modify.c index 2757d33e4c..6966c2c6fd 100644 --- a/servers/slapd/back-sql/modify.c +++ b/servers/slapd/back-sql/modify.c @@ -23,29 +23,50 @@ #include "util.h" /* - * PostgreSQL doesn't work without :( + * PostgreSQL 7.0 doesn't work without :( */ #define BACKSQL_REALLOC_STMT +/* + * Skip: + * - the first occurrence of objectClass, which is used + * to determine how to bulid the SQL entry (FIXME ?!?) + * - operational attributes + * empty attributes (FIXME ?!?) + */ +#define backsql_attr_skip(ad,vals) \ + ( \ + ( (ad) == slap_schema.si_ad_objectClass \ + && (vals)[ 1 ].bv_val == NULL ) \ + || is_at_operational( (ad)->ad_type ) \ + || ( (vals)[ 0 ].bv_val == NULL ) \ + ) + static int backsql_modify_internal( backsql_info *bi, SQLHDBC dbh, backsql_oc_map_rec *oc, backsql_entryID *e_id, - Modifications *modlist ) + Modifications *modlist, + const char **text ) { RETCODE rc; SQLHSTMT sth; Modifications *ml; + int res = LDAP_SUCCESS; - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): " "traversing modifications list\n", 0, 0, 0 ); + + *text = NULL; + #ifndef BACKSQL_REALLOC_STMT SQLAllocStmt( dbh, &sth ); #endif /* BACKSQL_REALLOC_STMT */ + for ( ml = modlist; ml != NULL; ml = ml->sml_next ) { - AttributeDescription *ad; + AttributeDescription *ad; backsql_at_map_rec *at = NULL; struct berval *at_val; Modification *c_mod; @@ -60,16 +81,30 @@ backsql_modify_internal( #endif /* BACKSQL_REALLOC_STMT */ c_mod = &ml->sml_mod; - ad = c_mod->sm_desc; - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): attribute '%s'\n", - ad->ad_cname.bv_val, 0, 0 ); + + Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): " + "modifying attribute '%s'\n", + ad->ad_cname.bv_val, 0, 0 ); + + if ( backsql_attr_skip( ad, c_mod->sm_bvalues ) ) { + continue; + } + at = backsql_ad2at( oc, ad ); if ( at == NULL ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): " "attribute provided is not registered " - "in objectclass '%s'\n", + "in objectClass '%s'\n", ad->ad_cname.bv_val, 0, 0 ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_UNWILLING_TO_PERFORM; + *text = "operation not permitted " + "within namingContext"; + goto done; + } + continue; } @@ -77,58 +112,100 @@ backsql_modify_internal( case LDAP_MOD_REPLACE: { SQLHSTMT asth; BACKSQL_ROW_NTS row; - - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + + Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): " "replacing values for attribute '%s'\n", - at->name.bv_val, 0, 0 ); + at->ad->ad_cname.bv_val, 0, 0 ); if ( at->add_proc == NULL ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "add procedure is not defined " "for attribute '%s' " "- unable to perform replacements\n", - at->name.bv_val, 0, 0 ); + at->ad->ad_cname.bv_val, 0, 0 ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_UNWILLING_TO_PERFORM; + *text = "operation not permitted " + "within namingContext"; + goto done; + } + break; } if ( at->delete_proc == NULL ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "delete procedure is not defined " "for attribute '%s' " "- adding only\n", - at->name.bv_val, 0, 0 ); + at->ad->ad_cname.bv_val, 0, 0 ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_UNWILLING_TO_PERFORM; + *text = "operation not permitted " + "within namingContext"; + goto done; + } + goto add_only; } del_all: rc = backsql_Prepare( dbh, &asth, at->query, 0 ); if ( rc != SQL_SUCCESS ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "error preparing query\n", 0, 0, 0 ); backsql_PrintErrors( bi->db_env, dbh, asth, rc ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_OTHER; + *text = "SQL-backend error"; + goto done; + } + break; } rc = backsql_BindParamID( asth, 1, &e_id->keyval ); if ( rc != SQL_SUCCESS ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "error binding key value parameter\n", 0, 0, 0 ); backsql_PrintErrors( bi->db_env, dbh, asth, rc ); SQLFreeStmt( asth, SQL_DROP ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_OTHER; + *text = "SQL-backend error"; + goto done; + } + break; } rc = SQLExecute( asth ); if ( !BACKSQL_SUCCESS( rc ) ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "error executing attribute query\n", 0, 0, 0 ); backsql_PrintErrors( bi->db_env, dbh, asth, rc ); SQLFreeStmt( asth, SQL_DROP ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_OTHER; + *text = "SQL-backend error"; + goto done; + } + break; } @@ -163,19 +240,25 @@ del_all: strlen( row.cols[ i ] ), 0 ); Debug( LDAP_DEBUG_TRACE, - "backsql_modify(): " + "backsql_modify_internal(): " "executing '%s'\n", at->delete_proc, 0, 0 ); rc = SQLExecDirect( sth, at->delete_proc, SQL_NTS ); if ( rc != SQL_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, - "backsql_modify(): " + "backsql_modify_internal(): " "delete_proc " "execution failed\n", 0, 0, 0 ); backsql_PrintErrors( bi->db_env, dbh, sth, rc ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_OTHER; + *text = "SQL-backend error"; + goto done; + } } #ifdef BACKSQL_REALLOC_STMT SQLFreeStmt( sth, SQL_DROP ); @@ -194,24 +277,25 @@ del_all: case SLAP_MOD_SOFTADD: add_only:; if ( at->add_proc == NULL ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "add procedure is not defined " "for attribute '%s'\n", - at->name.bv_val, 0, 0 ); - break; - } - - if ( c_mod->sm_bvalues == NULL ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " - "no values given to add " - "for attribute '%s'\n", - at->name.bv_val, 0, 0 ); + at->ad->ad_cname.bv_val, 0, 0 ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_UNWILLING_TO_PERFORM; + *text = "operation not permitted " + "within namingContext"; + goto done; + } + break; } - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): " "adding new values for attribute '%s'\n", - at->name.bv_val, 0, 0 ); + at->ad->ad_cname.bv_val, 0, 0 ); for ( i = 0, at_val = c_mod->sm_bvalues; at_val->bv_val != NULL; i++, at_val++ ) { @@ -240,18 +324,25 @@ add_only:; 0, 0, at_val->bv_val, at_val->bv_len, 0 ); - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "executing '%s'\n", at->add_proc, 0, 0 ); rc = SQLExecDirect( sth, at->add_proc, SQL_NTS ); if ( rc != SQL_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, - "backsql_modify(): " + "backsql_modify_internal(): " "add_proc execution failed\n", 0, 0, 0 ); backsql_PrintErrors( bi->db_env, dbh, sth, rc ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_OTHER; + *text = "SQL-backend error"; + goto done; + } } #ifdef BACKSQL_REALLOC_STMT SQLFreeStmt( sth, SQL_DROP ); @@ -262,25 +353,36 @@ add_only:; case LDAP_MOD_DELETE: if ( at->delete_proc == NULL ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "delete procedure is not defined " "for attribute '%s'\n", - at->name.bv_val, 0, 0 ); + at->ad->ad_cname.bv_val, 0, 0 ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_UNWILLING_TO_PERFORM; + *text = "operation not permitted " + "within namingContext"; + goto done; + } + break; } if ( c_mod->sm_bvalues == NULL ) { - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "no values given to delete " "for attribute '%s' " "-- deleting all values\n", - at->name.bv_val, 0, 0 ); + at->ad->ad_cname.bv_val, 0, 0 ); goto del_all; } - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, "backsql_modify_internal(): " "deleting values for attribute '%s'\n", - at->name.bv_val, 0, 0 ); + at->ad->ad_cname.bv_val, 0, 0 ); + for ( i = 0, at_val = c_mod->sm_bvalues; at_val->bv_val != NULL; i++, at_val++ ) { @@ -308,18 +410,25 @@ add_only:; 0, 0, at_val->bv_val, at_val->bv_len, 0 ); - Debug( LDAP_DEBUG_TRACE, "backsql_modify(): " + Debug( LDAP_DEBUG_TRACE, + "backsql_modify_internal(): " "executing '%s'\n", at->delete_proc, 0, 0 ); rc = SQLExecDirect( sth, at->delete_proc, SQL_NTS ); if ( rc != SQL_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, - "backsql_modify(): " + "backsql_modify_internal(): " "delete_proc execution " "failed\n", 0, 0, 0 ); backsql_PrintErrors( bi->db_env, dbh, sth, rc ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + res = LDAP_OTHER; + *text = "SQL-backend error"; + goto done; + } } #ifdef BACKSQL_REALLOC_STMT SQLFreeStmt( sth, SQL_DROP ); @@ -335,6 +444,8 @@ add_only:; #endif /* BACKSQL_REALLOC_STMT */ } +done:; + #ifndef BACKSQL_REALLOC_STMT SQLFreeStmt( sth, SQL_DROP ); #endif /* BACKSQL_REALLOC_STMT */ @@ -342,7 +453,7 @@ add_only:; /* * FIXME: should fail in case one change fails? */ - return LDAP_SUCCESS; + return res; } int @@ -358,7 +469,9 @@ backsql_modify( SQLHDBC dbh; backsql_oc_map_rec *oc = NULL; backsql_entryID e_id; + Entry e; int res; + const char *text = NULL; /* * FIXME: in case part of the operation cannot be performed @@ -415,7 +528,17 @@ backsql_modify( return 1; } - res = backsql_modify_internal( bi, dbh, oc, &e_id, modlist ); + e.e_attrs = NULL; + e.e_name = *dn; + e.e_nname = *ndn; + if ( !acl_check_modlist( be, conn, op, &e, modlist )) { + res = LDAP_INSUFFICIENT_ACCESS; + + } else { + res = backsql_modify_internal( bi, dbh, oc, &e_id, + modlist, &text ); + } + if ( res == LDAP_SUCCESS ) { /* * Commit only if all operations succeed @@ -428,7 +551,7 @@ backsql_modify( */ SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT ); } - send_ldap_result( conn, op, res, "", NULL, NULL, NULL ); + send_ldap_result( conn, op, res, "", text, NULL, NULL ); Debug( LDAP_DEBUG_TRACE, "<==backsql_modify()\n", 0, 0, 0 ); return 0; @@ -460,6 +583,7 @@ backsql_modrdn( const char *text = NULL; LDAPRDN *new_rdn = NULL; LDAPRDN *old_rdn = NULL; + Entry e; Modifications *mod; Debug( LDAP_DEBUG_TRACE, "==>backsql_modrdn() renaming entry '%s', " @@ -494,9 +618,43 @@ backsql_modrdn( Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): entry id is %ld\n", e_id.id, 0, 0 ); + if ( backsql_has_children( bi, dbh, ndn ) == LDAP_COMPARE_TRUE ) { + Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): " + "entry \"%s\" has children\n", dn->bv_val, 0, 0 ); + send_ldap_result( conn, op, LDAP_NOT_ALLOWED_ON_NONLEAF, + NULL, "subtree delete not supported", + NULL, NULL ); + return 1; + } + dnParent( dn, &p_dn ); dnParent( ndn, &p_ndn ); + /* + * namingContext "" is not supported + */ + if ( p_dn.bv_len == 0 ) { + Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): " + "parent is \"\" - aborting\n", 0, 0, 0 ); + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, + "", "not allowed within namingContext", + NULL, NULL ); + goto modrdn_return; + } + + /* + * Check for children access to parent + */ + e.e_attrs = NULL; + e.e_name = p_dn; + e.e_nname = p_ndn; + if ( !access_allowed( be, conn, op, &e, slap_schema.si_ad_children, + NULL, ACL_WRITE, NULL ) ) { + Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0, 0, 0 ); + res = LDAP_INSUFFICIENT_ACCESS; + goto modrdn_return; + } + if ( newSuperior ) { /* * namingContext "" is not supported @@ -513,6 +671,21 @@ backsql_modrdn( new_pdn = newSuperior; new_npdn = nnewSuperior; + e.e_name = *new_pdn; + e.e_nname = *new_npdn; + + /* + * Check for children access to new parent + */ + if ( !access_allowed( be, conn, op, &e, + slap_schema.si_ad_children, + NULL, ACL_WRITE, NULL ) ) { + Debug( LDAP_DEBUG_TRACE, "no access to new parent\n", + 0, 0, 0 ); + res = LDAP_INSUFFICIENT_ACCESS; + goto modrdn_return; + } + } else { new_pdn = &p_dn; new_npdn = &p_ndn; @@ -611,8 +784,9 @@ backsql_modrdn( goto modrdn_return; } - /* Get attribute type and attribute value of our new rdn, we will - * need to add that to our new entry + /* + * Get attribute type and attribute value of our new rdn, + * we will need to add that to our new entry */ if ( ldap_bv2rdn( newrdn, &new_rdn, (char **)&text, LDAP_DN_FORMAT_LDAP ) ) { @@ -664,29 +838,35 @@ backsql_modrdn( } } - res = slap_modrdn2mods( NULL, NULL, NULL, NULL, old_rdn, new_rdn, + e.e_name = new_dn; + e.e_nname = new_ndn; + res = slap_modrdn2mods( be, conn, op, &e, old_rdn, new_rdn, deleteoldrdn, &mod ); if ( res != LDAP_SUCCESS ) { goto modrdn_return; } - oc = backsql_id2oc( bi, e_id.oc_id ); - res = backsql_modify_internal( bi, dbh, oc, &e_id, mod ); - - if ( res != LDAP_SUCCESS ) { + if ( !acl_check_modlist( be, conn, op, &e, mod )) { + res = LDAP_INSUFFICIENT_ACCESS; goto modrdn_return; } - /* - * Commit only if all operations succeed - * - * FIXME: backsql_modify_internal() does not fail - * if add/delete operations are not available, or - * if a multiple value add actually results in a replace, - * or if a single operation on an attribute fails for any - * reason - */ - SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT ); + oc = backsql_id2oc( bi, e_id.oc_id ); + res = backsql_modify_internal( bi, dbh, oc, &e_id, mod, &text ); + + if ( res == LDAP_SUCCESS ) { + + /* + * Commit only if all operations succeed + * + * FIXME: backsql_modify_internal() does not fail + * if add/delete operations are not available, or + * if a multiple value add actually results in a replace, + * or if a single operation on an attribute fails for any + * reason + */ + SQLTransact( SQL_NULL_HENV, dbh, SQL_COMMIT ); + } modrdn_return: SQLFreeStmt( sth, SQL_DROP ); @@ -714,7 +894,7 @@ modrdn_return: } } - send_ldap_result( conn, op, res, "", NULL, NULL, NULL ); + send_ldap_result( conn, op, res, "", text, NULL, NULL ); Debug( LDAP_DEBUG_TRACE, "<==backsql_modrdn()\n", 0, 0, 0 ); return 0; @@ -736,26 +916,43 @@ backsql_add( backsql_oc_map_rec *oc = NULL; backsql_at_map_rec *at_rec = NULL; backsql_entryID e_id, parent_id; + Entry p; int res; Attribute *at; struct berval *at_val; struct berval pdn; - /* first parameter no, parameter order */ + /* first parameter #, parameter order */ SQLUSMALLINT pno, po; /* procedure return code */ int prc; Debug( LDAP_DEBUG_TRACE, "==>backsql_add(): adding entry '%s'\n", - e->e_dn, 0, 0 ); + e->e_name.bv_val, 0, 0 ); for ( at = e->e_attrs; at != NULL; at = at->a_next ) { if ( at->a_desc == slap_schema.si_ad_objectClass ) { - /* - * FIXME: only the objectClass provided first - * is considered when creating a new entry - */ - oc = backsql_name2oc( bi, &at->a_vals[ 0 ] ); - break; + if ( global_schemacheck ) { + const char *text = NULL; + char textbuf[ 1024 ]; + size_t textlen = sizeof( textbuf ); + struct berval soc; + + int rc = structural_class( at->a_vals, &soc, + NULL, &text, textbuf, textlen ); + if ( rc != LDAP_SUCCESS ) { + break; + } + oc = backsql_name2oc( bi, &soc ); + + } else { + + /* + * FIXME: only the objectClass provided first + * is considered when creating a new entry + */ + oc = backsql_name2oc( bi, &at->a_vals[ 0 ] ); + } + break; } } @@ -777,6 +974,16 @@ backsql_add( "operation not permitted within namingContext", NULL, NULL ); return 1; + + } else if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) + && oc->create_keyval == NULL ) { + Debug( LDAP_DEBUG_TRACE, "backsql_add(): " + "create procedure needs select, but none is defined" + "- aborting\n", 0, 0, 0 ); + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, "", + "operation not permitted within namingContext", + NULL, NULL ); + return 1; } prc = backsql_get_db_conn( be, conn, &dbh ); @@ -862,6 +1069,16 @@ backsql_add( * is expected to return the id as the first column of a select */ + p.e_attrs = NULL; + p.e_name = pdn; + dnParent( &e->e_nname, &p.e_nname ); + if ( !access_allowed( be, conn, op, &p, slap_schema.si_ad_children, + NULL, ACL_WRITE, NULL ) ) { + send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, + NULL, NULL, NULL, NULL ); + return 1; + } + #ifndef BACKSQL_REALLOC_STMT rc = SQLAllocStmt( dbh, &sth ); #else /* BACKSQL_REALLOC_STMT */ @@ -899,6 +1116,27 @@ backsql_add( SWORD ncols; SQLINTEGER is_null; + if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) { +#ifndef BACKSQL_REALLOC_STMT + SQLFreeStmt( sth, SQL_RESET_PARAMS ); +#else /* BACKSQL_REALLOC_STMT */ + SQLFreeStmt( sth, SQL_DROP ); + rc = SQLAllocStmt( dbh, &sth ); + if ( rc != SQL_SUCCESS ) { + send_ldap_result( conn, op, LDAP_OTHER, "", + "SQL-backend error", NULL, NULL ); + return 1; + } +#endif /* BACKSQL_REALLOC_STMT */ + + rc = SQLExecDirect( sth, oc->create_keyval, SQL_NTS ); + if ( rc != SQL_SUCCESS ) { + send_ldap_result( conn, op, LDAP_OTHER, "", + "SQL-backend error", NULL, NULL ); + return 1; + } + } + /* * the query to know the id of the inserted entry * must be embedded in the create procedure @@ -916,8 +1154,8 @@ backsql_add( } else if ( ncols != 1 ) { Debug( LDAP_DEBUG_TRACE, "backsql_add(): " - "create_proc result is bogus\n", - 0, 0, 0 ); + "create_proc result is bogus (ncols=%d)\n", + ncols, 0, 0 ); backsql_PrintErrors( bi->db_env, dbh, sth, rc); SQLFreeStmt( sth, SQL_DROP ); send_ldap_result( conn, op, LDAP_OTHER, "", @@ -979,7 +1217,18 @@ backsql_add( for ( at = e->e_attrs; at != NULL; at = at->a_next ) { SQLUSMALLINT currpos; - if ( at->a_vals[ 0 ].bv_val == NULL ) { + Debug( LDAP_DEBUG_TRACE, "backsql_add(): " + "adding attribute '%s'\n", + at->a_desc->ad_cname.bv_val, 0, 0 ); + + /* + * Skip: + * - the first occurrence of objectClass, which is used + * to determine how to bulid the SQL entry (FIXME ?!?) + * - operational attributes + * empty attributes (FIXME ?!?) + */ + if ( backsql_attr_skip( at->a_desc, at->a_vals ) ) { continue; } @@ -991,6 +1240,16 @@ backsql_add( "in objectclass '%s'\n", at->a_desc->ad_cname.bv_val, oc->name.bv_val, 0 ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + send_ldap_result( conn, op, + LDAP_UNWILLING_TO_PERFORM, "", + "operation not permitted " + "within namingContext", + NULL, NULL ); + return 1; + } + continue; } @@ -999,12 +1258,31 @@ backsql_add( "add procedure is not defined " "for attribute '%s'\n", at->a_desc->ad_cname.bv_val, 0, 0 ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + send_ldap_result( conn, op, + LDAP_UNWILLING_TO_PERFORM, "", + "operation not permitted " + "within namingContext", + NULL, NULL ); + return 1; + } + continue; } #ifdef BACKSQL_REALLOC_STMT rc = backsql_Prepare( dbh, &sth, at_rec->add_proc, 0 ); if ( rc != SQL_SUCCESS ) { + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + send_ldap_result( conn, op, + LDAP_OTHER, "", + "SQL-backend error", + NULL, NULL ); + return 1; + } + continue; } #endif /* BACKSQL_REALLOC_STMT */ @@ -1025,10 +1303,20 @@ backsql_add( SQL_INTEGER, 0, 0, &new_keyval, 0, 0 ); currpos = pno + 2 - po; - for ( i = 0, at_val = &at->a_vals[ 0 ]; - at_val->bv_val != NULL; + for ( i = 0, at_val = &at->a_vals[ i ]; + at_val->bv_val != NULL; i++, at_val = &at->a_vals[ i ] ) { + /* + * Do not deal with the objectClass that is used + * to build the entry + */ + if ( at->a_desc == slap_schema.si_ad_objectClass ) { + if ( ber_bvcmp( at_val, &oc->name ) == 0 ) { + continue; + } + } + /* * check for syntax needed here * maybe need binary bind? @@ -1055,6 +1343,14 @@ backsql_add( "add_proc execution failed\n", 0, 0, 0 ); backsql_PrintErrors( bi->db_env, dbh, sth, rc ); + + if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) { + send_ldap_result( conn, op, + LDAP_OTHER, "", + "SQL-backend error", + NULL, NULL ); + return 1; + } } } #ifndef BACKSQL_REALLOC_STMT @@ -1072,6 +1368,7 @@ backsql_add( return 1; } #endif /* BACKSQL_REALLOC_STMT */ + backsql_BindParamStr( sth, 1, e->e_name.bv_val, BACKSQL_MAX_DN_LEN ); SQLBindParameter( sth, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &oc->id, 0, 0 ); @@ -1135,12 +1432,30 @@ backsql_delete( RETCODE rc; backsql_oc_map_rec *oc = NULL; backsql_entryID e_id; + Entry e; int res; /* first parameter no */ SQLUSMALLINT pno; Debug( LDAP_DEBUG_TRACE, "==>backsql_delete(): deleting entry '%s'\n", ndn->bv_val, 0, 0 ); + + dnParent( dn, &e.e_name ); + dnParent( ndn, &e.e_nname ); + e.e_attrs = NULL; + + /* check parent for "children" acl */ + if ( !access_allowed( be, conn, op, &e, slap_schema.si_ad_children, + NULL, ACL_WRITE, NULL ) ) { + Debug( LDAP_DEBUG_TRACE, "backsql_delete(): " + "no write access to parent\n", + 0, 0, 0 ); + send_ldap_result( conn, op, LDAP_INSUFFICIENT_ACCESS, + "", NULL, NULL, NULL ); + return 1; + + } + res = backsql_get_db_conn( be, conn, &dbh ); if ( res != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "backsql_delete(): " @@ -1160,13 +1475,32 @@ backsql_delete( return 1; } + res = backsql_has_children( bi, dbh, ndn ); + switch ( res ) { + case LDAP_COMPARE_TRUE: + Debug( LDAP_DEBUG_TRACE, "backsql_delete(): " + "entry \"%s\" has children\n", dn->bv_val, 0, 0 ); + send_ldap_result( conn, op, LDAP_NOT_ALLOWED_ON_NONLEAF, + NULL, "subtree delete not supported", + NULL, NULL ); + return 1; + + case LDAP_COMPARE_FALSE: + break; + + default: + send_ldap_result( conn, op, res, NULL, NULL, NULL, NULL ); + return 1; + } + oc = backsql_id2oc( bi, e_id.oc_id ); if ( oc == NULL ) { Debug( LDAP_DEBUG_TRACE, "backsql_delete(): " "cannot determine objectclass of entry " "-- aborting\n", 0, 0, 0 ); - send_ldap_result( conn, op, LDAP_OTHER, "", - "SQL-backend error", NULL, NULL ); + send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, "", + "operation not permitted within namingContext", + NULL, NULL ); return 1; } @@ -1175,7 +1509,7 @@ backsql_delete( "delete procedure is not defined " "for this objectclass - aborting\n", 0, 0, 0 ); send_ldap_result( conn, op, LDAP_UNWILLING_TO_PERFORM, "", - "operation not supported for required DN", + "operation not permitted within namingContext", NULL, NULL ); return 1; } diff --git a/servers/slapd/back-sql/other.c b/servers/slapd/back-sql/other.c index 57c16d546e..1629a086e3 100644 --- a/servers/slapd/back-sql/other.c +++ b/servers/slapd/back-sql/other.c @@ -16,6 +16,7 @@ #include "slap.h" #include "back-sql.h" #include "sql-wrap.h" +#include "entry-id.h" int backsql_compare( @@ -43,5 +44,70 @@ backsql_abandon( return 0; } + +/* + * sets the supported operational attributes (if required) + */ + +int +backsql_operational( + BackendDB *be, + Connection *conn, + Operation *op, + Entry *e, + AttributeName *attrs, + int opattrs, + Attribute **a ) +{ + + backsql_info *bi = (backsql_info*)be->be_private; + SQLHDBC dbh = SQL_NULL_HDBC; + Attribute **aa = a; + int rc; + + Debug( LDAP_DEBUG_TRACE, "==>backsql_operational(): entry '%s'\n", + e->e_nname.bv_val, 0, 0 ); + + + if ( opattrs || ad_inlist( slap_schema.si_ad_hasSubordinates, attrs ) ) { + rc = backsql_get_db_conn( be, conn, &dbh ); + if ( rc != LDAP_SUCCESS ) { + goto no_connection; + } + + rc = backsql_has_children( bi, dbh, &e->e_nname ); + + switch( rc ) { + case LDAP_COMPARE_TRUE: + case LDAP_COMPARE_FALSE: + *aa = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE ); + if ( *aa != NULL ) { + aa = &(*aa)->a_next; + } + rc = 0; + break; + + default: + Debug(LDAP_DEBUG_TRACE, + "backsql_operational(): " + "has_children failed( %d)\n", + rc, 0, 0 ); + rc = 1; + break; + } + } + + return rc; + +no_connection:; + Debug( LDAP_DEBUG_TRACE, "backsql_operational(): " + "could not get connection handle - exiting\n", + 0, 0, 0 ); + send_ldap_result( conn, op, rc, "", + rc == LDAP_OTHER ? "SQL-backend error" : "", + NULL, NULL ); + return 1; +} + #endif /* SLAPD_SQL */ diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql index ca19bb1ed1..c82e75002e 100644 --- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql @@ -6,6 +6,7 @@ create table ldap_oc_mappings keytbl varchar(64) not null, keycol varchar(64) not null, create_proc varchar(255), + create_keyval varchar(255), delete_proc varchar(255), expect_return integer not null ); diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf b/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf index 86661da25d..556470cb53 100644 --- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf @@ -30,4 +30,7 @@ dbpasswd ibmdb2 subtree_cond "upper(ldap_entries.dn) LIKE CONCAT('%',?)" insentry_query "insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select max(id)+1 from ldap_entries),?,?,?,?)" upper_func "upper" +upper_needs_cast "yes" +create_needs_select "yes" +has_ldapinfo_dn_ru "no" diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql index a1da4a1d5b..137d91f326 100644 --- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql @@ -1,7 +1,8 @@ drop table persons; CREATE TABLE persons ( id int NOT NULL, - name varchar(255) NOT NULL + name varchar(255) NOT NULL, + surname varchar(255) NOT NULL ); drop table institutes; diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql index f141f414eb..623d81990d 100644 --- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql @@ -1,8 +1,8 @@ insert into institutes (id,name) values (1,'sql'); -insert into persons (id,name) values (1,'Mitya Kovalev'); -insert into persons (id,name) values (2,'Torvlobnor Puzdoy'); -insert into persons (id,name) values (3,'Akakiy Zinberstein'); +insert into persons (id,name,surname) values (1,'Mitya','Kovalev'); +insert into persons (id,name,surname) values (2,'Torvlobnor','Puzdoy'); +insert into persons (id,name,surname) values (3,'Akakiy','Zinberstein'); insert into phones (id,phone,pers_id) values (1,'332-2334',1); insert into phones (id,phone,pers_id) values (2,'222-3234',1); diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql index c38036ba20..e538f19e7d 100644 --- a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql +++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql @@ -1,24 +1,27 @@ --mappings -insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return) -values (1,'inetOrgPerson','persons','id','insert into persons (name) values ('''');\n select last_insert_id();',NULL,0); +insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return) +values (1,'inetOrgPerson','persons','id','insert into persons (id,name,surname) values ((select max(id)+1 from persons),'''','''')','select max(id) from persons',NULL,0); -insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return) -values (2,'document','documents','id',NULL,NULL,0); +insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return) +values (2,'document','documents','id',NULL,NULL,NULL,0); -insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return) -values (3,'organization','institutes','id',NULL,NULL,0); +insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return) +values (3,'organization','institutes','id',NULL,NULL,NULL,0); insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) -values (1,1,'cn','persons.name','persons',NULL,NULL,NULL,3,0); +values (1,1,'cn','case when persons.name!='''' and persons.surname!='''' then persons.name||'' ''||persons.surname when persons.surname!='''' then persons.surname when persons.name!='''' then persons.name else '''' end','persons',NULL,NULL,NULL,3,0); insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (2,1,'telephoneNumber','phones.phone','persons,phones', 'phones.pers_id=persons.id',NULL,NULL,3,0); insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) -values (3,1,'sn','persons.name','persons',NULL,NULL,NULL,3,0); +values (3,1,'sn','persons.surname','persons',NULL,'update persons set surname=? where id=?',NULL,3,0); + +insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) +values (6,1,'givenName','persons.name','persons',NULL,'update persons set name=? where id=?',NULL,3,0); insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (4,2,'description','documents.abstract','documents',NULL,NULL,NULL,3,0); @@ -35,7 +38,7 @@ insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where, values (7,3,'o','institutes.name','institutes',NULL,NULL,NULL,3,0); insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) -values (8,1,'documentDN','ldap_entries.dn','ldap_entries,documents,authors_docs,persons', +values (8,1,'documentAuthor','ldap_entries.dn','ldap_entries,documents,authors_docs,persons', 'ldap_entries.keyval=documents.id AND ldap_entries.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id', NULL,NULL,3,0); diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf b/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf index 3a5dbb5ed8..aecc890597 100644 --- a/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf +++ b/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf @@ -27,9 +27,9 @@ rootpw secret dbname PostgreSQL dbuser postgres dbpasswd postgres -subtree_cond "upper(ldap_entries.dn) LIKE '%'||?" insentry_query "insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select max(id)+1 from ldap_entries),?,?,?,?)" upper_func "upper" strcast_func "text" +concat_pattern "?||?" has_ldapinfo_dn_ru no diff --git a/servers/slapd/back-sql/schema-map.c b/servers/slapd/back-sql/schema-map.c index cf765adc93..eaa63ab692 100644 --- a/servers/slapd/back-sql/schema-map.c +++ b/servers/slapd/back-sql/schema-map.c @@ -73,16 +73,25 @@ backsql_make_attr_query( backsql_at_map_rec *at_map ) { struct berval tmps = { 0, NULL }; - int tmpslen = 0; - - backsql_strcat( &tmps, &tmpslen, "SELECT ", at_map->sel_expr, - " AS ", at_map->name.bv_val, - " FROM ", at_map->from_tbls, - " WHERE ", oc_map->keytbl,".", oc_map->keycol, - "=?", NULL ); - if ( at_map->join_where != NULL ) { - backsql_strcat( &tmps, &tmpslen, " AND ", - at_map->join_where, NULL ); + ber_len_t tmpslen = 0; + + backsql_strfcat( &tmps, &tmpslen, "lblblblbcbl", + (ber_len_t)sizeof( "SELECT " ) - 1, "SELECT ", + &at_map->sel_expr, + (ber_len_t)sizeof( " AS " ) - 1, " AS ", + &at_map->ad->ad_cname, + (ber_len_t)sizeof( " FROM " ) - 1, " FROM ", + &at_map->from_tbls, + (ber_len_t)sizeof( " WHERE " ) - 1, " WHERE ", + &oc_map->keytbl, + '.', + &oc_map->keycol, + (ber_len_t)sizeof( "=?" ) - 1, "=?" ); + + if ( at_map->join_where.bv_val != NULL ) { + backsql_strfcat( &tmps, &tmpslen, "lb", + (ber_len_t)sizeof( " AND ") - 1, " AND ", + &at_map->join_where ); } at_map->query = tmps.bv_val; @@ -94,30 +103,34 @@ static int backsql_add_sysmaps( backsql_oc_map_rec *oc_map ) { backsql_at_map_rec *at_map; - int len; - char s[ 30 ]; - struct berval bv; + char s[ 30 ]; + ber_len_t len, slen; + snprintf( s, sizeof( s ), "%ld", oc_map->id ); + slen = strlen( s ); at_map = (backsql_at_map_rec *)ch_calloc(1, sizeof( backsql_at_map_rec ) ); at_map->ad = slap_schema.si_ad_objectClass; - ber_dupbv( &at_map->name, &at_map->ad->ad_cname ); - at_map->sel_expr = ch_strdup( "ldap_entry_objclasses.oc_name" ); - at_map->from_tbls = ch_strdup( "ldap_entry_objclasses,ldap_entries" ); - len = strlen( at_map->from_tbls ); - backsql_merge_from_clause( &at_map->from_tbls, &len, oc_map->keytbl ); + ber_str2bv( "ldap_entry_objclasses.oc_name", 0, 1, &at_map->sel_expr ); + ber_str2bv( "ldap_entry_objclasses,ldap_entries", 0, 1, + &at_map->from_tbls ); + len = at_map->from_tbls.bv_len + 1; + backsql_merge_from_clause( &at_map->from_tbls, &len, &oc_map->keytbl ); len = 0; - bv.bv_val = NULL; - bv.bv_len = 0; - backsql_strcat( &bv, &len, - "ldap_entries.id=ldap_entry_objclasses.entry_id " - "and ldap_entries.keyval=", - oc_map->keytbl, ".", oc_map->keycol, - " and ldap_entries.oc_map_id=", s, NULL ); - at_map->join_where = bv.bv_val; + at_map->join_where.bv_val = NULL; + at_map->join_where.bv_len = 0; + backsql_strfcat( &at_map->join_where, &len, "lbcbll", + (ber_len_t)sizeof( "ldap_entries.id=ldap_entry_objclasses.entry_id and ldap_entries.keyval=" ) - 1, + "ldap_entries.id=ldap_entry_objclasses.entry_id and ldap_entries.keyval=", + &oc_map->keytbl, + '.', + &oc_map->keycol, + (ber_len_t)sizeof( " and ldap_entries.oc_map_id=" ) - 1, + " and ldap_entries.oc_map_id=", + slen, s ); at_map->add_proc = NULL; at_map->delete_proc = NULL; @@ -130,21 +143,23 @@ backsql_add_sysmaps( backsql_oc_map_rec *oc_map ) at_map = (backsql_at_map_rec *)ch_calloc( 1, sizeof( backsql_at_map_rec ) ); at_map->ad = slap_schema.si_ad_ref; - ber_dupbv( &at_map->name, &at_map->ad->ad_cname ); - at_map->sel_expr = ch_strdup( "ldap_referrals.url" ); - at_map->from_tbls = ch_strdup( "ldap_referrals,ldap_entries" ); - len = strlen( at_map->from_tbls ); - backsql_merge_from_clause( &at_map->from_tbls, &len,oc_map->keytbl ); + ber_str2bv( "ldap_referrals.url", 0, 1, &at_map->sel_expr ); + ber_str2bv( "ldap_referrals,ldap_entries", 0, 1, &at_map->from_tbls ); + len = at_map->from_tbls.bv_len + 1; + backsql_merge_from_clause( &at_map->from_tbls, &len, &oc_map->keytbl ); len = 0; - bv.bv_val = NULL; - bv.bv_len = 0; - backsql_strcat( &bv, &len, - "ldap_entries.id=ldap_referrals.entry_id " - "and ldap_entries.keyval=", - oc_map->keytbl, ".", oc_map->keycol, - " and ldap_entries.oc_map_id=", s, NULL ); - at_map->join_where = bv.bv_val; + at_map->join_where.bv_val = NULL; + at_map->join_where.bv_len = 0; + backsql_strfcat( &at_map->join_where, &len, "lbcbll", + (ber_len_t)sizeof( "ldap_entries.id=ldap_referrals.entry_id and ldap_entries.keyval=" ) - 1, + "ldap_entries.id=ldap_referrals.entry_id and ldap_entries.keyval=", + &oc_map->keytbl, + '.', + &oc_map->keycol, + (ber_len_t)sizeof( " and ldap_entries.oc_map_id=" ) - 1, + " and ldap_entries.oc_map_id=", + slen, s ); at_map->add_proc = NULL; at_map->delete_proc = NULL; @@ -166,23 +181,23 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) unsigned long oc_id; backsql_oc_map_rec *oc_map; backsql_at_map_rec *at_map; - char *tmps; - int tmpslen; Debug( LDAP_DEBUG_TRACE, "==>load_schema_map()\n", 0, 0, 0 ); /* * TimesTen : See if the ldap_entries.dn_ru field exists in the schema */ - if ( si->has_ldapinfo_dn_ru == -1 ) { + if ( !BACKSQL_DONTCHECK_LDAPINFO_DN_RU( si ) ) { rc = backsql_Prepare( dbh, &oc_sth, backsql_check_dn_ru_query, 0 ); if ( rc == SQL_SUCCESS ) { - si->has_ldapinfo_dn_ru = 1; /* Yes, the field exists */ + /* Yes, the field exists */ + si->bsql_flags |= BSQLF_HAS_LDAPINFO_DN_RU; Debug( LDAP_DEBUG_TRACE, "ldapinfo.dn_ru field exists " "in the schema\n", 0, 0, 0 ); } else { - si->has_ldapinfo_dn_ru = 0; /* No such field exists */ + /* No such field exists */ + si->bsql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU; } SQLFreeStmt( oc_sth, SQL_DROP ); @@ -228,10 +243,12 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) backsql_BindRowAsStrings( oc_sth, &oc_row ); rc = SQLFetch( oc_sth ); for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( oc_sth ) ) { + int colnum; + oc_map = (backsql_oc_map_rec *)ch_calloc( 1, sizeof( backsql_oc_map_rec ) ); - oc_map->id = atoi( oc_row.cols[ 0 ] ); + oc_map->id = strtol( oc_row.cols[ 0 ], NULL, 0 ); ber_str2bv( oc_row.cols[ 1 ], 0, 1, &oc_map->name ); oc_map->oc = oc_bvfind( &oc_map->name ); @@ -242,13 +259,21 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) return LDAP_OTHER; /* undefined objectClass ? */ } - oc_map->keytbl = ch_strdup( oc_row.cols[ 2 ] ); - oc_map->keycol = ch_strdup( oc_row.cols[ 3 ] ); + ber_str2bv( oc_row.cols[ 2 ], 0, 1, &oc_map->keytbl ); + ber_str2bv( oc_row.cols[ 3 ], 0, 1, &oc_map->keycol ); oc_map->create_proc = ( oc_row.is_null[ 4 ] < 0 ) ? NULL : ch_strdup( oc_row.cols[ 4 ] ); - oc_map->delete_proc = ( oc_row.is_null[ 5 ] < 0 ) ? NULL - : ch_strdup( oc_row.cols[ 5 ] ); - oc_map->expect_return = atoi( oc_row.cols[ 6 ] ); + + colnum = 5; + if ( BACKSQL_CREATE_NEEDS_SELECT( si ) ) { + colnum = 6; + oc_map->create_keyval = ( oc_row.is_null[ 5 ] < 0 ) + ? NULL : ch_strdup( oc_row.cols[ 5 ] ); + } + oc_map->delete_proc = ( oc_row.is_null[ colnum ] < 0 ) ? NULL + : ch_strdup( oc_row.cols[ colnum ] ); + oc_map->expect_return = strtol( oc_row.cols[ colnum + 1 ], + NULL, 0 ); /* * FIXME: first attempt to check for offending @@ -263,17 +288,22 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) oc_id = oc_map->id; Debug( LDAP_DEBUG_TRACE, "load_schema_map(): " "objectClass '%s': keytbl='%s' keycol='%s'\n", - oc_map->name.bv_val, oc_map->keytbl, oc_map->keycol ); + oc_map->name.bv_val, + oc_map->keytbl.bv_val, oc_map->keycol.bv_val ); if ( oc_map->create_proc ) { Debug( LDAP_DEBUG_TRACE, "create_proc='%s'\n", oc_map->create_proc, 0, 0 ); } + if ( oc_map->create_keyval ) { + Debug( LDAP_DEBUG_TRACE, "create_keyval='%s'\n", + oc_map->create_keyval, 0, 0 ); + } if ( oc_map->delete_proc ) { Debug( LDAP_DEBUG_TRACE, "delete_proc='%s'\n", oc_map->delete_proc, 0, 0 ); } Debug( LDAP_DEBUG_TRACE, "expect_return: " - "add=%s, del=%s; attributes:\n", + "add=%d, del=%d; attributes:\n", BACKSQL_IS_ADD( oc_map->expect_return ), BACKSQL_IS_DEL( oc_map->expect_return ), 0 ); @@ -293,6 +323,8 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) rc = SQLFetch( at_sth ); for ( ; BACKSQL_SUCCESS(rc); rc = SQLFetch( at_sth ) ) { const char *text = NULL; + struct berval bv; + ber_len_t tmpslen; Debug( LDAP_DEBUG_TRACE, "********'%s'\n", at_row.cols[ 0 ], 0, 0 ); @@ -310,33 +342,44 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) at_row.cols[ 8 ], 0, 0 ); at_map = (backsql_at_map_rec *)ch_calloc( 1, sizeof( backsql_at_map_rec ) ); - ber_str2bv( at_row.cols[ 0 ], 0, 1, &at_map->name ); - rc = slap_bv2ad( &at_map->name, &at_map->ad, &text ); + rc = slap_str2ad( at_row.cols[ 0 ], + &at_map->ad, &text ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_TRACE, "load_schema_map(): " "attribute '%s' for objectClass '%s' " "is not defined in schema: %s\n", - at_map->name.bv_val, + at_map->ad->ad_cname.bv_val, oc_map->name.bv_val, text ); return LDAP_CONSTRAINT_VIOLATION; } - at_map->sel_expr = ch_strdup( at_row.cols[ 1 ] ); - at_map->sel_expr_u = ( at_row.is_null[ 8 ] < 0 ) ? NULL - : ch_strdup( at_row.cols[ 8 ] ); - tmps = NULL; + ber_str2bv( at_row.cols[ 1 ], 0, 1, &at_map->sel_expr ); + if ( at_row.is_null[ 8 ] < 0 ) { + at_map->sel_expr_u.bv_val = NULL; + at_map->sel_expr_u.bv_len = 0; + } else { + ber_str2bv( at_row.cols[ 8 ], 0, 1, + &at_map->sel_expr_u ); + } tmpslen = 0; - backsql_merge_from_clause( &tmps, &tmpslen, - at_row.cols[ 2 ] ); - at_map->from_tbls = tmps; - at_map->join_where = ( at_row.is_null[ 3 ] < 0 ) ? NULL - : ch_strdup( at_row.cols[ 3 ] ); + ber_str2bv( at_row.cols[ 2 ], 0, 0, &bv ); + backsql_merge_from_clause( &at_map->from_tbls, + &tmpslen, &bv ); + if ( at_row.is_null[ 3 ] < 0 ) { + at_map->join_where.bv_val = NULL; + at_map->join_where.bv_len = 0; + } else { + ber_str2bv( at_row.cols[ 3 ], 0, 1, + &at_map->join_where ); + } at_map->add_proc = ( at_row.is_null[ 4 ] < 0 ) ? NULL : ch_strdup( at_row.cols[4] ); at_map->delete_proc = ( at_row.is_null[ 5 ] < 0 ) ? NULL : ch_strdup( at_row.cols[ 5 ] ); - at_map->param_order = atoi( at_row.cols[ 6 ] ); - at_map->expect_return = atoi( at_row.cols[ 7 ] ); + at_map->param_order = strtol( at_row.cols[ 6 ], + NULL, 0 ); + at_map->expect_return = strtol( at_row.cols[ 7 ], + NULL, 0 ); backsql_make_attr_query( oc_map, at_map ); Debug( LDAP_DEBUG_TRACE, "load_schema_map(): " "preconstructed query '%s'\n", @@ -350,7 +393,7 @@ backsql_load_schema_map( backsql_info *si, SQLHDBC dbh ) backsql_FreeRow( &oc_row ); SQLFreeStmt( at_sth, SQL_DROP ); SQLFreeStmt( oc_sth, SQL_DROP ); - si->schema_loaded = 1; + si->bsql_flags |= BSQLF_SCHEMA_LOADED; Debug( LDAP_DEBUG_TRACE, "<==load_schema_map()\n", 0, 0, 0 ); return LDAP_SUCCESS; } @@ -360,16 +403,16 @@ backsql_oc2oc( backsql_info *si, ObjectClass *oc ) { backsql_oc_map_rec tmp, *res; -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>backsql_oc2oc(): " "searching for objectclass with name='%s'\n", objclass, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ tmp.oc = oc; res = (backsql_oc_map_rec *)avl_find( si->oc_by_oc, &tmp, (AVL_CMP)backsql_cmp_oc ); -#if 0 +#ifdef BACKSQL_TRACE if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): " "found name='%s', id=%d\n", res->name, res->id, 0 ); @@ -377,7 +420,7 @@ backsql_oc2oc( backsql_info *si, ObjectClass *oc ) Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): " "not found\n", 0, 0, 0 ); } -#endif +#endif /* BACKSQL_TRACE */ return res; } @@ -390,11 +433,11 @@ backsql_name2oc( backsql_info *si, struct berval *oc_name ) { backsql_oc_map_rec tmp, *res; -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>oc_with_name(): " "searching for objectclass with name='%s'\n", objclass, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ tmp.oc = oc_bvfind( oc_name ); if ( tmp.oc == NULL ) { @@ -403,7 +446,7 @@ backsql_name2oc( backsql_info *si, struct berval *oc_name ) res = (backsql_oc_map_rec *)avl_find( si->oc_by_oc, &tmp, (AVL_CMP)backsql_cmp_oc ); -#if 0 +#ifdef BACKSQL_TRACE if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): " "found name='%s', id=%d\n", res->name, res->id, 0 ); @@ -411,7 +454,7 @@ backsql_name2oc( backsql_info *si, struct berval *oc_name ) Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): " "not found\n", 0, 0, 0 ); } -#endif +#endif /* BACKSQL_TRACE */ return res; } @@ -421,16 +464,16 @@ backsql_id2oc( backsql_info *si, unsigned long id ) { backsql_oc_map_rec tmp, *res; -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>oc_with_id(): " "searching for objectclass with id='%d'\n", id, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ tmp.id = id; res = (backsql_oc_map_rec *)avl_find( si->oc_by_id, &tmp, (AVL_CMP)backsql_cmp_oc_id ); -#if 0 +#ifdef BACKSQL_TRACE if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): " "found name='%s', id=%d\n", res->name, res->id, 0 ); @@ -438,7 +481,7 @@ backsql_id2oc( backsql_info *si, unsigned long id ) Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): " "not found\n", 0, 0, 0 ); } -#endif +#endif /* BACKSQL_TRACE */ return res; } @@ -448,25 +491,26 @@ backsql_ad2at( backsql_oc_map_rec* objclass, AttributeDescription *ad ) { backsql_at_map_rec tmp, *res; -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>backsql_ad2at(): " "searching for attribute '%s' for objectclass '%s'\n", attr, objclass->name, 0 ); -#endif +#endif /* BACKSQL_TRACE */ + tmp.ad = ad; res = (backsql_at_map_rec *)avl_find( objclass->attrs, &tmp, (AVL_CMP)backsql_cmp_attr ); -#if 0 +#ifdef BACKSQL_TRACE if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): " "found name='%s', sel_expr='%s'\n", - res->name, res->sel_expr, 0 ); + res->name, res->sel_expr.bv_val, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): " "not found\n", 0, 0, 0 ); } -#endif +#endif /* BACKSQL_TRACE */ return res; } @@ -480,11 +524,11 @@ backsql_name2at( backsql_oc_map_rec* objclass, struct berval *attr ) backsql_at_map_rec tmp, *res; const char *text = NULL; -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>backsql_name2at(): " "searching for attribute '%s' for objectclass '%s'\n", attr, objclass->name, 0 ); -#endif +#endif /* BACKSQL_TRACE */ if ( slap_bv2ad( attr, &tmp.ad, &text ) != LDAP_SUCCESS ) { return NULL; @@ -493,16 +537,16 @@ backsql_name2at( backsql_oc_map_rec* objclass, struct berval *attr ) res = (backsql_at_map_rec *)avl_find( objclass->attrs, &tmp, (AVL_CMP)backsql_cmp_attr ); -#if 0 +#ifdef BACKSQL_TRACE if ( res != NULL ) { Debug( LDAP_DEBUG_TRACE, "<==backsql_name2at(): " "found name='%s', sel_expr='%s'\n", - res->name, res->sel_expr, 0 ); + res->name, res->sel_expr.bv_val, 0 ); } else { Debug( LDAP_DEBUG_TRACE, "<==backsql_name2at(): " "not found\n", 0, 0, 0 ); } -#endif +#endif /* BACKSQL_TRACE */ return res; } @@ -511,14 +555,13 @@ static void backsql_free_attr( backsql_at_map_rec *at ) { Debug( LDAP_DEBUG_TRACE, "==>free_attr(): '%s'\n", - at->name.bv_val, 0, 0 ); - ch_free( at->name.bv_val ); - ch_free( at->sel_expr ); - if ( at->from_tbls != NULL ) { - ch_free( at->from_tbls ); + at->ad->ad_cname.bv_val, 0, 0 ); + ch_free( at->sel_expr.bv_val ); + if ( at->from_tbls.bv_val != NULL ) { + ch_free( at->from_tbls.bv_val ); } - if ( at->join_where != NULL ) { - ch_free( at->join_where ); + if ( at->join_where.bv_val != NULL ) { + ch_free( at->join_where.bv_val ); } if ( at->add_proc != NULL ) { ch_free( at->add_proc ); @@ -531,8 +574,8 @@ backsql_free_attr( backsql_at_map_rec *at ) } /* TimesTen */ - if ( at->sel_expr_u ) { - ch_free( at->sel_expr_u ); + if ( at->sel_expr_u.bv_val ) { + ch_free( at->sel_expr_u.bv_val ); } ch_free( at ); @@ -547,11 +590,14 @@ backsql_free_oc( backsql_oc_map_rec *oc ) oc->name.bv_val, 0, 0 ); avl_free( oc->attrs, (AVL_FREE)backsql_free_attr ); ch_free( oc->name.bv_val ); - ch_free( oc->keytbl ); - ch_free( oc->keycol ); + ch_free( oc->keytbl.bv_val ); + ch_free( oc->keycol.bv_val ); if ( oc->create_proc != NULL ) { ch_free( oc->create_proc ); } + if ( oc->create_keyval != NULL ) { + ch_free( oc->create_keyval ); + } if ( oc->delete_proc != NULL ) { ch_free( oc->delete_proc ); } diff --git a/servers/slapd/back-sql/schema-map.h b/servers/slapd/back-sql/schema-map.h index 27f07278ef..7c7f0cf39e 100644 --- a/servers/slapd/back-sql/schema-map.h +++ b/servers/slapd/back-sql/schema-map.h @@ -10,14 +10,23 @@ * in file LICENSE in the top-level directory of the distribution. */ - typedef struct { + /* + * FIXME: we explicitly keep the objectClass name because + * the ObjectClass structure does not use bervals (yet?) + */ struct berval name; + /* + * Structure of corresponding LDAP objectClass definition + */ ObjectClass *oc; - char *keytbl; - char *keycol; + struct berval keytbl; + struct berval keycol; /* expected to return keyval of newly created entry */ char *create_proc; + /* in case create_proc does not return the keyval of the newly + * created row */ + char *create_keyval; /* supposed to expect keyval as parameter and delete * all the attributes as well */ char *delete_proc; @@ -29,12 +38,11 @@ typedef struct { } backsql_oc_map_rec; typedef struct { - /* literal name of corresponding LDAP attribute type */ - struct berval name; + /* Description of corresponding LDAP attribute type */ AttributeDescription *ad; - char *from_tbls; - char *join_where; - char *sel_expr; + struct berval from_tbls; + struct berval join_where; + struct berval sel_expr; /* supposed to expect 2 binded values: entry keyval * and attr. value to add, like "add_name(?,?,?)" */ char *add_proc; @@ -45,7 +53,7 @@ typedef struct { * is preconstructed from parts on schemamap load time */ char *query; /* following flags are bitmasks (first bit used for add_proc, - * second - for modify, third - for delete_proc) */ + * second - for delete_proc) */ /* order of parameters for procedures above; * 1 means "data then keyval", 0 means "keyval then data" */ int param_order; @@ -54,7 +62,7 @@ typedef struct { * for return code) */ int expect_return; /* TimesTen */ - char *sel_expr_u; + struct berval sel_expr_u; } backsql_at_map_rec; /* defines to support bitmasks above */ diff --git a/servers/slapd/back-sql/search.c b/servers/slapd/back-sql/search.c index b8f5e143b0..ffd1856fd8 100644 --- a/servers/slapd/back-sql/search.c +++ b/servers/slapd/back-sql/search.c @@ -23,14 +23,6 @@ #include "entry-id.h" #include "util.h" -static struct berval AllUser = BER_BVC( LDAP_ALL_USER_ATTRIBUTES ); -static struct berval AllOper = BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ); -static struct berval NoAttrs = BER_BVC( LDAP_NO_ATTRS ); - -#if 0 -static struct berval NoAttrs = BER_BVC( LDAP_NO_ATTRS ); -#endif - static int backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad ) { @@ -102,6 +94,7 @@ backsql_init_search( bsi->be = be; bsi->conn = conn; bsi->op = op; + bsi->attr_flags = 0; /* * FIXME: need to discover how to deal with 1.1 (NoAttrs) @@ -123,8 +116,11 @@ backsql_init_search( /* * ignore "+" */ - if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 - || BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) { + if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) { + continue; + + } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) { + bsi->attr_flags |= BSQL_SF_ALL_OPER; continue; } @@ -161,8 +157,8 @@ backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op ) if ( !f ) { return 0; } - - backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, "(", NULL ); + + backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", '(' /* ) */ ); while ( 1 ) { res = backsql_process_filter( bsi, f ); @@ -181,18 +177,20 @@ backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op ) switch ( op ) { case LDAP_FILTER_AND: - backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, - " AND ", NULL ); + backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l", + (ber_len_t)sizeof( " AND " ) - 1, + " AND " ); break; case LDAP_FILTER_OR: - backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, - " OR ", NULL ); + backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l", + (ber_len_t)sizeof( " OR " ) - 1, + " OR " ); break; } } - backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, /* ( */ ")", NULL ); + backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", /* ( */ ')' ); return 1; } @@ -209,63 +207,75 @@ backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f ) at = backsql_ad2at( bsi->oc, f->f_sub_desc ); + assert( at ); + /* * When dealing with case-sensitive strings * we may omit normalization; however, normalized * SQL filters are more liberal. */ - backsql_strcat( &bsi->flt_where, &bsi->fwhere_len, "(" /* ) */ , NULL ); + backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", '(' /* ) */ ); /* TimesTen */ - Debug( LDAP_DEBUG_TRACE, "expr: '%s' '%s'\n", at->sel_expr, - at->sel_expr_u ? at->sel_expr_u : "", 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..bbc2374d09 100644 --- a/servers/slapd/back-sql/sql-wrap.c +++ b/servers/slapd/back-sql/sql-wrap.c @@ -64,16 +64,16 @@ backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, char *query, int timeout ) return rc; } -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>_SQLPrepare()\n", 0, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ SQLGetInfo( dbh, SQL_DRIVER_NAME, drv_name, sizeof( drv_name ), &len ); -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "_SQLPrepare(): driver name='%s'\n", drv_name, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ ldap_pvt_str2upper( drv_name ); if ( !strncmp( drv_name, "SQLSRV32.DLL", sizeof( drv_name ) ) ) { @@ -107,10 +107,10 @@ backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, char *query, int timeout ) } } -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "<==_SQLPrepare() calling SQLPrepare()\n", 0, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ return SQLPrepare( *sth, query, SQL_NTS ); } @@ -153,23 +153,23 @@ backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row ) return SQL_ERROR; } -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==> backsql_BindRowAsStrings()\n", 0, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ rc = SQLNumResultCols( sth, &row->ncols ); if ( rc != SQL_SUCCESS ) { -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "_SQLBindRowAsStrings(): " "SQLNumResultCols() failed:\n", 0, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC, sth, rc ); } else { -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: " "ncols=%d\n", (int)row->ncols, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ row->col_names = (BerVarray)ch_calloc( row->ncols + 1, sizeof( struct berval ) ); @@ -185,11 +185,11 @@ backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row ) &name_len, &col_type, &col_prec, &col_scale, &col_null ); ber_str2bv( colname, 0, 1, &row->col_names[ i - 1 ] ); -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: " "col_name=%s, col_prec[%d]=%d\n", colname, (int)i, (int)col_prec ); -#endif +#endif /* BACKSQL_TRACE */ if ( col_type == SQL_LONGVARCHAR || col_type == SQL_LONGVARBINARY) { #if 0 @@ -229,9 +229,10 @@ backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row ) row->col_names[ i - 1 ].bv_len = 0; row->cols[ i - 1 ] = NULL; } -#if 0 + +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "<== backsql_BindRowAsStrings()\n", 0, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ return rc; } @@ -305,10 +306,11 @@ int backsql_free_db_env( backsql_info *si ) { Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_env()\n", 0, 0, 0 ); -#if 0 + +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "free_db_env(): delete AVL tree here!!!\n", 0, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ /* * stop, if frontend waits for all threads to shutdown @@ -366,7 +368,8 @@ backsql_open_db_conn( backsql_info *si, int ldap_cid, backsql_db_conn **pdbc ) * See if this connection is to TimesTen. If it is, * remember that fact for later use. */ - si->isTimesTen = 0; /* Assume until proven otherwise */ + /* Assume until proven otherwise */ + si->bsql_flags &= ~BSQLF_USE_REVERSE_DN; DBMSName[ 0 ] = '\0'; rc = SQLGetInfo( dbc->dbh, SQL_DBMS_NAME, (PTR)&DBMSName, sizeof( DBMSName ), NULL ); @@ -375,7 +378,7 @@ backsql_open_db_conn( backsql_info *si, int ldap_cid, backsql_db_conn **pdbc ) strcmp( DBMSName, "Front-Tier" ) == 0 ) { Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: " "TimesTen database!\n", 0, 0, 0 ); - si->isTimesTen = 1; + si->bsql_flags |= BSQLF_USE_REVERSE_DN; } } else { Debug( LDAP_DEBUG_TRACE, "backsql_open_db_conn: " @@ -454,11 +457,12 @@ backsql_get_db_conn( Backend *be, Connection *ldapc, SQLHDBC *dbh ) } ldap_pvt_thread_mutex_lock( &si->schema_mutex ); - if ( !si->schema_loaded ) { + if ( !BACKSQL_SCHEMA_LOADED( si ) ) { Debug( LDAP_DEBUG_TRACE, "backsql_get_db_conn(): " "first call -- reading schema map\n", 0, 0, 0 ); rc = backsql_load_schema_map( si, dbc->dbh ); if ( rc != LDAP_SUCCESS ) { + ldap_pvt_thread_mutex_unlock( &si->schema_mutex ); backsql_free_db_conn( be, ldapc ); return rc; } diff --git a/servers/slapd/back-sql/util.c b/servers/slapd/back-sql/util.c index 48701c6aa4..ffd10efc95 100644 --- a/servers/slapd/back-sql/util.c +++ b/servers/slapd/back-sql/util.c @@ -23,10 +23,17 @@ #include "schema-map.h" #include "util.h" +#define BACKSQL_MAX(a,b) ((a)>(b)?(a):(b)) +#define BACKSQL_MIN(a,b) ((a)<(b)?(a):(b)) + +#define BACKSQL_STR_GROW 256 char backsql_def_oc_query[] = "SELECT id,name,keytbl,keycol,create_proc,delete_proc,expect_return " "FROM ldap_oc_mappings"; +char backsql_def_needs_select_oc_query[] = + "SELECT id,name,keytbl,keycol,create_proc,create_keyval,delete_proc," + "expect_return FROM ldap_oc_mappings"; char backsql_def_at_query[] = "SELECT name,sel_expr,from_tbls,join_where,add_proc,delete_proc," "param_order,expect_return,sel_expr_u FROM ldap_attr_mappings " @@ -38,34 +45,29 @@ char backsql_def_insentry_query[] = char backsql_def_subtree_cond[] = "ldap_entries.dn LIKE CONCAT('%',?)"; char backsql_def_upper_subtree_cond[] = "(ldap_entries.dn) LIKE CONCAT('%',?)"; char backsql_id_query[] = "SELECT id,keyval,oc_map_id FROM ldap_entries WHERE "; +/* better ?||? or cast(?||? as varchar) */ +char backsql_def_concat_func[] = "CONCAT(?,?)"; /* TimesTen */ char backsql_check_dn_ru_query[] = "SELECT dn_ru from ldap_entries"; -/* - * Frequently used constants - */ -struct berval - bv_n_objectclass = BER_BVC("objectclass"), - bv_n_0_10 = BER_BVC("0.10"); - struct berval * -backsql_strcat( struct berval *dest, int *buflen, ... ) +backsql_strcat( struct berval *dest, ber_len_t *buflen, ... ) { va_list strs; - int cdlen, cslen, grow; + ber_len_t cdlen, cslen, grow; char *cstr; assert( dest ); assert( dest->bv_val == NULL || dest->bv_len == strlen( dest->bv_val ) ); -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>backsql_strcat()\n" ); -#endif +#endif /* BACKSQL_TRACE */ va_start( strs, buflen ); - if ( dest->bv_val == NULL || *buflen <= 0 ) { + if ( dest->bv_val == NULL || *buflen == 0 ) { dest->bv_val = (char *)ch_calloc( BACKSQL_STR_GROW, sizeof( char ) ); dest->bv_len = 0; @@ -78,12 +80,13 @@ backsql_strcat( struct berval *dest, int *buflen, ... ) if ( *buflen - cdlen <= cslen ) { char *tmp_dest; -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "backsql_strcat(): " "buflen=%d, cdlen=%d, cslen=%d " "-- reallocating dest\n", *buflen, cdlen + 1, cslen ); -#endif +#endif /* BACKSQL_TRACE */ + tmp_dest = (char *)ch_realloc( dest->bv_val, ( *buflen ) + grow * sizeof( char ) ); if ( tmp_dest == NULL ) { @@ -94,20 +97,131 @@ backsql_strcat( struct berval *dest, int *buflen, ... ) } dest->bv_val = tmp_dest; *buflen += grow; -#if 0 + +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "backsql_strcat(): " "new buflen=%d, dest=%p\n", *buflen, dest, 0 ); -#endif +#endif /* BACKSQL_TRACE */ } AC_MEMCPY( dest->bv_val + cdlen, cstr, cslen + 1 ); cdlen += cslen; } va_end( strs ); -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "<==backsql_strcat() (dest='%s')\n", dest, 0, 0 ); -#endif +#endif /* BACKSQL_TRACE */ + + dest->bv_len = cdlen; + + return dest; +} + +struct berval * +backsql_strfcat( struct berval *dest, ber_len_t *buflen, const char *fmt, ... ) +{ + va_list strs; + ber_len_t cdlen; + + assert( dest ); + assert( buflen ); + assert( fmt ); + assert( *buflen == 0 || *buflen > dest->bv_len ); + assert( dest->bv_val == NULL + || dest->bv_len == strlen( dest->bv_val ) ); + +#ifdef BACKSQL_TRACE + Debug( LDAP_DEBUG_TRACE, "==>backsql_strfcat()\n" ); +#endif /* BACKSQL_TRACE */ + + va_start( strs, fmt ); + if ( dest->bv_val == NULL || *buflen == 0 ) { + dest->bv_val = (char *)ch_calloc( BACKSQL_STR_GROW, + sizeof( char ) ); + dest->bv_len = 0; + *buflen = BACKSQL_STR_GROW; + } + + cdlen = dest->bv_len; + for ( ; fmt[0]; fmt++ ) { + ber_len_t cslen, grow; + char *cstr, cc[ 2 ] = { '\0', '\0' }; + struct berval *cbv; + + switch ( fmt[ 0 ] ) { + + /* berval */ + case 'b': + cbv = va_arg( strs, struct berval * ); + cstr = cbv->bv_val; + cslen = cbv->bv_len; + break; + + /* length + string */ + case 'l': + cslen = va_arg( strs, ber_len_t ); + cstr = va_arg( strs, char * ); + break; + + /* string */ + case 's': + cstr = va_arg( strs, char * ); + cslen = strlen( cstr ); + break; + + /* char */ + case 'c': + /* + * `char' is promoted to `int' when passed through `...' + */ + cc[0] = va_arg( strs, int ); + cstr = cc; + cslen = 1; + break; + + default: + assert( 0 ); + } + + grow = BACKSQL_MAX( BACKSQL_STR_GROW, cslen ); + if ( *buflen - cdlen <= cslen ) { + char *tmp_dest; + +#ifdef BACKSQL_TRACE + Debug( LDAP_DEBUG_TRACE, "backsql_strfcat(): " + "buflen=%d, cdlen=%d, cslen=%d " + "-- reallocating dest\n", + *buflen, cdlen + 1, cslen ); +#endif /* BACKSQL_TRACE */ + + tmp_dest = (char *)ch_realloc( dest->bv_val, + ( *buflen ) + grow * sizeof( char ) ); + if ( tmp_dest == NULL ) { + Debug( LDAP_DEBUG_ANY, "backsql_strfcat(): " + "could not reallocate string buffer.\n", + 0, 0, 0 ); + return NULL; + } + dest->bv_val = tmp_dest; + *buflen += grow * sizeof( char ); + +#ifdef BACKSQL_TRACE + Debug( LDAP_DEBUG_TRACE, "backsql_strfcat(): " + "new buflen=%d, dest=%p\n", *buflen, dest, 0 ); +#endif /* BACKSQL_TRACE */ + } + + AC_MEMCPY( dest->bv_val + cdlen, cstr, cslen + 1 ); + cdlen += cslen; + } + + va_end( strs ); + +#ifdef BACKSQL_TRACE + Debug( LDAP_DEBUG_TRACE, "<==backsql_strfcat() (dest='%s')\n", + dest, 0, 0 ); +#endif /* BACKSQL_TRACE */ dest->bv_len = cdlen; @@ -120,17 +234,15 @@ backsql_entry_addattr( struct berval *at_name, struct berval *at_val ) { - struct berval add_val[ 2 ]; AttributeDescription *ad; int rc; const char *text; +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "backsql_entry_addattr(): " "at_name='%s', at_val='%s'\n", at_name->bv_val, at_val->bv_val, 0 ); - add_val[ 0 ] = *at_val; - add_val[ 1 ].bv_val = NULL; - add_val[ 1 ].bv_len = 0; +#endif /* BACKSQL_TRACE */ ad = NULL; rc = slap_bv2ad( at_name, &ad, &text ); @@ -141,7 +253,7 @@ backsql_entry_addattr( return 0; } - rc = attr_merge( e, ad, add_val ); + rc = attr_merge_one( e, ad, at_val ); if ( rc != 0 ) { Debug( LDAP_DEBUG_TRACE, "backsql_entry_addattr(): " @@ -149,8 +261,11 @@ backsql_entry_addattr( at_val->bv_val, at_name->bv_val, 0 ); return 0; } - + +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "<==backsql_query_addattr()\n", 0, 0, 0 ); +#endif /* BACKSQL_TRACE */ + return 1; } @@ -159,7 +274,10 @@ backsql_get_table_spec( char **p ) { char *s, *q; struct berval res = { 0, NULL }; - int res_len = 0; + ber_len_t res_len = 0; + + assert( p ); + assert( *p ); s = *p; while ( **p && **p != ',' ) { @@ -188,49 +306,57 @@ backsql_get_table_spec( char **p ) s = q; BACKSQL_NEXT_WORD; } + #if 0 backsql_strcat( &res, &res_len, " AS ", s, NULL ); /* oracle doesn't understand AS :( */ #endif + /* table alias */ - backsql_strcat( &res, &res_len, " ", s, NULL); + backsql_strfcat( &res, &res_len, "cs", ' ', s ); + return res.bv_val; } int -backsql_merge_from_clause( char **dest_from, int *dest_len, char *src_from ) +backsql_merge_from_clause( + struct berval *dest_from, + ber_len_t *dest_len, + struct berval *src_from ) { char *s, *p, *srcc, *pos, e; struct berval res = { 0 , NULL }; -#if 0 +#ifdef BACKSQL_TRACE Debug( LDAP_DEBUG_TRACE, "==>backsql_merge_from_clause(): " "dest_from='%s',src_from='%s'\n", - dest_from, src_from, 0 ); -#endif - srcc = ch_strdup( src_from ); + dest_from ? dest_from->bv_val : "", 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