2 * Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
4 * Redistribution and use in source and binary forms are permitted only
5 * as authorized by the OpenLDAP Public License. A copy of this
6 * license is available at http://www.OpenLDAP.org/license.html or
7 * in file LICENSE in the top-level directory of the distribution.
15 #include <sys/types.h>
16 #include "ac/string.h"
22 #include "schema-map.h"
26 #define BACKSQL_STOP 0
27 #define BACKSQL_CONTINUE 1
29 static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
32 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
35 AttributeName *an = NULL;
37 if ( bsi->attrs == NULL ) {
42 * clear the list (retrieve all attrs)
45 ch_free( bsi->attrs );
50 for ( ; bsi->attrs[ n_attrs ].an_name.bv_val; n_attrs++ ) {
51 an = &bsi->attrs[ n_attrs ];
53 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
54 "attribute '%s' is in list\n",
55 an->an_name.bv_val, 0, 0 );
57 * We can live with strcmp because the attribute
58 * list has been normalized before calling be_search
60 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
65 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
66 "adding '%s' to list\n", ad->ad_cname.bv_val, 0, 0 );
68 an = (AttributeName *)ch_realloc( bsi->attrs,
69 sizeof( AttributeName ) * ( n_attrs + 2 ) );
74 an[ n_attrs ].an_name = ad->ad_cname;
75 an[ n_attrs ].an_desc = ad;
76 an[ n_attrs + 1 ].an_name.bv_val = NULL;
77 an[ n_attrs + 1 ].an_name.bv_len = 0;
86 backsql_srch_info *bsi,
95 AttributeName *attrs )
101 bsi->slimit = slimit;
102 bsi->tlimit = tlimit;
103 bsi->filter = filter;
111 if ( attrs == NULL || an_find( attrs, &AllUser ) ) {
115 bsi->attrs = (AttributeName *)ch_calloc( 1,
116 sizeof( AttributeName ) );
117 bsi->attrs[ 0 ].an_name.bv_val = NULL;
118 bsi->attrs[ 0 ].an_name.bv_len = 0;
120 for ( p = attrs; p->an_name.bv_val; p++ ) {
122 * ignore "1.1"; handle "+"
124 if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
125 bsi->bsi_flags |= BSQL_SF_ALL_OPER;
128 } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
132 backsql_attrlist_add( bsi, p->an_desc );
138 bsi->n_candidates = 0;
139 bsi->stoptime = stoptime;
140 bsi->sel.bv_val = NULL;
143 bsi->from.bv_val = NULL;
144 bsi->from.bv_len = 0;
146 bsi->join_where.bv_val = NULL;
147 bsi->join_where.bv_len = 0;
149 bsi->flt_where.bv_val = NULL;
150 bsi->flt_where.bv_len = 0;
153 bsi->status = LDAP_SUCCESS;
157 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
165 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", '(' /* ) */ );
168 res = backsql_process_filter( bsi, f );
171 * TimesTen : If the query has no answers,
172 * don't bother to run the query.
183 case LDAP_FILTER_AND:
184 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
185 (ber_len_t)sizeof( " AND " ) - 1,
190 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
191 (ber_len_t)sizeof( " OR " ) - 1,
197 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", /* ( */ ')' );
203 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f )
206 backsql_at_map_rec *at;
212 at = backsql_ad2at( bsi->oc, f->f_sub_desc );
217 * When dealing with case-sensitive strings
218 * we may omit normalization; however, normalized
219 * SQL filters are more liberal.
222 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", '(' /* ) */ );
225 Debug( LDAP_DEBUG_TRACE, "expr: '%s' '%s'\n", at->sel_expr.bv_val,
226 at->sel_expr_u.bv_val ? at->sel_expr_u.bv_val : "<NULL>", 0 );
227 if ( bsi->bi->upper_func.bv_val ) {
229 * If a pre-upper-cased version of the column exists, use it
231 if ( at->sel_expr_u.bv_val ) {
232 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
235 (ber_len_t)sizeof( " LIKE '" ) - 1,
238 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
240 &bsi->bi->upper_func,
244 (ber_len_t)sizeof( " LIKE '" ) - 1,
248 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "bl",
250 (ber_len_t)sizeof( " LIKE '" ) - 1, " LIKE '" );
253 if ( f->f_sub_initial.bv_val != NULL ) {
256 start = bsi->flt_where.bv_len;
257 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "b",
259 if ( bsi->bi->upper_func.bv_val ) {
260 ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
264 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c", '%' );
266 if ( f->f_sub_any != NULL ) {
267 for ( i = 0; f->f_sub_any[ i ].bv_val != NULL; i++ ) {
271 Debug( LDAP_DEBUG_TRACE,
272 "==>backsql_process_sub_filter(): "
273 "sub_any='%s'\n", f->f_sub_any[ i ].bv_val,
275 #endif /* BACKSQL_TRACE */
277 start = bsi->flt_where.bv_len;
278 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
282 if ( bsi->bi->upper_func.bv_val ) {
284 * Note: toupper('%') = '%'
286 ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
290 if ( f->f_sub_final.bv_val != NULL ) {
293 start = bsi->flt_where.bv_len;
294 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "b",
296 if ( bsi->bi->upper_func.bv_val ) {
297 ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
302 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
303 (ber_len_t)sizeof( /* (' */ "')" ) - 1, /* ( */ "')" );
309 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
311 backsql_at_map_rec *at;
312 backsql_at_map_rec oc_attr = {
313 slap_schema.si_ad_objectClass, BER_BVC(""), BER_BVC(""),
314 BER_BVNULL, NULL, NULL, NULL };
315 AttributeDescription *ad = NULL;
320 struct berval *filter_value = NULL;
322 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
323 if ( f == NULL || f->f_choice == SLAPD_FILTER_COMPUTED ) {
327 switch( f->f_choice ) {
329 rc = backsql_process_filter_list( bsi, f->f_or,
334 case LDAP_FILTER_AND:
335 rc = backsql_process_filter_list( bsi, f->f_and,
340 case LDAP_FILTER_NOT:
341 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
342 (ber_len_t)sizeof( "NOT (" /* ) */ ) - 1,
344 rc = backsql_process_filter( bsi, f->f_not );
345 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "c",
350 case LDAP_FILTER_PRESENT:
354 case LDAP_FILTER_EXT:
355 ad = f->f_mra->ma_desc;
364 /* TimesTen : Don't run the query */
373 * Turn structuralObjectClass into objectClass
375 if ( ad == slap_schema.si_ad_objectClass
376 || ad == slap_schema.si_ad_structuralObjectClass ) {
378 backsql_strfcat( &at->sel_expr, &len, "cbc",
380 &bsi->oc->oc->soc_cname,
383 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
385 * FIXME: this is not robust; e.g. a filter
386 * '(!(hasSubordinates=TRUE))' fails because
387 * in SQL it would read 'NOT (1=1)' instead
389 * Note however that hasSubordinates is boolean,
390 * so a more appropriate filter would be
391 * '(hasSubordinates=FALSE)'
393 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
394 (ber_len_t)sizeof( "1=1" ) - 1, "1=1" );
395 if ( ad == slap_schema.si_ad_hasSubordinates ) {
397 * We use this flag since we need to parse
398 * the filter anyway; we should have used
399 * the frontend API function
400 * filter_has_subordinates()
402 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
406 * clear attributes to fetch, to require ALL
407 * and try extended match on all attributes
409 backsql_attrlist_add( bsi, NULL );
414 at = backsql_ad2at( bsi->oc, ad );
418 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
419 "attribute '%s' is not defined for objectclass '%s'\n",
420 ad->ad_cname.bv_val, BACKSQL_OC_NAME( bsi->oc ), 0 );
421 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
422 (ber_len_t)sizeof( "1=0" ) - 1, "1=0" );
426 backsql_merge_from_clause( &bsi->from, &bsi->from_len,
429 * need to add this attribute to list of attrs to load,
430 * so that we could do test_filter() later
432 backsql_attrlist_add( bsi, ad );
434 if ( at->join_where.bv_val != NULL
435 && strstr( bsi->join_where.bv_val, at->join_where.bv_val ) == NULL ) {
436 backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, "lb",
437 (ber_len_t)sizeof( " AND " ) - 1, " AND ",
443 * FIXME: this is not required any more; however, note that
444 * attribute name syntax might collide with SQL legal aliases
446 if ( at != &oc_attr ) {
447 backsql_strfcat( &bsi->sel, &bsi->sel_len, "cblb",
450 (ber_len_t)sizeof( " AS " ) - 1, " AS ",
455 switch ( f->f_choice ) {
456 case LDAP_FILTER_EQUALITY:
457 filter_value = &f->f_av_value;
460 /* fail over next case */
462 case LDAP_FILTER_EXT:
463 filter_value = &f->f_mra->ma_value;
467 * maybe we should check type of at->sel_expr here somehow,
468 * to know whether upper_func is applicable, but for now
469 * upper_func stuff is made for Oracle, where UPPER is
470 * safely applicable to NUMBER etc.
472 if ( bsi->bi->upper_func.bv_val ) {
475 if ( at->sel_expr_u.bv_val ) {
476 backsql_strfcat( &bsi->flt_where,
477 &bsi->fwhere_len, "cbl",
480 (ber_len_t)sizeof( "='" ) - 1,
483 backsql_strfcat( &bsi->flt_where,
484 &bsi->fwhere_len, "cbcbl",
486 &bsi->bi->upper_func,
489 (ber_len_t)sizeof( /* ( */ ")='" ) - 1,
493 start = bsi->flt_where.bv_len;
495 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
498 (ber_len_t)sizeof( /* (' */ "')" ) - 1,
501 ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
504 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
508 (ber_len_t)sizeof( "='" ) - 1, "='",
510 (ber_len_t)sizeof( /* (' */ "')" ) - 1,
517 * FIXME: should we uppercase the operands?
519 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "cblbc",
522 (ber_len_t)sizeof( ">=" ) - 1, ">=",
529 * FIXME: should we uppercase the operands?
531 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "cblbc",
534 (ber_len_t)sizeof( "<=" ) - 1, "<=",
539 case LDAP_FILTER_PRESENT:
540 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "lbl",
541 (ber_len_t)sizeof( "NOT (" ) - 1, "NOT (",
543 (ber_len_t)sizeof( " IS NULL)" ) - 1, " IS NULL)" );
546 case LDAP_FILTER_SUBSTRINGS:
547 backsql_process_sub_filter( bsi, f );
550 case LDAP_FILTER_APPROX:
554 * maybe we should check type of at->sel_expr here somehow,
555 * to know whether upper_func is applicable, but for now
556 * upper_func stuff is made for Oracle, where UPPER is
557 * safely applicable to NUMBER etc.
559 if ( bsi->bi->upper_func.bv_val ) {
562 if ( at->sel_expr_u.bv_val ) {
563 backsql_strfcat( &bsi->flt_where,
564 &bsi->fwhere_len, "cbl",
567 (ber_len_t)sizeof( " LIKE '%" ) - 1,
570 backsql_strfcat( &bsi->flt_where,
571 &bsi->fwhere_len, "cbcbl",
573 &bsi->bi->upper_func,
576 (ber_len_t)sizeof( /* ( */ ") LIKE '%" ) - 1,
577 /* ( */ ") LIKE '%" );
580 start = bsi->flt_where.bv_len;
582 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
585 (ber_len_t)sizeof( /* (' */ "%')" ) - 1,
588 ldap_pvt_str2upper( &bsi->flt_where.bv_val[ start ] );
591 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len,
595 (ber_len_t)sizeof( " LIKE '%" ) - 1,
598 (ber_len_t)sizeof( /* (' */ "%')" ) - 1,
604 /* unhandled filter type; should not happen */
606 backsql_strfcat( &bsi->flt_where, &bsi->fwhere_len, "l",
607 (ber_len_t)sizeof( "1=1" ) - 1, "1=1" );
613 if ( oc_attr.sel_expr.bv_val != NULL ) {
614 free( oc_attr.sel_expr.bv_val );
617 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter()\n", 0, 0, 0 );
621 if ( oc_attr.sel_expr.bv_val != NULL ) {
622 free( oc_attr.sel_expr.bv_val );
624 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter() returns -1\n",
630 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
632 backsql_info *bi = (backsql_info *)bsi->op->o_bd->be_private;
637 query->bv_val = NULL;
640 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
641 bsi->sel.bv_val = NULL;
644 bsi->from.bv_val = NULL;
645 bsi->from.bv_len = 0;
647 bsi->join_where.bv_val = NULL;
648 bsi->join_where.bv_len = 0;
650 bsi->flt_where.bv_val = NULL;
651 bsi->flt_where.bv_len = 0;
656 * FIXME: this query has been split in case a string cast function
657 * is defined; more sophisticated (pattern based) function should
660 backsql_strcat( &bsi->sel, &bsi->sel_len,
661 "SELECT DISTINCT ldap_entries.id,",
662 bsi->oc->keytbl.bv_val, ".", bsi->oc->keycol.bv_val,
663 ",'", bsi->oc->name.bv_val, "' AS objectClass",
664 ",ldap_entries.dn AS dn", NULL );
667 backsql_strfcat( &bsi->sel, &bsi->sel_len, "lbcbc",
668 (ber_len_t)sizeof( "SELECT DISTINCT ldap_entries.id," ) - 1,
669 "SELECT DISTINCT ldap_entries.id,",
675 if ( bi->strcast_func.bv_val ) {
676 backsql_strfcat( &bsi->sel, &bsi->sel_len, "blbl",
678 (ber_len_t)sizeof( "('" /* ') */ ) - 1,
680 &bsi->oc->oc->soc_cname,
681 (ber_len_t)sizeof( /* (' */ "')" ) - 1,
684 backsql_strfcat( &bsi->sel, &bsi->sel_len, "cbc",
686 &bsi->oc->oc->soc_cname,
689 backsql_strfcat( &bsi->sel, &bsi->sel_len, "l",
690 (ber_len_t)sizeof( " AS objectClass,ldap_entries.dn AS dn" ) - 1,
691 " AS objectClass,ldap_entries.dn AS dn" );
693 backsql_strfcat( &bsi->from, &bsi->from_len, "lb",
694 (ber_len_t)sizeof( " FROM ldap_entries," ) - 1,
695 " FROM ldap_entries,",
698 backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, "lbcbl",
699 (ber_len_t)sizeof( " WHERE " ) - 1, " WHERE ",
703 (ber_len_t)sizeof( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ) - 1,
704 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
706 switch ( bsi->scope ) {
707 case LDAP_SCOPE_BASE:
708 if ( bsi->bi->upper_func.bv_val ) {
709 backsql_strfcat( &bsi->join_where, &bsi->jwhere_len,
711 &bsi->bi->upper_func,
712 (ber_len_t)sizeof( "(ldap_entries.dn)=" ) - 1,
713 "(ldap_entries.dn)=",
714 &bsi->bi->upper_func_open,
716 &bsi->bi->upper_func_close );
718 backsql_strfcat( &bsi->join_where, &bsi->jwhere_len,
720 (ber_len_t)sizeof( "ldap_entries.dn=?" ) - 1,
721 "ldap_entries.dn=?" );
725 case LDAP_SCOPE_ONELEVEL:
726 backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, "l",
727 (ber_len_t)sizeof( "ldap_entries.parent=?" ) - 1,
728 "ldap_entries.parent=?" );
731 case LDAP_SCOPE_SUBTREE:
732 if ( bsi->bi->upper_func.bv_val ) {
733 backsql_strfcat( &bsi->join_where, &bsi->jwhere_len,
735 &bsi->bi->upper_func,
736 (ber_len_t)sizeof( "(ldap_entries.dn) LIKE " ) - 1,
737 "(ldap_entries.dn) LIKE ",
738 &bsi->bi->upper_func_open,
740 &bsi->bi->upper_func_close );
742 backsql_strfcat( &bsi->join_where, &bsi->jwhere_len,
744 (ber_len_t)sizeof( "ldap_entries.dn LIKE ?" ) - 1,
745 "ldap_entries.dn LIKE ?" );
749 backsql_strfcat( &bsi->join_where, &bsi->jwhere_len, "b",
750 &bsi->bi->subtree_cond );
758 rc = backsql_process_filter( bsi, bsi->filter );
760 backsql_strfcat( query, &q_len, "bbblb",
764 (ber_len_t)sizeof( " AND " ) - 1, " AND ",
767 } else if ( rc < 0 ) {
769 * Indicates that there's no possible way the filter matches
770 * anything. No need to issue the query
772 Debug( LDAP_DEBUG_TRACE,
773 "<==backsql_srch_query() returns NULL\n", 0, 0, 0 );
774 free( query->bv_val );
775 query->bv_val = NULL;
778 free( bsi->sel.bv_val );
781 free( bsi->from.bv_val );
782 bsi->from.bv_len = 0;
784 free( bsi->join_where.bv_val );
785 bsi->join_where.bv_len = 0;
787 free( bsi->flt_where.bv_val );
788 bsi->flt_where.bv_len = 0;
791 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query()\n", 0, 0, 0 );
793 return ( query->bv_val == NULL ? 1 : 0 );
797 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
799 backsql_oc_map_rec *oc = v_oc;
800 backsql_srch_info *bsi = v_bsi;
804 backsql_entryID base_id, *c_id;
810 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc='%s'\n",
811 BACKSQL_OC_NAME( oc ), 0, 0 );
813 if ( bsi->n_candidates == -1 ) {
814 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
815 "unchecked limit has been overcome\n", 0, 0, 0 );
816 /* should never get here */
822 if ( backsql_srch_query( bsi, &query ) ) {
823 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
824 "could not construct query for objectclass\n",
826 return BACKSQL_CONTINUE;
829 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
830 query.bv_val, 0, 0 );
832 rc = backsql_Prepare( bsi->dbh, &sth, query.bv_val, 0 );
833 free( query.bv_val );
834 if ( rc != SQL_SUCCESS ) {
835 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
836 "error preparing query\n", 0, 0, 0 );
837 backsql_PrintErrors( bsi->bi->db_env, bsi->dbh, sth, rc );
838 return BACKSQL_CONTINUE;
841 if ( backsql_BindParamID( sth, 1, &bsi->oc->id ) != SQL_SUCCESS ) {
842 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
843 "error binding objectclass id parameter\n", 0, 0, 0 );
844 return BACKSQL_CONTINUE;
847 switch ( bsi->scope ) {
848 case LDAP_SCOPE_BASE:
849 rc = backsql_BindParamStr( sth, 2, bsi->base_dn->bv_val,
850 BACKSQL_MAX_DN_LEN );
851 if ( rc != SQL_SUCCESS ) {
852 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
853 "error binding base_dn parameter\n", 0, 0, 0 );
854 backsql_PrintErrors( bsi->bi->db_env, bsi->dbh,
856 return BACKSQL_CONTINUE;
860 case LDAP_SCOPE_SUBTREE: {
863 * + 1 because we need room for '%'; this makes a subtree
864 * search for a DN BACKSQL_MAX_DN_LEN long legal
865 * if it returns that DN only
867 char temp_base_dn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
870 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
871 * however this should be handled earlier
873 assert( bsi->base_dn->bv_len <= BACKSQL_MAX_DN_LEN );
876 * Sets the parameters for the SQL built earlier
877 * NOTE that all the databases could actually use
878 * the TimesTen version, which would be cleaner
879 * and would also eliminate the need for the
880 * subtree_cond line in the configuration file.
881 * For now, I'm leaving it the way it is,
882 * so non-TimesTen databases use the original code.
883 * But at some point this should get cleaned up.
885 * If "dn" is being used, do a suffix search.
886 * If "dn_ru" is being used, do a prefix search.
888 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bsi->bi ) ) {
889 temp_base_dn[ 0 ] = '\0';
890 for ( i = 0, j = bsi->base_dn->bv_len - 1;
892 temp_base_dn[ i ] = bsi->base_dn->bv_val[ j ];
894 temp_base_dn[ i ] = '%';
895 temp_base_dn[ i + 1 ] = '\0';
896 ldap_pvt_str2upper( temp_base_dn );
899 temp_base_dn[ 0 ] = '%';
900 AC_MEMCPY( &temp_base_dn[ 1 ], bsi->base_dn->bv_val,
901 bsi->base_dn->bv_len + 1 );
902 ldap_pvt_str2upper( &temp_base_dn[ 1 ] );
905 Debug( LDAP_DEBUG_TRACE, "dn '%s'\n", temp_base_dn, 0, 0 );
907 rc = backsql_BindParamStr( sth, 2, temp_base_dn,
908 BACKSQL_MAX_DN_LEN );
909 if ( rc != SQL_SUCCESS ) {
910 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
911 "error binding base_dn parameter (2)\n",
913 backsql_PrintErrors( bsi->bi->db_env, bsi->dbh,
915 return BACKSQL_CONTINUE;
920 case LDAP_SCOPE_ONELEVEL:
921 res = backsql_dn2id( bsi->bi, &base_id,
922 bsi->dbh, bsi->base_dn );
923 if ( res != LDAP_SUCCESS ) {
924 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
925 "could not retrieve base_dn id%s\n",
926 res == LDAP_NO_SUCH_OBJECT ? ": no such entry"
929 return BACKSQL_CONTINUE;
932 rc = backsql_BindParamID( sth, 2, &base_id.id );
933 backsql_free_entryID( &base_id, 0 );
934 if ( rc != SQL_SUCCESS ) {
935 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
936 "error binding base id parameter\n", 0, 0, 0 );
937 return BACKSQL_CONTINUE;
942 rc = SQLExecute( sth );
943 if ( !BACKSQL_SUCCESS( rc ) ) {
944 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
945 "error executing query\n", 0, 0, 0 );
946 backsql_PrintErrors( bsi->bi->db_env, bsi->dbh, sth, rc );
947 SQLFreeStmt( sth, SQL_DROP );
948 return BACKSQL_CONTINUE;
951 backsql_BindRowAsStrings( sth, &row );
952 rc = SQLFetch( sth );
953 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
954 c_id = (backsql_entryID *)ch_calloc( 1,
955 sizeof( backsql_entryID ) );
956 c_id->id = strtol( row.cols[ 0 ], NULL, 0 );
957 c_id->keyval = strtol( row.cols[ 1 ], NULL, 0 );
958 c_id->oc_id = bsi->oc->id;
959 ber_str2bv( row.cols[ 3 ], 0, 1, &c_id->dn );
960 c_id->next = bsi->id_list;
964 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
965 "added entry id=%ld, keyval=%ld dn='%s'\n",
966 c_id->id, c_id->keyval, row.cols[ 3 ] );
968 if ( bsi->n_candidates == -1 ) {
972 backsql_FreeRow( &row );
973 SQLFreeStmt( sth, SQL_DROP );
975 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates()\n", 0, 0, 0 );
977 return ( bsi->n_candidates == -1 ? BACKSQL_STOP : BACKSQL_CONTINUE );
981 backsql_search( Operation *op, SlapReply *rs )
987 struct berval *nbase,
993 struct berval *filterstr,
994 AttributeName *attrs,
997 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
1002 time_t stoptime = 0;
1003 backsql_srch_info srch_info;
1004 backsql_entryID *eid = NULL;
1005 struct slap_limits_set *limit = NULL;
1008 manageDSAit = get_manageDSAit( op );
1010 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1011 "base='%s', filter='%s', scope=%d,",
1012 op->o_req_ndn.bv_val,
1013 op->oq_search.rs_filterstr.bv_val,
1014 op->oq_search.rs_scope );
1015 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1016 "attributes to load: %s\n",
1017 op->oq_search.rs_deref,
1018 op->oq_search.rs_attrsonly,
1019 op->oq_search.rs_attrs == NULL ? "all" : "custom list" );
1021 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1022 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1023 "search base length (%ld) exceeds max length (%ld)\n",
1024 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1026 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1027 * since it is impossible that such a long DN exists
1030 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1031 send_ldap_result( op, rs );
1035 sres = backsql_get_db_conn( op, &dbh );
1036 if ( sres != LDAP_SUCCESS ) {
1037 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1038 "could not get connection handle - exiting\n",
1041 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
1042 send_ldap_result( op, rs );
1046 /* TimesTen : Pass it along to the lower level routines */
1047 srch_info.use_reverse_dn = BACKSQL_USE_REVERSE_DN( bi );
1049 /* if not root, get appropriate limits */
1050 if ( be_isroot( op->o_bd, &op->o_ndn ) ) {
1053 ( void ) get_limits( op->o_bd, &op->o_ndn, &limit );
1056 /* The time/size limits come first because they require very little
1057 * effort, so there's no chance the candidates are selected and then
1058 * the request is not honored only because of time/size constraints */
1060 /* if no time limit requested, use soft limit (unless root!) */
1062 if ( op->oq_search.rs_tlimit == 0 ) {
1063 op->oq_search.rs_tlimit = -1; /* allow root to set no limit */
1066 if ( op->oq_search.rs_slimit == 0 ) {
1067 op->oq_search.rs_slimit = -1;
1071 /* if no limit is required, use soft limit */
1072 if ( op->oq_search.rs_tlimit <= 0 ) {
1073 op->oq_search.rs_tlimit = limit->lms_t_soft;
1075 /* if requested limit higher than hard limit, abort */
1076 } else if ( op->oq_search.rs_tlimit > limit->lms_t_hard ) {
1077 /* no hard limit means use soft instead */
1078 if ( limit->lms_t_hard == 0
1079 && limit->lms_t_soft > -1
1080 && op->oq_search.rs_tlimit > limit->lms_t_soft ) {
1081 op->oq_search.rs_tlimit = limit->lms_t_soft;
1083 /* positive hard limit means abort */
1084 } else if ( limit->lms_t_hard > 0 ) {
1085 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1086 send_ldap_result( op, rs );
1090 /* negative hard limit means no limit */
1093 /* if no limit is required, use soft limit */
1094 if ( op->oq_search.rs_slimit <= 0 ) {
1095 op->oq_search.rs_slimit = limit->lms_s_soft;
1097 /* if requested limit higher than hard limit, abort */
1098 } else if ( op->oq_search.rs_slimit > limit->lms_s_hard ) {
1099 /* no hard limit means use soft instead */
1100 if ( limit->lms_s_hard == 0
1101 && limit->lms_s_soft > -1
1102 && op->oq_search.rs_slimit > limit->lms_s_soft ) {
1103 op->oq_search.rs_slimit = limit->lms_s_soft;
1105 /* positive hard limit means abort */
1106 } else if ( limit->lms_s_hard > 0 ) {
1107 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1108 send_ldap_result( op, rs );
1112 /* negative hard limit means no limit */
1116 /* compute it anyway; root does not use it */
1117 stoptime = op->o_time + op->oq_search.rs_tlimit;
1119 backsql_init_search( &srch_info, &op->o_req_dn,
1120 op->oq_search.rs_scope,
1121 op->oq_search.rs_slimit, op->oq_search.rs_tlimit,
1122 stoptime, op->oq_search.rs_filter,
1123 dbh, op, op->oq_search.rs_attrs );
1126 * for each objectclass we try to construct query which gets IDs
1127 * of entries matching LDAP query filter and scope (or at least
1128 * candidates), and get the IDs
1130 srch_info.n_candidates = ( isroot ? -2 : limit->lms_s_unchecked == -1
1131 ? -2 : limit->lms_s_unchecked );
1132 avl_apply( bi->oc_by_oc, backsql_oc_get_candidates,
1133 &srch_info, BACKSQL_STOP, AVL_INORDER );
1134 if ( !isroot && limit->lms_s_unchecked != -1 ) {
1135 if ( srch_info.n_candidates == -1 ) {
1136 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1137 send_ldap_result( op, rs );
1143 * now we load candidate entries (only those attributes
1144 * mentioned in attrs and filter), test it against full filter
1145 * and then send to client
1147 for ( eid = srch_info.id_list; eid != NULL;
1148 eid = backsql_free_entryID( eid, 1 ) ) {
1149 Attribute *hasSubordinate = NULL,
1152 /* check for abandon */
1153 if ( op->o_abandon ) {
1157 /* check time limit */
1158 if ( op->oq_search.rs_tlimit != -1 && slap_get_time() > stoptime ) {
1159 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
1160 rs->sr_ctrls = NULL;
1161 rs->sr_ref = rs->sr_v2ref;
1162 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1164 send_ldap_result( op, rs );
1168 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1169 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
1170 eid->id, eid->oc_id, eid->keyval );
1172 entry = (Entry *)ch_calloc( sizeof( Entry ), 1 );
1173 res = backsql_id2entry( &srch_info, entry, eid );
1174 if ( res == NULL ) {
1175 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1176 "error in backsql_id2entry() "
1177 "- skipping entry\n", 0, 0, 0 );
1181 if ( !manageDSAit &&
1182 op->oq_search.rs_scope != LDAP_SCOPE_BASE &&
1183 is_entry_referral( entry ) ) {
1185 struct berval matched_dn;
1187 ber_dupbv( &matched_dn, &entry->e_name );
1188 refs = get_entry_referrals( op, entry );
1190 rs->sr_ref = referral_rewrite( refs,
1191 &matched_dn, &op->o_req_dn,
1192 op->oq_search.rs_scope );
1193 ber_bvarray_free( refs );
1197 rs->sr_text = "bad_referral object";
1200 rs->sr_err = LDAP_REFERRAL;
1201 rs->sr_matched = matched_dn.bv_val;
1202 send_search_reference( op, rs );
1204 ber_bvarray_free( rs->sr_ref );
1206 ber_memfree( matched_dn.bv_val );
1207 rs->sr_matched = NULL;
1213 * We use this flag since we need to parse the filter
1214 * anyway; we should have used the frontend API function
1215 * filter_has_subordinates()
1217 if ( srch_info.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
1220 rc = backsql_has_children( bi, dbh, &entry->e_nname );
1223 case LDAP_COMPARE_TRUE:
1224 case LDAP_COMPARE_FALSE:
1225 hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
1226 if ( hasSubordinate != NULL ) {
1227 for ( a = entry->e_attrs;
1231 a->a_next = hasSubordinate;
1237 Debug(LDAP_DEBUG_TRACE,
1238 "backsql_search(): "
1239 "has_children failed( %d)\n",
1250 if ( test_filter( op, entry, op->oq_search.rs_filter )
1251 == LDAP_COMPARE_TRUE ) {
1252 if ( hasSubordinate && !( srch_info.bsi_flags & BSQL_SF_ALL_OPER )
1253 && !ad_inlist( slap_schema.si_ad_hasSubordinates, op->oq_search.rs_attrs ) ) {
1255 attr_free( hasSubordinate );
1256 hasSubordinate = NULL;
1259 #if 0 /* noop is masked SLAP_CTRL_UPDATE */
1264 rs->sr_entry = entry;
1265 sres = send_search_entry( op, rs );
1275 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1276 "connection lost\n", 0, 0, 0 );
1281 * FIXME: send_search_entry failed;
1287 entry_free( entry );
1289 if ( op->oq_search.rs_slimit != -1
1290 && rs->sr_nentries >= op->oq_search.rs_slimit ) {
1291 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1292 send_ldap_result( op, rs );
1299 if ( rs->sr_nentries > 0 ) {
1300 rs->sr_ref = rs->sr_v2ref;
1301 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1304 rs->sr_err = srch_info.status;
1306 send_ldap_result( op, rs );
1308 if ( rs->sr_v2ref ) {
1309 ber_bvarray_free( rs->sr_v2ref );
1310 rs->sr_v2ref = NULL;
1314 ch_free( srch_info.attrs );
1316 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
1320 #endif /* SLAPD_SQL */