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.bb_val.bv_val = NULL;
141 bsi->sel.bb_val.bv_len = 0;
143 bsi->from.bb_val.bv_val = NULL;
144 bsi->from.bb_val.bv_len = 0;
145 bsi->from.bb_len = 0;
146 bsi->join_where.bb_val.bv_val = NULL;
147 bsi->join_where.bb_val.bv_len = 0;
148 bsi->join_where.bb_len = 0;
149 bsi->flt_where.bb_val.bv_val = NULL;
150 bsi->flt_where.bb_val.bv_len = 0;
151 bsi->flt_where.bb_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, "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, "l",
185 (ber_len_t)sizeof( " AND " ) - 1,
190 backsql_strfcat( &bsi->flt_where, "l",
191 (ber_len_t)sizeof( " OR " ) - 1,
197 backsql_strfcat( &bsi->flt_where, "c", /* ( */ ')' );
203 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f )
206 backsql_at_map_rec *at;
207 backsql_info *bi = (backsql_info *)bsi->op->o_bd->be_private;
213 at = backsql_ad2at( bsi->oc, f->f_sub_desc );
218 * When dealing with case-sensitive strings
219 * we may omit normalization; however, normalized
220 * SQL filters are more liberal.
223 backsql_strfcat( &bsi->flt_where, "c", '(' /* ) */ );
226 Debug( LDAP_DEBUG_TRACE, "expr: '%s' '%s'\n", at->sel_expr.bv_val,
227 at->sel_expr_u.bv_val ? at->sel_expr_u.bv_val : "<NULL>", 0 );
228 if ( bi->upper_func.bv_val ) {
230 * If a pre-upper-cased version of the column exists, use it
232 if ( at->sel_expr_u.bv_val ) {
233 backsql_strfcat( &bsi->flt_where,
236 (ber_len_t)sizeof( " LIKE '" ) - 1,
239 backsql_strfcat( &bsi->flt_where,
245 (ber_len_t)sizeof( " LIKE '" ) - 1,
249 backsql_strfcat( &bsi->flt_where, "bl",
251 (ber_len_t)sizeof( " LIKE '" ) - 1, " LIKE '" );
254 if ( f->f_sub_initial.bv_val != NULL ) {
257 start = bsi->flt_where.bb_val.bv_len;
258 backsql_strfcat( &bsi->flt_where, "b",
260 if ( bi->upper_func.bv_val ) {
261 ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
265 backsql_strfcat( &bsi->flt_where, "c", '%' );
267 if ( f->f_sub_any != NULL ) {
268 for ( i = 0; f->f_sub_any[ i ].bv_val != NULL; i++ ) {
272 Debug( LDAP_DEBUG_TRACE,
273 "==>backsql_process_sub_filter(): "
274 "sub_any='%s'\n", f->f_sub_any[ i ].bv_val,
276 #endif /* BACKSQL_TRACE */
278 start = bsi->flt_where.bb_val.bv_len;
279 backsql_strfcat( &bsi->flt_where,
283 if ( bi->upper_func.bv_val ) {
285 * Note: toupper('%') = '%'
287 ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
291 if ( f->f_sub_final.bv_val != NULL ) {
294 start = bsi->flt_where.bb_val.bv_len;
295 backsql_strfcat( &bsi->flt_where, "b",
297 if ( bi->upper_func.bv_val ) {
298 ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
303 backsql_strfcat( &bsi->flt_where, "l",
304 (ber_len_t)sizeof( /* (' */ "')" ) - 1, /* ( */ "')" );
310 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
312 backsql_info *bi = (backsql_info *)bsi->op->o_bd->be_private;
313 backsql_at_map_rec *at;
314 backsql_at_map_rec oc_attr = {
315 slap_schema.si_ad_objectClass, BER_BVC(""), BER_BVC(""),
316 BER_BVNULL, NULL, NULL, NULL };
317 AttributeDescription *ad = NULL;
321 struct berval *filter_value = NULL;
323 Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
324 if ( f == NULL || f->f_choice == SLAPD_FILTER_COMPUTED ) {
328 switch( f->f_choice ) {
330 rc = backsql_process_filter_list( bsi, f->f_or,
335 case LDAP_FILTER_AND:
336 rc = backsql_process_filter_list( bsi, f->f_and,
341 case LDAP_FILTER_NOT:
342 backsql_strfcat( &bsi->flt_where, "l",
343 (ber_len_t)sizeof( "NOT (" /* ) */ ) - 1,
345 rc = backsql_process_filter( bsi, f->f_not );
346 backsql_strfcat( &bsi->flt_where, "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 ) {
377 struct berbuf bb = BB_NULL;
380 backsql_strfcat( &bb, "cbc",
382 &bsi->oc->oc->soc_cname,
384 at->sel_expr = bb.bb_val;
386 } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
388 * FIXME: this is not robust; e.g. a filter
389 * '(!(hasSubordinates=TRUE))' fails because
390 * in SQL it would read 'NOT (1=1)' instead
392 * Note however that hasSubordinates is boolean,
393 * so a more appropriate filter would be
394 * '(hasSubordinates=FALSE)'
396 backsql_strfcat( &bsi->flt_where, "l",
397 (ber_len_t)sizeof( "1=1" ) - 1, "1=1" );
398 if ( ad == slap_schema.si_ad_hasSubordinates ) {
400 * We use this flag since we need to parse
401 * the filter anyway; we should have used
402 * the frontend API function
403 * filter_has_subordinates()
405 bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
409 * clear attributes to fetch, to require ALL
410 * and try extended match on all attributes
412 backsql_attrlist_add( bsi, NULL );
417 at = backsql_ad2at( bsi->oc, ad );
421 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
422 "attribute '%s' is not defined for objectclass '%s'\n",
423 ad->ad_cname.bv_val, BACKSQL_OC_NAME( bsi->oc ), 0 );
424 backsql_strfcat( &bsi->flt_where, "l",
425 (ber_len_t)sizeof( "1=0" ) - 1, "1=0" );
429 backsql_merge_from_clause( &bsi->from, &at->from_tbls );
431 * need to add this attribute to list of attrs to load,
432 * so that we could do test_filter() later
434 backsql_attrlist_add( bsi, ad );
436 if ( at->join_where.bv_val != NULL
437 && strstr( bsi->join_where.bb_val.bv_val, at->join_where.bv_val ) == NULL ) {
438 backsql_strfcat( &bsi->join_where, "lb",
439 (ber_len_t)sizeof( " AND " ) - 1, " AND ",
445 * FIXME: this is not required any more; however, note that
446 * attribute name syntax might collide with SQL legal aliases
448 if ( at != &oc_attr ) {
449 backsql_strfcat( &bsi->sel, "cblb",
452 (ber_len_t)sizeof( " AS " ) - 1, " AS ",
457 switch ( f->f_choice ) {
458 case LDAP_FILTER_EQUALITY:
459 filter_value = &f->f_av_value;
462 /* fail over next case */
464 case LDAP_FILTER_EXT:
465 filter_value = &f->f_mra->ma_value;
469 * maybe we should check type of at->sel_expr here somehow,
470 * to know whether upper_func is applicable, but for now
471 * upper_func stuff is made for Oracle, where UPPER is
472 * safely applicable to NUMBER etc.
474 if ( bi->upper_func.bv_val ) {
477 if ( at->sel_expr_u.bv_val ) {
478 backsql_strfcat( &bsi->flt_where, "cbl",
481 (ber_len_t)sizeof( "='" ) - 1,
484 backsql_strfcat( &bsi->flt_where, "cbcbl",
489 (ber_len_t)sizeof( /* ( */ ")='" ) - 1,
493 start = bsi->flt_where.bb_val.bv_len;
495 backsql_strfcat( &bsi->flt_where, "bl",
497 (ber_len_t)sizeof( /* (' */ "')" ) - 1,
500 ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
503 backsql_strfcat( &bsi->flt_where, "cblbl",
506 (ber_len_t)sizeof( "='" ) - 1, "='",
508 (ber_len_t)sizeof( /* (' */ "')" ) - 1,
515 * FIXME: should we uppercase the operands?
517 backsql_strfcat( &bsi->flt_where, "cblbc",
520 (ber_len_t)sizeof( ">=" ) - 1, ">=",
527 * FIXME: should we uppercase the operands?
529 backsql_strfcat( &bsi->flt_where, "cblbc",
532 (ber_len_t)sizeof( "<=" ) - 1, "<=",
537 case LDAP_FILTER_PRESENT:
538 backsql_strfcat( &bsi->flt_where, "lbl",
539 (ber_len_t)sizeof( "NOT (" ) - 1, "NOT (",
541 (ber_len_t)sizeof( " IS NULL)" ) - 1, " IS NULL)" );
544 case LDAP_FILTER_SUBSTRINGS:
545 backsql_process_sub_filter( bsi, f );
548 case LDAP_FILTER_APPROX:
552 * maybe we should check type of at->sel_expr here somehow,
553 * to know whether upper_func is applicable, but for now
554 * upper_func stuff is made for Oracle, where UPPER is
555 * safely applicable to NUMBER etc.
557 if ( bi->upper_func.bv_val ) {
560 if ( at->sel_expr_u.bv_val ) {
561 backsql_strfcat( &bsi->flt_where, "cbl",
564 (ber_len_t)sizeof( " LIKE '%" ) - 1,
567 backsql_strfcat( &bsi->flt_where, "cbcbl",
572 (ber_len_t)sizeof( /* ( */ ") LIKE '%" ) - 1,
573 /* ( */ ") LIKE '%" );
576 start = bsi->flt_where.bb_val.bv_len;
578 backsql_strfcat( &bsi->flt_where, "bl",
580 (ber_len_t)sizeof( /* (' */ "%')" ) - 1,
583 ldap_pvt_str2upper( &bsi->flt_where.bb_val.bv_val[ start ] );
586 backsql_strfcat( &bsi->flt_where, "cblbl",
589 (ber_len_t)sizeof( " LIKE '%" ) - 1,
592 (ber_len_t)sizeof( /* (' */ "%')" ) - 1,
598 /* unhandled filter type; should not happen */
600 backsql_strfcat( &bsi->flt_where, "l",
601 (ber_len_t)sizeof( "1=1" ) - 1, "1=1" );
607 if ( oc_attr.sel_expr.bv_val != NULL ) {
608 free( oc_attr.sel_expr.bv_val );
611 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter()\n", 0, 0, 0 );
615 if ( oc_attr.sel_expr.bv_val != NULL ) {
616 free( oc_attr.sel_expr.bv_val );
618 Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter() returns -1\n",
624 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
626 backsql_info *bi = (backsql_info *)bsi->op->o_bd->be_private;
630 query->bv_val = NULL;
633 Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
634 bsi->sel.bb_val.bv_val = NULL;
635 bsi->sel.bb_val.bv_len = 0;
637 bsi->from.bb_val.bv_val = NULL;
638 bsi->from.bb_val.bv_len = 0;
639 bsi->from.bb_len = 0;
640 bsi->join_where.bb_val.bv_val = NULL;
641 bsi->join_where.bb_val.bv_len = 0;
642 bsi->join_where.bb_len = 0;
643 bsi->flt_where.bb_val.bv_val = NULL;
644 bsi->flt_where.bb_val.bv_len = 0;
645 bsi->flt_where.bb_len = 0;
649 * FIXME: this query has been split in case a string cast function
650 * is defined; more sophisticated (pattern based) function should
653 backsql_strcat( &bsi->sel,
654 "SELECT DISTINCT ldap_entries.id,",
655 bsi->oc->keytbl.bv_val, ".", bsi->oc->keycol.bv_val,
656 ",'", bsi->oc->name.bv_val, "' AS objectClass",
657 ",ldap_entries.dn AS dn", NULL );
660 backsql_strfcat( &bsi->sel, "lbcbc",
661 (ber_len_t)sizeof( "SELECT DISTINCT ldap_entries.id," ) - 1,
662 "SELECT DISTINCT ldap_entries.id,",
668 if ( bi->strcast_func.bv_val ) {
669 backsql_strfcat( &bsi->sel, "blbl",
671 (ber_len_t)sizeof( "('" /* ') */ ) - 1,
673 &bsi->oc->oc->soc_cname,
674 (ber_len_t)sizeof( /* (' */ "')" ) - 1,
677 backsql_strfcat( &bsi->sel, "cbc",
679 &bsi->oc->oc->soc_cname,
682 backsql_strfcat( &bsi->sel, "l",
683 (ber_len_t)sizeof( " AS objectClass,ldap_entries.dn AS dn" ) - 1,
684 " AS objectClass,ldap_entries.dn AS dn" );
686 backsql_strfcat( &bsi->from, "lb",
687 (ber_len_t)sizeof( " FROM ldap_entries," ) - 1,
688 " FROM ldap_entries,",
691 backsql_strfcat( &bsi->join_where, "lbcbl",
692 (ber_len_t)sizeof( " WHERE " ) - 1, " WHERE ",
696 (ber_len_t)sizeof( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ) - 1,
697 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
699 switch ( bsi->scope ) {
700 case LDAP_SCOPE_BASE:
701 if ( bi->upper_func.bv_val ) {
702 backsql_strfcat( &bsi->join_where, "blbcb",
704 (ber_len_t)sizeof( "(ldap_entries.dn)=" ) - 1,
705 "(ldap_entries.dn)=",
706 &bi->upper_func_open,
708 &bi->upper_func_close );
710 backsql_strfcat( &bsi->join_where, "l",
711 (ber_len_t)sizeof( "ldap_entries.dn=?" ) - 1,
712 "ldap_entries.dn=?" );
716 case LDAP_SCOPE_ONELEVEL:
717 backsql_strfcat( &bsi->join_where, "l",
718 (ber_len_t)sizeof( "ldap_entries.parent=?" ) - 1,
719 "ldap_entries.parent=?" );
722 case LDAP_SCOPE_SUBTREE:
723 if ( bi->upper_func.bv_val ) {
724 backsql_strfcat( &bsi->join_where, "blbcb",
726 (ber_len_t)sizeof( "(ldap_entries.dn) LIKE " ) - 1,
727 "(ldap_entries.dn) LIKE ",
728 &bi->upper_func_open,
730 &bi->upper_func_close );
732 backsql_strfcat( &bsi->join_where, "l",
733 (ber_len_t)sizeof( "ldap_entries.dn LIKE ?" ) - 1,
734 "ldap_entries.dn LIKE ?" );
738 backsql_strfcat( &bsi->join_where, "b",
747 rc = backsql_process_filter( bsi, bsi->filter );
749 struct berbuf bb = BB_NULL;
751 backsql_strfcat( &bb, "bbblb",
754 &bsi->join_where.bb_val,
755 (ber_len_t)sizeof( " AND " ) - 1, " AND ",
756 &bsi->flt_where.bb_val );
760 } else if ( rc < 0 ) {
762 * Indicates that there's no possible way the filter matches
763 * anything. No need to issue the query
765 Debug( LDAP_DEBUG_TRACE,
766 "<==backsql_srch_query() returns NULL\n", 0, 0, 0 );
767 free( query->bv_val );
768 query->bv_val = NULL;
771 free( bsi->sel.bb_val.bv_val );
772 bsi->sel.bb_val.bv_len = 0;
774 free( bsi->from.bb_val.bv_val );
775 bsi->from.bb_val.bv_len = 0;
776 bsi->from.bb_len = 0;
777 free( bsi->join_where.bb_val.bv_val );
778 bsi->join_where.bb_val.bv_len = 0;
779 bsi->join_where.bb_len = 0;
780 free( bsi->flt_where.bb_val.bv_val );
781 bsi->flt_where.bb_val.bv_len = 0;
782 bsi->flt_where.bb_len = 0;
784 Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query()\n", 0, 0, 0 );
786 return ( query->bv_val == NULL ? 1 : 0 );
790 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
792 backsql_oc_map_rec *oc = v_oc;
793 backsql_srch_info *bsi = v_bsi;
794 backsql_info *bi = (backsql_info *)bsi->op->o_bd->be_private;
798 backsql_entryID base_id, *c_id;
804 Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc='%s'\n",
805 BACKSQL_OC_NAME( oc ), 0, 0 );
807 if ( bsi->n_candidates == -1 ) {
808 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
809 "unchecked limit has been overcome\n", 0, 0, 0 );
810 /* should never get here */
812 bsi->status = LDAP_OTHER;
817 if ( backsql_srch_query( bsi, &query ) ) {
818 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
819 "could not construct query for objectclass\n",
821 bsi->status = LDAP_OTHER;
822 return BACKSQL_CONTINUE;
825 Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
826 query.bv_val, 0, 0 );
828 rc = backsql_Prepare( bsi->dbh, &sth, query.bv_val, 0 );
829 free( query.bv_val );
830 if ( rc != SQL_SUCCESS ) {
831 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
832 "error preparing query\n", 0, 0, 0 );
833 backsql_PrintErrors( bi->db_env, bsi->dbh, sth, rc );
834 bsi->status = LDAP_OTHER;
835 return BACKSQL_CONTINUE;
838 Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->oc->id, 0, 0 );
840 if ( backsql_BindParamID( sth, 1, &bsi->oc->id ) != SQL_SUCCESS ) {
841 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
842 "error binding objectclass id parameter\n", 0, 0, 0 );
843 bsi->status = LDAP_OTHER;
844 return BACKSQL_CONTINUE;
847 switch ( bsi->scope ) {
848 case LDAP_SCOPE_BASE:
849 Debug( LDAP_DEBUG_TRACE, "(base)dn: '%s'\n",
850 bsi->base_dn->bv_val, 0, 0 );
852 rc = backsql_BindParamStr( sth, 2, bsi->base_dn->bv_val,
853 BACKSQL_MAX_DN_LEN );
854 if ( rc != SQL_SUCCESS ) {
855 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
856 "error binding base_dn parameter\n", 0, 0, 0 );
857 backsql_PrintErrors( bi->db_env, bsi->dbh,
859 bsi->status = LDAP_OTHER;
860 return BACKSQL_CONTINUE;
864 case LDAP_SCOPE_SUBTREE: {
867 * + 1 because we need room for '%'; this makes a subtree
868 * search for a DN BACKSQL_MAX_DN_LEN long legal
869 * if it returns that DN only
871 char temp_base_dn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
874 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
875 * however this should be handled earlier
877 assert( bsi->base_dn->bv_len <= BACKSQL_MAX_DN_LEN );
880 * Sets the parameters for the SQL built earlier
881 * NOTE that all the databases could actually use
882 * the TimesTen version, which would be cleaner
883 * and would also eliminate the need for the
884 * subtree_cond line in the configuration file.
885 * For now, I'm leaving it the way it is,
886 * so non-TimesTen databases use the original code.
887 * But at some point this should get cleaned up.
889 * If "dn" is being used, do a suffix search.
890 * If "dn_ru" is being used, do a prefix search.
892 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
893 temp_base_dn[ 0 ] = '\0';
894 for ( i = 0, j = bsi->base_dn->bv_len - 1;
896 temp_base_dn[ i ] = bsi->base_dn->bv_val[ j ];
898 temp_base_dn[ i ] = '%';
899 temp_base_dn[ i + 1 ] = '\0';
902 temp_base_dn[ 0 ] = '%';
903 AC_MEMCPY( &temp_base_dn[ 1 ], bsi->base_dn->bv_val,
904 bsi->base_dn->bv_len + 1 );
906 ldap_pvt_str2upper( temp_base_dn );
908 Debug( LDAP_DEBUG_TRACE, "(sub)dn: '%s'\n", temp_base_dn,
911 rc = backsql_BindParamStr( sth, 2, temp_base_dn,
912 BACKSQL_MAX_DN_LEN );
913 if ( rc != SQL_SUCCESS ) {
914 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
915 "error binding base_dn parameter (2)\n",
917 backsql_PrintErrors( bi->db_env, bsi->dbh,
919 bsi->status = LDAP_OTHER;
920 return BACKSQL_CONTINUE;
925 case LDAP_SCOPE_ONELEVEL:
926 res = backsql_dn2id( bi, &base_id,
927 bsi->dbh, bsi->base_dn );
928 if ( res != LDAP_SUCCESS ) {
929 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
930 "could not retrieve base_dn id%s\n",
931 res == LDAP_NO_SUCH_OBJECT ? ": no such entry"
934 return BACKSQL_CONTINUE;
937 Debug( LDAP_DEBUG_TRACE, "(one)id: '%s'\n", base_id.id,
940 rc = backsql_BindParamID( sth, 2, &base_id.id );
941 backsql_free_entryID( &base_id, 0 );
942 if ( rc != SQL_SUCCESS ) {
943 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
944 "error binding base id parameter\n", 0, 0, 0 );
945 bsi->status = LDAP_OTHER;
946 return BACKSQL_CONTINUE;
951 rc = SQLExecute( sth );
952 if ( !BACKSQL_SUCCESS( rc ) ) {
953 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
954 "error executing query\n", 0, 0, 0 );
955 backsql_PrintErrors( bi->db_env, bsi->dbh, sth, rc );
956 SQLFreeStmt( sth, SQL_DROP );
957 bsi->status = LDAP_OTHER;
958 return BACKSQL_CONTINUE;
961 backsql_BindRowAsStrings( sth, &row );
962 rc = SQLFetch( sth );
963 for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
964 c_id = (backsql_entryID *)ch_calloc( 1,
965 sizeof( backsql_entryID ) );
966 c_id->id = strtol( row.cols[ 0 ], NULL, 0 );
967 c_id->keyval = strtol( row.cols[ 1 ], NULL, 0 );
968 c_id->oc_id = bsi->oc->id;
969 ber_str2bv( row.cols[ 3 ], 0, 1, &c_id->dn );
970 c_id->next = bsi->id_list;
974 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
975 "added entry id=%ld, keyval=%ld dn='%s'\n",
976 c_id->id, c_id->keyval, row.cols[ 3 ] );
978 if ( bsi->n_candidates == -1 ) {
982 backsql_FreeRow( &row );
983 SQLFreeStmt( sth, SQL_DROP );
985 Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates()\n", 0, 0, 0 );
987 return ( bsi->n_candidates == -1 ? BACKSQL_STOP : BACKSQL_CONTINUE );
991 backsql_search( Operation *op, SlapReply *rs )
993 backsql_info *bi = (backsql_info *)op->o_bd->be_private;
999 backsql_srch_info srch_info;
1000 backsql_entryID *eid = NULL;
1001 struct slap_limits_set *limit = NULL;
1004 manageDSAit = get_manageDSAit( op );
1006 Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1007 "base='%s', filter='%s', scope=%d,",
1008 op->o_req_ndn.bv_val,
1009 op->oq_search.rs_filterstr.bv_val,
1010 op->oq_search.rs_scope );
1011 Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1012 "attributes to load: %s\n",
1013 op->oq_search.rs_deref,
1014 op->oq_search.rs_attrsonly,
1015 op->oq_search.rs_attrs == NULL ? "all" : "custom list" );
1017 if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1018 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1019 "search base length (%ld) exceeds max length (%ld)\n",
1020 op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1022 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1023 * since it is impossible that such a long DN exists
1026 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1027 send_ldap_result( op, rs );
1031 sres = backsql_get_db_conn( op, &dbh );
1032 if ( sres != LDAP_SUCCESS ) {
1033 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1034 "could not get connection handle - exiting\n",
1037 rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
1038 send_ldap_result( op, rs );
1042 /* if not root, get appropriate limits */
1043 if ( be_isroot( op->o_bd, &op->o_ndn ) ) {
1046 ( void ) get_limits( op->o_bd, &op->o_ndn, &limit );
1049 /* The time/size limits come first because they require very little
1050 * effort, so there's no chance the candidates are selected and then
1051 * the request is not honored only because of time/size constraints */
1053 /* if no time limit requested, use soft limit (unless root!) */
1055 if ( op->oq_search.rs_tlimit == 0 ) {
1056 op->oq_search.rs_tlimit = -1; /* allow root to set no limit */
1059 if ( op->oq_search.rs_slimit == 0 ) {
1060 op->oq_search.rs_slimit = -1;
1064 /* if no limit is required, use soft limit */
1065 if ( op->oq_search.rs_tlimit <= 0 ) {
1066 op->oq_search.rs_tlimit = limit->lms_t_soft;
1068 /* if requested limit higher than hard limit, abort */
1069 } else if ( op->oq_search.rs_tlimit > limit->lms_t_hard ) {
1070 /* no hard limit means use soft instead */
1071 if ( limit->lms_t_hard == 0
1072 && limit->lms_t_soft > -1
1073 && op->oq_search.rs_tlimit > limit->lms_t_soft ) {
1074 op->oq_search.rs_tlimit = limit->lms_t_soft;
1076 /* positive hard limit means abort */
1077 } else if ( limit->lms_t_hard > 0 ) {
1078 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1079 send_ldap_result( op, rs );
1083 /* negative hard limit means no limit */
1086 /* if no limit is required, use soft limit */
1087 if ( op->oq_search.rs_slimit <= 0 ) {
1088 op->oq_search.rs_slimit = limit->lms_s_soft;
1090 /* if requested limit higher than hard limit, abort */
1091 } else if ( op->oq_search.rs_slimit > limit->lms_s_hard ) {
1092 /* no hard limit means use soft instead */
1093 if ( limit->lms_s_hard == 0
1094 && limit->lms_s_soft > -1
1095 && op->oq_search.rs_slimit > limit->lms_s_soft ) {
1096 op->oq_search.rs_slimit = limit->lms_s_soft;
1098 /* positive hard limit means abort */
1099 } else if ( limit->lms_s_hard > 0 ) {
1100 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1101 send_ldap_result( op, rs );
1105 /* negative hard limit means no limit */
1109 /* compute it anyway; root does not use it */
1110 stoptime = op->o_time + op->oq_search.rs_tlimit;
1112 backsql_init_search( &srch_info, &op->o_req_dn,
1113 op->oq_search.rs_scope,
1114 op->oq_search.rs_slimit, op->oq_search.rs_tlimit,
1115 stoptime, op->oq_search.rs_filter,
1116 dbh, op, op->oq_search.rs_attrs );
1119 * for each objectclass we try to construct query which gets IDs
1120 * of entries matching LDAP query filter and scope (or at least
1121 * candidates), and get the IDs
1123 srch_info.n_candidates = ( isroot ? -2 : limit->lms_s_unchecked == -1
1124 ? -2 : limit->lms_s_unchecked );
1125 avl_apply( bi->oc_by_oc, backsql_oc_get_candidates,
1126 &srch_info, BACKSQL_STOP, AVL_INORDER );
1127 if ( !isroot && limit->lms_s_unchecked != -1 ) {
1128 if ( srch_info.n_candidates == -1 ) {
1129 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1130 send_ldap_result( op, rs );
1136 * now we load candidate entries (only those attributes
1137 * mentioned in attrs and filter), test it against full filter
1138 * and then send to client
1140 for ( eid = srch_info.id_list; eid != NULL;
1141 eid = backsql_free_entryID( eid, 1 ) ) {
1142 Attribute *hasSubordinate = NULL,
1145 /* check for abandon */
1146 if ( op->o_abandon ) {
1150 /* check time limit */
1151 if ( op->oq_search.rs_tlimit != -1 && slap_get_time() > stoptime ) {
1152 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
1153 rs->sr_ctrls = NULL;
1154 rs->sr_ref = rs->sr_v2ref;
1155 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1157 send_ldap_result( op, rs );
1161 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
1162 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
1163 eid->id, eid->oc_id, eid->keyval );
1165 entry = (Entry *)ch_calloc( sizeof( Entry ), 1 );
1166 res = backsql_id2entry( &srch_info, entry, eid );
1167 if ( res == NULL ) {
1168 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1169 "error in backsql_id2entry() "
1170 "- skipping entry\n", 0, 0, 0 );
1174 if ( !manageDSAit &&
1175 op->oq_search.rs_scope != LDAP_SCOPE_BASE &&
1176 is_entry_referral( entry ) ) {
1178 struct berval matched_dn;
1180 ber_dupbv( &matched_dn, &entry->e_name );
1181 refs = get_entry_referrals( op, entry );
1183 rs->sr_ref = referral_rewrite( refs,
1184 &matched_dn, &op->o_req_dn,
1185 op->oq_search.rs_scope );
1186 ber_bvarray_free( refs );
1190 rs->sr_text = "bad_referral object";
1193 rs->sr_err = LDAP_REFERRAL;
1194 rs->sr_matched = matched_dn.bv_val;
1195 send_search_reference( op, rs );
1197 ber_bvarray_free( rs->sr_ref );
1199 ber_memfree( matched_dn.bv_val );
1200 rs->sr_matched = NULL;
1206 * We use this flag since we need to parse the filter
1207 * anyway; we should have used the frontend API function
1208 * filter_has_subordinates()
1210 if ( srch_info.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
1213 rc = backsql_has_children( bi, dbh, &entry->e_nname );
1216 case LDAP_COMPARE_TRUE:
1217 case LDAP_COMPARE_FALSE:
1218 hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
1219 if ( hasSubordinate != NULL ) {
1220 for ( a = entry->e_attrs;
1224 a->a_next = hasSubordinate;
1230 Debug(LDAP_DEBUG_TRACE,
1231 "backsql_search(): "
1232 "has_children failed( %d)\n",
1243 if ( test_filter( op, entry, op->oq_search.rs_filter )
1244 == LDAP_COMPARE_TRUE ) {
1245 if ( hasSubordinate && !( srch_info.bsi_flags & BSQL_SF_ALL_OPER )
1246 && !ad_inlist( slap_schema.si_ad_hasSubordinates, op->oq_search.rs_attrs ) ) {
1248 attr_free( hasSubordinate );
1249 hasSubordinate = NULL;
1252 #if 0 /* noop is masked SLAP_CTRL_UPDATE */
1257 rs->sr_attrs = op->oq_search.rs_attrs;
1258 rs->sr_entry = entry;
1259 sres = send_search_entry( op, rs );
1260 rs->sr_entry = NULL;
1261 rs->sr_attrs = NULL;
1271 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1272 "connection lost\n", 0, 0, 0 );
1277 * FIXME: send_search_entry failed;
1283 entry_free( entry );
1285 if ( op->oq_search.rs_slimit != -1
1286 && rs->sr_nentries >= op->oq_search.rs_slimit ) {
1287 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1288 send_ldap_result( op, rs );
1295 if ( rs->sr_nentries > 0 ) {
1296 rs->sr_ref = rs->sr_v2ref;
1297 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
1300 rs->sr_err = srch_info.status;
1302 send_ldap_result( op, rs );
1304 if ( rs->sr_v2ref ) {
1305 ber_bvarray_free( rs->sr_v2ref );
1306 rs->sr_v2ref = NULL;
1310 ch_free( srch_info.attrs );
1312 Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
1316 #endif /* SLAPD_SQL */