1 /* $OpenLDAP: pkg/ldap/servers/slapd/back-sql/search.c,v 1.117.2.8 2008/02/11 23:26:48 kurt Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1999-2008 The OpenLDAP Foundation.
5 * Portions Copyright 1999 Dmitry Kovalev.
6 * Portions Copyright 2002 Pierangelo Masarati.
7 * Portions Copyright 2004 Mark Adamson.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
19 * This work was initially developed by Dmitry Kovalev for inclusion
20 * by OpenLDAP Software. Additional significant contributors include
21 * Pierangelo Masarati and Mark Adamson.
27 #include <sys/types.h>
28 #include "ac/string.h"
33 #include "proto-sql.h"
35 static int backsql_process_filter( backsql_srch_info
*bsi
, Filter
*f
);
36 static int backsql_process_filter_eq( backsql_srch_info
*bsi
,
37 backsql_at_map_rec
*at
,
38 int casefold
, struct berval
*filter_value
);
39 static int backsql_process_filter_like( backsql_srch_info
*bsi
,
40 backsql_at_map_rec
*at
,
41 int casefold
, struct berval
*filter_value
);
42 static int backsql_process_filter_attr( backsql_srch_info
*bsi
, Filter
*f
,
43 backsql_at_map_rec
*at
);
45 /* For LDAP_CONTROL_PAGEDRESULTS, a 32 bit cookie is available to keep track of
46 the state of paged results. The ldap_entries.id and oc_map_id values of the
47 last entry returned are used as the cookie, so 6 bits are used for the OC id
48 and the other 26 for ldap_entries ID number. If your max(oc_map_id) is more
49 than 63, you will need to steal more bits from ldap_entries ID number and
50 put them into the OC ID part of the cookie. */
51 #define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F))
52 #define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6)
53 #define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F)
55 static int parse_paged_cookie( Operation
*op
, SlapReply
*rs
);
57 static void send_paged_response(
63 backsql_attrlist_add( backsql_srch_info
*bsi
, AttributeDescription
*ad
)
66 AttributeName
*an
= NULL
;
68 if ( bsi
->bsi_attrs
== NULL
) {
73 * clear the list (retrieve all attrs)
76 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_attrs
, bsi
->bsi_op
->o_tmpmemctx
);
77 bsi
->bsi_attrs
= NULL
;
78 bsi
->bsi_flags
|= BSQL_SF_ALL_ATTRS
;
83 if ( slap_ad_is_binary( ad
) ) {
84 ad
= ad
->ad_type
->sat_ad
;
87 for ( ; !BER_BVISNULL( &bsi
->bsi_attrs
[ n_attrs
].an_name
); n_attrs
++ ) {
88 an
= &bsi
->bsi_attrs
[ n_attrs
];
90 Debug( LDAP_DEBUG_TRACE
, "==>backsql_attrlist_add(): "
91 "attribute \"%s\" is in list\n",
92 an
->an_name
.bv_val
, 0, 0 );
94 * We can live with strcmp because the attribute
95 * list has been normalized before calling be_search
97 if ( !BACKSQL_NCMP( &an
->an_name
, &ad
->ad_cname
) ) {
102 Debug( LDAP_DEBUG_TRACE
, "==>backsql_attrlist_add(): "
103 "adding \"%s\" to list\n", ad
->ad_cname
.bv_val
, 0, 0 );
105 an
= (AttributeName
*)bsi
->bsi_op
->o_tmprealloc( bsi
->bsi_attrs
,
106 sizeof( AttributeName
) * ( n_attrs
+ 2 ),
107 bsi
->bsi_op
->o_tmpmemctx
);
112 an
[ n_attrs
].an_name
= ad
->ad_cname
;
113 an
[ n_attrs
].an_desc
= ad
;
114 BER_BVZERO( &an
[ n_attrs
+ 1 ].an_name
);
122 * Initializes the search structure.
124 * If get_base_id != 0, the field bsi_base_id is filled
125 * with the entryID of bsi_base_ndn; it must be freed
126 * by backsql_free_entryID() when no longer required.
128 * NOTE: base must be normalized
132 backsql_srch_info
*bsi
,
133 struct berval
*nbase
,
140 AttributeName
*attrs
,
143 backsql_info
*bi
= (backsql_info
*)op
->o_bd
->be_private
;
144 int rc
= LDAP_SUCCESS
;
146 bsi
->bsi_base_ndn
= nbase
;
147 bsi
->bsi_use_subtree_shortcut
= 0;
148 BER_BVZERO( &bsi
->bsi_base_id
.eid_dn
);
149 BER_BVZERO( &bsi
->bsi_base_id
.eid_ndn
);
150 bsi
->bsi_scope
= scope
;
151 bsi
->bsi_filter
= filter
;
155 bsi
->bsi_flags
= BSQL_SF_NONE
;
157 bsi
->bsi_attrs
= NULL
;
159 if ( BACKSQL_FETCH_ALL_ATTRS( bi
) ) {
161 * if requested, simply try to fetch all attributes
163 bsi
->bsi_flags
|= BSQL_SF_ALL_ATTRS
;
166 if ( BACKSQL_FETCH_ALL_USERATTRS( bi
) ) {
167 bsi
->bsi_flags
|= BSQL_SF_ALL_USER
;
169 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi
) ) {
170 bsi
->bsi_flags
|= BSQL_SF_ALL_OPER
;
173 if ( attrs
== NULL
) {
174 /* NULL means all user attributes */
175 bsi
->bsi_flags
|= BSQL_SF_ALL_USER
;
181 bsi
->bsi_attrs
= (AttributeName
*)bsi
->bsi_op
->o_tmpalloc(
182 sizeof( AttributeName
),
183 bsi
->bsi_op
->o_tmpmemctx
);
184 BER_BVZERO( &bsi
->bsi_attrs
[ 0 ].an_name
);
186 for ( p
= attrs
; !BER_BVISNULL( &p
->an_name
); p
++ ) {
187 if ( BACKSQL_NCMP( &p
->an_name
, &AllUser
) == 0 ) {
189 bsi
->bsi_flags
|= BSQL_SF_ALL_USER
;
191 /* if all attrs are requested, there's
192 * no need to continue */
193 if ( BSQL_ISF_ALL_ATTRS( bsi
) ) {
194 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_attrs
,
195 bsi
->bsi_op
->o_tmpmemctx
);
196 bsi
->bsi_attrs
= NULL
;
201 } else if ( BACKSQL_NCMP( &p
->an_name
, &AllOper
) == 0 ) {
203 bsi
->bsi_flags
|= BSQL_SF_ALL_OPER
;
205 /* if all attrs are requested, there's
206 * no need to continue */
207 if ( BSQL_ISF_ALL_ATTRS( bsi
) ) {
208 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_attrs
,
209 bsi
->bsi_op
->o_tmpmemctx
);
210 bsi
->bsi_attrs
= NULL
;
215 } else if ( BACKSQL_NCMP( &p
->an_name
, &NoAttrs
) == 0 ) {
219 } else if ( p
->an_desc
== slap_schema
.si_ad_objectClass
) {
223 backsql_attrlist_add( bsi
, p
->an_desc
);
226 if ( got_oc
== 0 && !( bsi
->bsi_flags
& BSQL_SF_ALL_USER
) ) {
227 /* add objectClass if not present,
228 * because it is required to understand
229 * if an entry is a referral, an alias
231 backsql_attrlist_add( bsi
, slap_schema
.si_ad_objectClass
);
235 if ( !BSQL_ISF_ALL_ATTRS( bsi
) && bi
->sql_anlist
) {
238 /* use hints if available */
239 for ( p
= bi
->sql_anlist
; !BER_BVISNULL( &p
->an_name
); p
++ ) {
240 if ( BACKSQL_NCMP( &p
->an_name
, &AllUser
) == 0 ) {
242 bsi
->bsi_flags
|= BSQL_SF_ALL_USER
;
244 /* if all attrs are requested, there's
245 * no need to continue */
246 if ( BSQL_ISF_ALL_ATTRS( bsi
) ) {
247 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_attrs
,
248 bsi
->bsi_op
->o_tmpmemctx
);
249 bsi
->bsi_attrs
= NULL
;
254 } else if ( BACKSQL_NCMP( &p
->an_name
, &AllOper
) == 0 ) {
256 bsi
->bsi_flags
|= BSQL_SF_ALL_OPER
;
258 /* if all attrs are requested, there's
259 * no need to continue */
260 if ( BSQL_ISF_ALL_ATTRS( bsi
) ) {
261 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_attrs
,
262 bsi
->bsi_op
->o_tmpmemctx
);
263 bsi
->bsi_attrs
= NULL
;
269 backsql_attrlist_add( bsi
, p
->an_desc
);
275 bsi
->bsi_id_list
= NULL
;
276 bsi
->bsi_id_listtail
= &bsi
->bsi_id_list
;
277 bsi
->bsi_n_candidates
= 0;
278 bsi
->bsi_stoptime
= stoptime
;
279 BER_BVZERO( &bsi
->bsi_sel
.bb_val
);
280 bsi
->bsi_sel
.bb_len
= 0;
281 BER_BVZERO( &bsi
->bsi_from
.bb_val
);
282 bsi
->bsi_from
.bb_len
= 0;
283 BER_BVZERO( &bsi
->bsi_join_where
.bb_val
);
284 bsi
->bsi_join_where
.bb_len
= 0;
285 BER_BVZERO( &bsi
->bsi_flt_where
.bb_val
);
286 bsi
->bsi_flt_where
.bb_len
= 0;
287 bsi
->bsi_filter_oc
= NULL
;
289 if ( BACKSQL_IS_GET_ID( flags
) ) {
290 int matched
= BACKSQL_IS_MATCHED( flags
);
291 int getentry
= BACKSQL_IS_GET_ENTRY( flags
);
294 assert( op
->o_bd
->be_private
!= NULL
);
296 rc
= backsql_dn2id( op
, rs
, dbh
, nbase
, &bsi
->bsi_base_id
,
299 /* the entry is collected either if requested for by getentry
300 * or if get noSuchObject and requested to climb the tree,
301 * so that a matchedDN or a referral can be returned */
302 if ( ( rc
== LDAP_NO_SUCH_OBJECT
&& matched
) || getentry
) {
303 if ( !BER_BVISNULL( &bsi
->bsi_base_id
.eid_ndn
) ) {
304 assert( bsi
->bsi_e
!= NULL
);
306 if ( dn_match( nbase
, &bsi
->bsi_base_id
.eid_ndn
) )
312 * let's see if it is a referral and, in case, get it
314 backsql_attrlist_add( bsi
, slap_schema
.si_ad_ref
);
315 rc
= backsql_id2entry( bsi
, &bsi
->bsi_base_id
);
316 if ( rc
== LDAP_SUCCESS
) {
317 if ( is_entry_referral( bsi
->bsi_e
) )
319 BerVarray erefs
= get_entry_referrals( op
, bsi
->bsi_e
);
321 rc
= rs
->sr_err
= LDAP_REFERRAL
;
322 rs
->sr_ref
= referral_rewrite( erefs
,
323 &bsi
->bsi_e
->e_nname
,
326 ber_bvarray_free( erefs
);
329 rc
= rs
->sr_err
= LDAP_OTHER
;
330 rs
->sr_text
= "bad referral object";
333 } else if ( !gotit
) {
334 rc
= rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
343 if ( gotit
&& BACKSQL_IS_GET_OC( flags
) ) {
344 bsi
->bsi_base_id
.eid_oc
= backsql_id2oc( bi
,
345 bsi
->bsi_base_id
.eid_oc_id
);
346 if ( bsi
->bsi_base_id
.eid_oc
== NULL
) {
348 backsql_free_entryID( &bsi
->bsi_base_id
, 1,
350 rc
= rs
->sr_err
= LDAP_OTHER
;
355 bsi
->bsi_status
= rc
;
363 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_attrs
,
364 bsi
->bsi_op
->o_tmpmemctx
);
372 backsql_process_filter_list( backsql_srch_info
*bsi
, Filter
*f
, int op
)
380 backsql_strfcat_x( &bsi
->bsi_flt_where
,
381 bsi
->bsi_op
->o_tmpmemctx
, "c", '(' /* ) */ );
384 res
= backsql_process_filter( bsi
, f
);
387 * TimesTen : If the query has no answers,
388 * don't bother to run the query.
399 case LDAP_FILTER_AND
:
400 backsql_strfcat_x( &bsi
->bsi_flt_where
,
401 bsi
->bsi_op
->o_tmpmemctx
, "l",
402 (ber_len_t
)STRLENOF( " AND " ),
407 backsql_strfcat_x( &bsi
->bsi_flt_where
,
408 bsi
->bsi_op
->o_tmpmemctx
, "l",
409 (ber_len_t
)STRLENOF( " OR " ),
415 backsql_strfcat_x( &bsi
->bsi_flt_where
,
416 bsi
->bsi_op
->o_tmpmemctx
, "c", /* ( */ ')' );
422 backsql_process_sub_filter( backsql_srch_info
*bsi
, Filter
*f
,
423 backsql_at_map_rec
*at
)
425 backsql_info
*bi
= (backsql_info
*)bsi
->bsi_op
->o_bd
->be_private
;
433 /* always uppercase strings by now */
434 #ifdef BACKSQL_UPPERCASE_FILTER
435 if ( f
->f_sub_desc
->ad_type
->sat_substr
&&
436 SLAP_MR_ASSOCIATED( f
->f_sub_desc
->ad_type
->sat_substr
,
437 bi
->sql_caseIgnoreMatch
) )
438 #endif /* BACKSQL_UPPERCASE_FILTER */
443 if ( f
->f_sub_desc
->ad_type
->sat_substr
&&
444 SLAP_MR_ASSOCIATED( f
->f_sub_desc
->ad_type
->sat_substr
,
445 bi
->sql_telephoneNumberMatch
) )
452 * to check for matching telephone numbers
453 * with intermixed chars, e.g. val='1234'
456 * val LIKE '%1%2%3%4%'
460 if ( f
->f_sub_initial
.bv_val
) {
461 bv
.bv_len
+= f
->f_sub_initial
.bv_len
;
463 if ( f
->f_sub_any
!= NULL
) {
464 for ( a
= 0; f
->f_sub_any
[ a
].bv_val
!= NULL
; a
++ ) {
465 bv
.bv_len
+= f
->f_sub_any
[ a
].bv_len
;
468 if ( f
->f_sub_final
.bv_val
) {
469 bv
.bv_len
+= f
->f_sub_final
.bv_len
;
471 bv
.bv_len
= 2 * bv
.bv_len
- 1;
472 bv
.bv_val
= ch_malloc( bv
.bv_len
+ 1 );
475 if ( !BER_BVISNULL( &f
->f_sub_initial
) ) {
476 bv
.bv_val
[ s
] = f
->f_sub_initial
.bv_val
[ 0 ];
477 for ( i
= 1; i
< f
->f_sub_initial
.bv_len
; i
++ ) {
478 bv
.bv_val
[ s
+ 2 * i
- 1 ] = '%';
479 bv
.bv_val
[ s
+ 2 * i
] = f
->f_sub_initial
.bv_val
[ i
];
481 bv
.bv_val
[ s
+ 2 * i
- 1 ] = '%';
485 if ( f
->f_sub_any
!= NULL
) {
486 for ( a
= 0; !BER_BVISNULL( &f
->f_sub_any
[ a
] ); a
++ ) {
487 bv
.bv_val
[ s
] = f
->f_sub_any
[ a
].bv_val
[ 0 ];
488 for ( i
= 1; i
< f
->f_sub_any
[ a
].bv_len
; i
++ ) {
489 bv
.bv_val
[ s
+ 2 * i
- 1 ] = '%';
490 bv
.bv_val
[ s
+ 2 * i
] = f
->f_sub_any
[ a
].bv_val
[ i
];
492 bv
.bv_val
[ s
+ 2 * i
- 1 ] = '%';
497 if ( !BER_BVISNULL( &f
->f_sub_final
) ) {
498 bv
.bv_val
[ s
] = f
->f_sub_final
.bv_val
[ 0 ];
499 for ( i
= 1; i
< f
->f_sub_final
.bv_len
; i
++ ) {
500 bv
.bv_val
[ s
+ 2 * i
- 1 ] = '%';
501 bv
.bv_val
[ s
+ 2 * i
] = f
->f_sub_final
.bv_val
[ i
];
503 bv
.bv_val
[ s
+ 2 * i
- 1 ] = '%';
507 bv
.bv_val
[ s
- 1 ] = '\0';
509 (void)backsql_process_filter_like( bsi
, at
, casefold
, &bv
);
510 ch_free( bv
.bv_val
);
516 * When dealing with case-sensitive strings
517 * we may omit normalization; however, normalized
518 * SQL filters are more liberal.
521 backsql_strfcat_x( &bsi
->bsi_flt_where
,
522 bsi
->bsi_op
->o_tmpmemctx
, "c", '(' /* ) */ );
525 Debug( LDAP_DEBUG_TRACE
, "backsql_process_sub_filter(%s):\n",
526 at
->bam_ad
->ad_cname
.bv_val
, 0, 0 );
527 Debug(LDAP_DEBUG_TRACE
, " expr: '%s%s%s'\n", at
->bam_sel_expr
.bv_val
,
528 at
->bam_sel_expr_u
.bv_val
? "' '" : "",
529 at
->bam_sel_expr_u
.bv_val
? at
->bam_sel_expr_u
.bv_val
: "" );
530 if ( casefold
&& BACKSQL_AT_CANUPPERCASE( at
) ) {
532 * If a pre-upper-cased version of the column
533 * or a precompiled upper function exists, use it
535 backsql_strfcat_x( &bsi
->bsi_flt_where
,
536 bsi
->bsi_op
->o_tmpmemctx
,
539 (ber_len_t
)STRLENOF( " LIKE '" ),
543 backsql_strfcat_x( &bsi
->bsi_flt_where
,
544 bsi
->bsi_op
->o_tmpmemctx
,
547 (ber_len_t
)STRLENOF( " LIKE '" ), " LIKE '" );
550 if ( !BER_BVISNULL( &f
->f_sub_initial
) ) {
554 Debug( LDAP_DEBUG_TRACE
,
555 "==>backsql_process_sub_filter(%s): "
556 "sub_initial=\"%s\"\n", at
->bam_ad
->ad_cname
.bv_val
,
557 f
->f_sub_initial
.bv_val
, 0 );
558 #endif /* BACKSQL_TRACE */
560 start
= bsi
->bsi_flt_where
.bb_val
.bv_len
;
561 backsql_strfcat_x( &bsi
->bsi_flt_where
,
562 bsi
->bsi_op
->o_tmpmemctx
,
565 if ( casefold
&& BACKSQL_AT_CANUPPERCASE( at
) ) {
566 ldap_pvt_str2upper( &bsi
->bsi_flt_where
.bb_val
.bv_val
[ start
] );
570 backsql_strfcat_x( &bsi
->bsi_flt_where
,
571 bsi
->bsi_op
->o_tmpmemctx
,
574 if ( f
->f_sub_any
!= NULL
) {
575 for ( i
= 0; !BER_BVISNULL( &f
->f_sub_any
[ i
] ); i
++ ) {
579 Debug( LDAP_DEBUG_TRACE
,
580 "==>backsql_process_sub_filter(%s): "
581 "sub_any[%d]=\"%s\"\n", at
->bam_ad
->ad_cname
.bv_val
,
582 i
, f
->f_sub_any
[ i
].bv_val
);
583 #endif /* BACKSQL_TRACE */
585 start
= bsi
->bsi_flt_where
.bb_val
.bv_len
;
586 backsql_strfcat_x( &bsi
->bsi_flt_where
,
587 bsi
->bsi_op
->o_tmpmemctx
,
591 if ( casefold
&& BACKSQL_AT_CANUPPERCASE( at
) ) {
593 * Note: toupper('%') = '%'
595 ldap_pvt_str2upper( &bsi
->bsi_flt_where
.bb_val
.bv_val
[ start
] );
600 if ( !BER_BVISNULL( &f
->f_sub_final
) ) {
604 Debug( LDAP_DEBUG_TRACE
,
605 "==>backsql_process_sub_filter(%s): "
606 "sub_final=\"%s\"\n", at
->bam_ad
->ad_cname
.bv_val
,
607 f
->f_sub_final
.bv_val
, 0 );
608 #endif /* BACKSQL_TRACE */
610 start
= bsi
->bsi_flt_where
.bb_val
.bv_len
;
611 backsql_strfcat_x( &bsi
->bsi_flt_where
,
612 bsi
->bsi_op
->o_tmpmemctx
,
615 if ( casefold
&& BACKSQL_AT_CANUPPERCASE( at
) ) {
616 ldap_pvt_str2upper( &bsi
->bsi_flt_where
.bb_val
.bv_val
[ start
] );
620 backsql_strfcat_x( &bsi
->bsi_flt_where
,
621 bsi
->bsi_op
->o_tmpmemctx
,
623 (ber_len_t
)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
629 backsql_merge_from_tbls( backsql_srch_info
*bsi
, struct berval
*from_tbls
)
631 if ( BER_BVISNULL( from_tbls
) ) {
635 if ( !BER_BVISNULL( &bsi
->bsi_from
.bb_val
) ) {
639 ber_dupbv_x( &tmp
, from_tbls
, bsi
->bsi_op
->o_tmpmemctx
);
641 for ( start
= tmp
.bv_val
, end
= strchr( start
, ',' ); start
; ) {
646 if ( strstr( bsi
->bsi_from
.bb_val
.bv_val
, start
) == NULL
)
648 backsql_strfcat_x( &bsi
->bsi_from
,
649 bsi
->bsi_op
->o_tmpmemctx
,
654 /* in case there are spaces after the comma... */
655 for ( start
= &end
[1]; isspace( start
[0] ); start
++ );
657 end
= strchr( start
, ',' );
666 bsi
->bsi_op
->o_tmpfree( tmp
.bv_val
, bsi
->bsi_op
->o_tmpmemctx
);
669 backsql_strfcat_x( &bsi
->bsi_from
,
670 bsi
->bsi_op
->o_tmpmemctx
,
678 backsql_process_filter( backsql_srch_info
*bsi
, Filter
*f
)
680 backsql_at_map_rec
**vat
= NULL
;
681 AttributeDescription
*ad
= NULL
;
686 Debug( LDAP_DEBUG_TRACE
, "==>backsql_process_filter()\n", 0, 0, 0 );
687 if ( f
->f_choice
== SLAPD_FILTER_COMPUTED
) {
691 switch ( f
->f_result
) {
692 case LDAP_COMPARE_TRUE
:
693 BER_BVSTR( &flt
, "10=10" );
697 case LDAP_COMPARE_FALSE
:
698 BER_BVSTR( &flt
, "11=0" );
702 case SLAPD_COMPARE_UNDEFINED
:
703 BER_BVSTR( &flt
, "12=0" );
712 Debug( LDAP_DEBUG_TRACE
, "backsql_process_filter(): "
713 "filter computed (%s)\n", msg
, 0, 0 );
714 backsql_strfcat_x( &bsi
->bsi_flt_where
,
715 bsi
->bsi_op
->o_tmpmemctx
, "b", &flt
);
720 switch( f
->f_choice
) {
722 rc
= backsql_process_filter_list( bsi
, f
->f_or
,
727 case LDAP_FILTER_AND
:
728 rc
= backsql_process_filter_list( bsi
, f
->f_and
,
733 case LDAP_FILTER_NOT
:
734 backsql_strfcat_x( &bsi
->bsi_flt_where
,
735 bsi
->bsi_op
->o_tmpmemctx
,
737 (ber_len_t
)STRLENOF( "NOT (" /* ) */ ),
739 rc
= backsql_process_filter( bsi
, f
->f_not
);
740 backsql_strfcat_x( &bsi
->bsi_flt_where
,
741 bsi
->bsi_op
->o_tmpmemctx
,
746 case LDAP_FILTER_PRESENT
:
750 case LDAP_FILTER_EXT
:
751 ad
= f
->f_mra
->ma_desc
;
752 if ( f
->f_mr_dnattrs
) {
754 * if dn attrs filtering is requested, better return
755 * success and let test_filter() deal with candidate
756 * selection; otherwise we'd need to set conditions
757 * on the contents of the DN, e.g. "SELECT ... FROM
758 * ldap_entries AS attributeName WHERE attributeName.dn
759 * like '%attributeName=value%'"
761 backsql_strfcat_x( &bsi
->bsi_flt_where
,
762 bsi
->bsi_op
->o_tmpmemctx
,
764 (ber_len_t
)STRLENOF( "1=1" ), "1=1" );
765 bsi
->bsi_status
= LDAP_SUCCESS
;
786 * Turn structuralObjectClass into objectClass
788 if ( ad
== slap_schema
.si_ad_objectClass
789 || ad
== slap_schema
.si_ad_structuralObjectClass
)
792 * If the filter is LDAP_FILTER_PRESENT, then it's done;
793 * otherwise, let's see if we are lucky: filtering
794 * for "structural" objectclass or ancestor...
796 switch ( f
->f_choice
) {
797 case LDAP_FILTER_EQUALITY
:
799 ObjectClass
*oc
= oc_bvfind( &f
->f_av_value
);
802 Debug( LDAP_DEBUG_TRACE
,
803 "backsql_process_filter(): "
804 "unknown objectClass \"%s\" "
806 f
->f_av_value
.bv_val
, 0, 0 );
807 bsi
->bsi_status
= LDAP_OTHER
;
813 * "structural" objectClass inheritance:
814 * - a search for "person" will also return
816 * - a search for "top" will return everything
818 if ( is_object_subclass( oc
, bsi
->bsi_oc
->bom_oc
) ) {
819 static struct berval ldap_entry_objclasses
= BER_BVC( "ldap_entry_objclasses" );
821 backsql_merge_from_tbls( bsi
, &ldap_entry_objclasses
);
823 backsql_strfcat_x( &bsi
->bsi_flt_where
,
824 bsi
->bsi_op
->o_tmpmemctx
,
826 (ber_len_t
)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
827 "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
828 &bsi
->bsi_oc
->bom_oc
->soc_cname
,
829 (ber_len_t
)STRLENOF( /* ((' */ "'))" ),
831 bsi
->bsi_status
= LDAP_SUCCESS
;
839 case LDAP_FILTER_PRESENT
:
840 backsql_strfcat_x( &bsi
->bsi_flt_where
,
841 bsi
->bsi_op
->o_tmpmemctx
,
843 (ber_len_t
)STRLENOF( "3=3" ), "3=3" );
844 bsi
->bsi_status
= LDAP_SUCCESS
;
848 /* FIXME: LDAP_FILTER_EXT? */
851 Debug( LDAP_DEBUG_TRACE
,
852 "backsql_process_filter(): "
853 "illegal/unhandled filter "
854 "on objectClass attribute",
856 bsi
->bsi_status
= LDAP_OTHER
;
861 } else if ( ad
== slap_schema
.si_ad_entryUUID
) {
863 #ifdef BACKSQL_ARBITRARY_KEY
864 struct berval keyval
;
865 #else /* ! BACKSQL_ARBITRARY_KEY */
866 unsigned long keyval
;
867 char keyvalbuf
[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
868 #endif /* ! BACKSQL_ARBITRARY_KEY */
870 switch ( f
->f_choice
) {
871 case LDAP_FILTER_EQUALITY
:
872 backsql_entryUUID_decode( &f
->f_av_value
, &oc_id
, &keyval
);
874 if ( oc_id
!= bsi
->bsi_oc
->bom_id
) {
875 bsi
->bsi_status
= LDAP_SUCCESS
;
880 #ifdef BACKSQL_ARBITRARY_KEY
881 backsql_strfcat_x( &bsi
->bsi_flt_where
,
882 bsi
->bsi_op
->o_tmpmemctx
,
884 &bsi
->bsi_oc
->bom_keytbl
, '.',
885 &bsi
->bsi_oc
->bom_keycol
,
886 STRLENOF( " LIKE '" ), " LIKE '",
888 #else /* ! BACKSQL_ARBITRARY_KEY */
889 snprintf( keyvalbuf
, sizeof( keyvalbuf
), "%lu", keyval
);
890 backsql_strfcat_x( &bsi
->bsi_flt_where
,
891 bsi
->bsi_op
->o_tmpmemctx
,
893 &bsi
->bsi_oc
->bom_keytbl
, '.',
894 &bsi
->bsi_oc
->bom_keycol
, '=', keyvalbuf
);
895 #endif /* ! BACKSQL_ARBITRARY_KEY */
898 case LDAP_FILTER_PRESENT
:
899 backsql_strfcat_x( &bsi
->bsi_flt_where
,
900 bsi
->bsi_op
->o_tmpmemctx
,
902 (ber_len_t
)STRLENOF( "4=4" ), "4=4" );
910 bsi
->bsi_flags
|= BSQL_SF_FILTER_ENTRYUUID
;
914 #ifdef BACKSQL_SYNCPROV
915 } else if ( ad
== slap_schema
.si_ad_entryCSN
) {
917 * support for syncrepl as producer...
920 if ( !bsi
->bsi_op
->o_sync
) {
921 /* unsupported at present... */
922 bsi
->bsi_status
= LDAP_OTHER
;
928 bsi
->bsi_flags
|= ( BSQL_SF_FILTER_ENTRYCSN
| BSQL_SF_RETURN_ENTRYUUID
);
930 /* if doing a syncrepl, try to return as much as possible,
931 * and always match the filter */
932 backsql_strfcat_x( &bsi
->bsi_flt_where
,
933 bsi
->bsi_op
->o_tmpmemctx
,
935 (ber_len_t
)STRLENOF( "5=5" ), "5=5" );
937 /* save for later use in operational attributes */
938 /* FIXME: saves only the first occurrence, because
939 * the filter during updates is written as
940 * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
941 * so we want our fake entryCSN to match the greatest
944 if ( bsi
->bsi_op
->o_private
== NULL
) {
945 bsi
->bsi_op
->o_private
= &f
->f_av_value
;
947 bsi
->bsi_status
= LDAP_SUCCESS
;
951 #endif /* BACKSQL_SYNCPROV */
953 } else if ( ad
== slap_schema
.si_ad_hasSubordinates
|| ad
== NULL
) {
955 * FIXME: this is not robust; e.g. a filter
956 * '(!(hasSubordinates=TRUE))' fails because
957 * in SQL it would read 'NOT (1=1)' instead
959 * Note however that hasSubordinates is boolean,
960 * so a more appropriate filter would be
961 * '(hasSubordinates=FALSE)'
963 * A more robust search for hasSubordinates
964 * would * require joining the ldap_entries table
965 * selecting if there are descendants of the
968 backsql_strfcat_x( &bsi
->bsi_flt_where
,
969 bsi
->bsi_op
->o_tmpmemctx
,
971 (ber_len_t
)STRLENOF( "6=6" ), "6=6" );
972 if ( ad
== slap_schema
.si_ad_hasSubordinates
) {
974 * instruct candidate selection algorithm
975 * and attribute list to try to detect
976 * if an entry has subordinates
978 bsi
->bsi_flags
|= BSQL_SF_FILTER_HASSUBORDINATE
;
982 * clear attributes to fetch, to require ALL
983 * and try extended match on all attributes
985 backsql_attrlist_add( bsi
, NULL
);
992 * attribute inheritance:
994 if ( backsql_supad2at( bsi
->bsi_oc
, ad
, &vat
) ) {
995 bsi
->bsi_status
= LDAP_OTHER
;
1000 if ( vat
== NULL
) {
1001 /* search anyway; other parts of the filter
1003 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1004 bsi
->bsi_op
->o_tmpmemctx
,
1006 (ber_len_t
)STRLENOF( "7=7" ), "7=7" );
1007 bsi
->bsi_status
= LDAP_SUCCESS
;
1012 /* if required, open extra level of parens */
1014 if ( vat
[0]->bam_next
|| vat
[1] ) {
1015 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1016 bsi
->bsi_op
->o_tmpmemctx
,
1024 if ( backsql_process_filter_attr( bsi
, f
, vat
[i
] ) == -1 ) {
1028 /* if more definitions of the same attr, apply */
1029 if ( vat
[i
]->bam_next
) {
1030 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1031 bsi
->bsi_op
->o_tmpmemctx
,
1033 STRLENOF( " OR " ), " OR " );
1034 vat
[i
] = vat
[i
]->bam_next
;
1038 /* if more descendants of the same attr, apply */
1041 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1042 bsi
->bsi_op
->o_tmpmemctx
,
1044 STRLENOF( " OR " ), " OR " );
1048 /* if needed, close extra level of parens */
1050 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1051 bsi
->bsi_op
->o_tmpmemctx
,
1062 Debug( LDAP_DEBUG_TRACE
,
1063 "<==backsql_process_filter() %s\n",
1064 rc
== 1 ? "succeeded" : "failed", 0, 0);
1070 backsql_process_filter_eq( backsql_srch_info
*bsi
, backsql_at_map_rec
*at
,
1071 int casefold
, struct berval
*filter_value
)
1074 * maybe we should check type of at->sel_expr here somehow,
1075 * to know whether upper_func is applicable, but for now
1076 * upper_func stuff is made for Oracle, where UPPER is
1077 * safely applicable to NUMBER etc.
1079 if ( casefold
&& BACKSQL_AT_CANUPPERCASE( at
) ) {
1082 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1083 bsi
->bsi_op
->o_tmpmemctx
,
1086 &at
->bam_sel_expr_u
,
1087 (ber_len_t
)STRLENOF( "='" ),
1090 start
= bsi
->bsi_flt_where
.bb_val
.bv_len
;
1092 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1093 bsi
->bsi_op
->o_tmpmemctx
,
1096 (ber_len_t
)STRLENOF( /* (' */ "')" ),
1099 ldap_pvt_str2upper( &bsi
->bsi_flt_where
.bb_val
.bv_val
[ start
] );
1102 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1103 bsi
->bsi_op
->o_tmpmemctx
,
1107 (ber_len_t
)STRLENOF( "='" ), "='",
1109 (ber_len_t
)STRLENOF( /* (' */ "')" ),
1117 backsql_process_filter_like( backsql_srch_info
*bsi
, backsql_at_map_rec
*at
,
1118 int casefold
, struct berval
*filter_value
)
1121 * maybe we should check type of at->sel_expr here somehow,
1122 * to know whether upper_func is applicable, but for now
1123 * upper_func stuff is made for Oracle, where UPPER is
1124 * safely applicable to NUMBER etc.
1126 if ( casefold
&& BACKSQL_AT_CANUPPERCASE( at
) ) {
1129 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1130 bsi
->bsi_op
->o_tmpmemctx
,
1133 &at
->bam_sel_expr_u
,
1134 (ber_len_t
)STRLENOF( " LIKE '%" ),
1137 start
= bsi
->bsi_flt_where
.bb_val
.bv_len
;
1139 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1140 bsi
->bsi_op
->o_tmpmemctx
,
1143 (ber_len_t
)STRLENOF( /* (' */ "%')" ),
1146 ldap_pvt_str2upper( &bsi
->bsi_flt_where
.bb_val
.bv_val
[ start
] );
1149 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1150 bsi
->bsi_op
->o_tmpmemctx
,
1154 (ber_len_t
)STRLENOF( " LIKE '%" ),
1157 (ber_len_t
)STRLENOF( /* (' */ "%')" ),
1165 backsql_process_filter_attr( backsql_srch_info
*bsi
, Filter
*f
, backsql_at_map_rec
*at
)
1167 backsql_info
*bi
= (backsql_info
*)bsi
->bsi_op
->o_bd
->be_private
;
1169 struct berval
*filter_value
= NULL
;
1170 MatchingRule
*matching_rule
= NULL
;
1171 struct berval ordering
= BER_BVC("<=");
1173 Debug( LDAP_DEBUG_TRACE
, "==>backsql_process_filter_attr(%s)\n",
1174 at
->bam_ad
->ad_cname
.bv_val
, 0, 0 );
1177 * need to add this attribute to list of attrs to load,
1178 * so that we can do test_filter() later
1180 backsql_attrlist_add( bsi
, at
->bam_ad
);
1182 backsql_merge_from_tbls( bsi
, &at
->bam_from_tbls
);
1184 if ( !BER_BVISNULL( &at
->bam_join_where
)
1185 && strstr( bsi
->bsi_join_where
.bb_val
.bv_val
,
1186 at
->bam_join_where
.bv_val
) == NULL
)
1188 backsql_strfcat_x( &bsi
->bsi_join_where
,
1189 bsi
->bsi_op
->o_tmpmemctx
,
1191 (ber_len_t
)STRLENOF( " AND " ), " AND ",
1192 &at
->bam_join_where
);
1195 switch ( f
->f_choice
) {
1196 case LDAP_FILTER_EQUALITY
:
1197 filter_value
= &f
->f_av_value
;
1198 matching_rule
= at
->bam_ad
->ad_type
->sat_equality
;
1200 goto equality_match
;
1202 /* fail over into next case */
1204 case LDAP_FILTER_EXT
:
1205 filter_value
= &f
->f_mra
->ma_value
;
1206 matching_rule
= f
->f_mr_rule
;
1209 /* always uppercase strings by now */
1210 #ifdef BACKSQL_UPPERCASE_FILTER
1211 if ( SLAP_MR_ASSOCIATED( matching_rule
,
1212 bi
->sql_caseIgnoreMatch
) )
1213 #endif /* BACKSQL_UPPERCASE_FILTER */
1218 /* FIXME: directoryString filtering should use a similar
1219 * approach to deal with non-prettified values like
1220 * " A non prettified value ", by using a LIKE
1221 * filter with all whitespaces collapsed to a single '%' */
1222 if ( SLAP_MR_ASSOCIATED( matching_rule
,
1223 bi
->sql_telephoneNumberMatch
) )
1229 * to check for matching telephone numbers
1230 * with intermized chars, e.g. val='1234'
1233 * val LIKE '%1%2%3%4%'
1236 bv
.bv_len
= 2 * filter_value
->bv_len
- 1;
1237 bv
.bv_val
= ch_malloc( bv
.bv_len
+ 1 );
1239 bv
.bv_val
[ 0 ] = filter_value
->bv_val
[ 0 ];
1240 for ( i
= 1; i
< filter_value
->bv_len
; i
++ ) {
1241 bv
.bv_val
[ 2 * i
- 1 ] = '%';
1242 bv
.bv_val
[ 2 * i
] = filter_value
->bv_val
[ i
];
1244 bv
.bv_val
[ 2 * i
- 1 ] = '\0';
1246 (void)backsql_process_filter_like( bsi
, at
, casefold
, &bv
);
1247 ch_free( bv
.bv_val
);
1252 /* NOTE: this is required by objectClass inheritance
1253 * and auxiliary objectClass use in filters for slightly
1254 * more efficient candidate selection. */
1255 /* FIXME: a bit too many specializations to deal with
1256 * very specific cases... */
1257 if ( at
->bam_ad
== slap_schema
.si_ad_objectClass
1258 || at
->bam_ad
== slap_schema
.si_ad_structuralObjectClass
)
1260 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1261 bsi
->bsi_op
->o_tmpmemctx
,
1263 (ber_len_t
)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1264 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1266 (ber_len_t
)STRLENOF( /* (' */ "')" ),
1272 * maybe we should check type of at->sel_expr here somehow,
1273 * to know whether upper_func is applicable, but for now
1274 * upper_func stuff is made for Oracle, where UPPER is
1275 * safely applicable to NUMBER etc.
1277 (void)backsql_process_filter_eq( bsi
, at
, casefold
, filter_value
);
1280 case LDAP_FILTER_GE
:
1281 ordering
.bv_val
= ">=";
1283 /* fall thru to next case */
1285 case LDAP_FILTER_LE
:
1286 filter_value
= &f
->f_av_value
;
1288 /* always uppercase strings by now */
1289 #ifdef BACKSQL_UPPERCASE_FILTER
1290 if ( at
->bam_ad
->ad_type
->sat_ordering
&&
1291 SLAP_MR_ASSOCIATED( at
->bam_ad
->ad_type
->sat_ordering
,
1292 bi
->sql_caseIgnoreMatch
) )
1293 #endif /* BACKSQL_UPPERCASE_FILTER */
1299 * FIXME: should we uppercase the operands?
1301 if ( casefold
&& BACKSQL_AT_CANUPPERCASE( at
) ) {
1304 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1305 bsi
->bsi_op
->o_tmpmemctx
,
1308 &at
->bam_sel_expr_u
,
1312 start
= bsi
->bsi_flt_where
.bb_val
.bv_len
;
1314 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1315 bsi
->bsi_op
->o_tmpmemctx
,
1318 (ber_len_t
)STRLENOF( /* (' */ "')" ),
1321 ldap_pvt_str2upper( &bsi
->bsi_flt_where
.bb_val
.bv_val
[ start
] );
1324 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1325 bsi
->bsi_op
->o_tmpmemctx
,
1332 (ber_len_t
)STRLENOF( /* (' */ "')" ),
1337 case LDAP_FILTER_PRESENT
:
1338 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1339 bsi
->bsi_op
->o_tmpmemctx
,
1341 (ber_len_t
)STRLENOF( "NOT (" /* ) */),
1344 (ber_len_t
)STRLENOF( /* ( */ " IS NULL)" ),
1345 /* ( */ " IS NULL)" );
1348 case LDAP_FILTER_SUBSTRINGS
:
1349 backsql_process_sub_filter( bsi
, f
, at
);
1352 case LDAP_FILTER_APPROX
:
1353 /* we do our best */
1356 * maybe we should check type of at->sel_expr here somehow,
1357 * to know whether upper_func is applicable, but for now
1358 * upper_func stuff is made for Oracle, where UPPER is
1359 * safely applicable to NUMBER etc.
1361 (void)backsql_process_filter_like( bsi
, at
, 1, &f
->f_av_value
);
1365 /* unhandled filter type; should not happen */
1367 backsql_strfcat_x( &bsi
->bsi_flt_where
,
1368 bsi
->bsi_op
->o_tmpmemctx
,
1370 (ber_len_t
)STRLENOF( "8=8" ), "8=8" );
1375 Debug( LDAP_DEBUG_TRACE
, "<==backsql_process_filter_attr(%s)\n",
1376 at
->bam_ad
->ad_cname
.bv_val
, 0, 0 );
1382 backsql_srch_query( backsql_srch_info
*bsi
, struct berval
*query
)
1384 backsql_info
*bi
= (backsql_info
*)bsi
->bsi_op
->o_bd
->be_private
;
1387 assert( query
!= NULL
);
1388 BER_BVZERO( query
);
1390 bsi
->bsi_use_subtree_shortcut
= 0;
1392 Debug( LDAP_DEBUG_TRACE
, "==>backsql_srch_query()\n", 0, 0, 0 );
1393 BER_BVZERO( &bsi
->bsi_sel
.bb_val
);
1394 BER_BVZERO( &bsi
->bsi_sel
.bb_val
);
1395 bsi
->bsi_sel
.bb_len
= 0;
1396 BER_BVZERO( &bsi
->bsi_from
.bb_val
);
1397 bsi
->bsi_from
.bb_len
= 0;
1398 BER_BVZERO( &bsi
->bsi_join_where
.bb_val
);
1399 bsi
->bsi_join_where
.bb_len
= 0;
1400 BER_BVZERO( &bsi
->bsi_flt_where
.bb_val
);
1401 bsi
->bsi_flt_where
.bb_len
= 0;
1403 backsql_strfcat_x( &bsi
->bsi_sel
,
1404 bsi
->bsi_op
->o_tmpmemctx
,
1406 (ber_len_t
)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1407 "SELECT DISTINCT ldap_entries.id,",
1408 &bsi
->bsi_oc
->bom_keytbl
,
1410 &bsi
->bsi_oc
->bom_keycol
,
1413 if ( !BER_BVISNULL( &bi
->sql_strcast_func
) ) {
1414 backsql_strfcat_x( &bsi
->bsi_sel
,
1415 bsi
->bsi_op
->o_tmpmemctx
,
1417 &bi
->sql_strcast_func
,
1418 (ber_len_t
)STRLENOF( "('" /* ') */ ),
1420 &bsi
->bsi_oc
->bom_oc
->soc_cname
,
1421 (ber_len_t
)STRLENOF( /* (' */ "')" ),
1424 backsql_strfcat_x( &bsi
->bsi_sel
,
1425 bsi
->bsi_op
->o_tmpmemctx
,
1428 &bsi
->bsi_oc
->bom_oc
->soc_cname
,
1432 backsql_strfcat_x( &bsi
->bsi_sel
,
1433 bsi
->bsi_op
->o_tmpmemctx
,
1435 &bi
->sql_dn_oc_aliasing
);
1436 backsql_strfcat_x( &bsi
->bsi_from
,
1437 bsi
->bsi_op
->o_tmpmemctx
,
1439 (ber_len_t
)STRLENOF( " FROM ldap_entries," ),
1440 " FROM ldap_entries,",
1441 &bsi
->bsi_oc
->bom_keytbl
);
1443 backsql_strfcat_x( &bsi
->bsi_join_where
,
1444 bsi
->bsi_op
->o_tmpmemctx
,
1446 (ber_len_t
)STRLENOF( " WHERE " ), " WHERE ",
1447 &bsi
->bsi_oc
->bom_keytbl
,
1449 &bsi
->bsi_oc
->bom_keycol
,
1450 (ber_len_t
)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1451 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1453 switch ( bsi
->bsi_scope
) {
1454 case LDAP_SCOPE_BASE
:
1455 if ( BACKSQL_CANUPPERCASE( bi
) ) {
1456 backsql_strfcat_x( &bsi
->bsi_join_where
,
1457 bsi
->bsi_op
->o_tmpmemctx
,
1459 &bi
->sql_upper_func
,
1460 (ber_len_t
)STRLENOF( "(ldap_entries.dn)=?" ),
1461 "(ldap_entries.dn)=?" );
1463 backsql_strfcat_x( &bsi
->bsi_join_where
,
1464 bsi
->bsi_op
->o_tmpmemctx
,
1466 (ber_len_t
)STRLENOF( "ldap_entries.dn=?" ),
1467 "ldap_entries.dn=?" );
1471 case BACKSQL_SCOPE_BASE_LIKE
:
1472 if ( BACKSQL_CANUPPERCASE( bi
) ) {
1473 backsql_strfcat_x( &bsi
->bsi_join_where
,
1474 bsi
->bsi_op
->o_tmpmemctx
,
1476 &bi
->sql_upper_func
,
1477 (ber_len_t
)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1478 "(ldap_entries.dn) LIKE ?" );
1480 backsql_strfcat_x( &bsi
->bsi_join_where
,
1481 bsi
->bsi_op
->o_tmpmemctx
,
1483 (ber_len_t
)STRLENOF( "ldap_entries.dn LIKE ?" ),
1484 "ldap_entries.dn LIKE ?" );
1488 case LDAP_SCOPE_ONELEVEL
:
1489 backsql_strfcat_x( &bsi
->bsi_join_where
,
1490 bsi
->bsi_op
->o_tmpmemctx
,
1492 (ber_len_t
)STRLENOF( "ldap_entries.parent=?" ),
1493 "ldap_entries.parent=?" );
1496 case LDAP_SCOPE_SUBORDINATE
:
1497 case LDAP_SCOPE_SUBTREE
:
1498 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi
) ) {
1500 BackendDB
*bd
= bsi
->bsi_op
->o_bd
;
1502 assert( bd
->be_nsuffix
!= NULL
);
1504 for ( i
= 0; !BER_BVISNULL( &bd
->be_nsuffix
[ i
] ); i
++ )
1506 if ( dn_match( &bd
->be_nsuffix
[ i
],
1507 bsi
->bsi_base_ndn
) )
1509 /* pass this to the candidate selection
1510 * routine so that the DN is not bound
1511 * to the select statement */
1512 bsi
->bsi_use_subtree_shortcut
= 1;
1518 if ( bsi
->bsi_use_subtree_shortcut
) {
1519 /* Skip the base DN filter, as every entry will match it */
1520 backsql_strfcat_x( &bsi
->bsi_join_where
,
1521 bsi
->bsi_op
->o_tmpmemctx
,
1523 (ber_len_t
)STRLENOF( "9=9"), "9=9");
1525 } else if ( !BER_BVISNULL( &bi
->sql_subtree_cond
) ) {
1526 /* This should always be true... */
1527 backsql_strfcat_x( &bsi
->bsi_join_where
,
1528 bsi
->bsi_op
->o_tmpmemctx
,
1530 &bi
->sql_subtree_cond
);
1532 } else if ( BACKSQL_CANUPPERCASE( bi
) ) {
1533 backsql_strfcat_x( &bsi
->bsi_join_where
,
1534 bsi
->bsi_op
->o_tmpmemctx
,
1536 &bi
->sql_upper_func
,
1537 (ber_len_t
)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1538 "(ldap_entries.dn) LIKE ?" );
1541 backsql_strfcat_x( &bsi
->bsi_join_where
,
1542 bsi
->bsi_op
->o_tmpmemctx
,
1544 (ber_len_t
)STRLENOF( "ldap_entries.dn LIKE ?" ),
1545 "ldap_entries.dn LIKE ?" );
1554 /* If paged results are in effect, ignore low ldap_entries.id numbers */
1555 if ( get_pagedresults(bsi
->bsi_op
) > SLAP_CONTROL_IGNORED
) {
1556 unsigned long lowid
= 0;
1558 /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
1559 if ( bsi
->bsi_oc
->bom_id
== PAGECOOKIE_TO_SQL_OC( ((PagedResultsState
*)bsi
->bsi_op
->o_pagedresults_state
)->ps_cookie
) )
1561 lowid
= PAGECOOKIE_TO_SQL_ID( ((PagedResultsState
*)bsi
->bsi_op
->o_pagedresults_state
)->ps_cookie
);
1565 char lowidstring
[48];
1568 lowidlen
= snprintf( lowidstring
, sizeof( lowidstring
),
1569 " AND ldap_entries.id>%lu", lowid
);
1570 backsql_strfcat_x( &bsi
->bsi_join_where
,
1571 bsi
->bsi_op
->o_tmpmemctx
,
1573 (ber_len_t
)lowidlen
,
1578 rc
= backsql_process_filter( bsi
, bsi
->bsi_filter
);
1580 struct berbuf bb
= BB_NULL
;
1582 backsql_strfcat_x( &bb
,
1583 bsi
->bsi_op
->o_tmpmemctx
,
1585 &bsi
->bsi_sel
.bb_val
,
1586 &bsi
->bsi_from
.bb_val
,
1587 &bsi
->bsi_join_where
.bb_val
,
1588 (ber_len_t
)STRLENOF( " AND " ), " AND ",
1589 &bsi
->bsi_flt_where
.bb_val
);
1593 } else if ( rc
< 0 ) {
1595 * Indicates that there's no possible way the filter matches
1596 * anything. No need to issue the query
1598 free( query
->bv_val
);
1599 BER_BVZERO( query
);
1602 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_sel
.bb_val
.bv_val
, bsi
->bsi_op
->o_tmpmemctx
);
1603 BER_BVZERO( &bsi
->bsi_sel
.bb_val
);
1604 bsi
->bsi_sel
.bb_len
= 0;
1605 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_from
.bb_val
.bv_val
, bsi
->bsi_op
->o_tmpmemctx
);
1606 BER_BVZERO( &bsi
->bsi_from
.bb_val
);
1607 bsi
->bsi_from
.bb_len
= 0;
1608 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_join_where
.bb_val
.bv_val
, bsi
->bsi_op
->o_tmpmemctx
);
1609 BER_BVZERO( &bsi
->bsi_join_where
.bb_val
);
1610 bsi
->bsi_join_where
.bb_len
= 0;
1611 bsi
->bsi_op
->o_tmpfree( bsi
->bsi_flt_where
.bb_val
.bv_val
, bsi
->bsi_op
->o_tmpmemctx
);
1612 BER_BVZERO( &bsi
->bsi_flt_where
.bb_val
);
1613 bsi
->bsi_flt_where
.bb_len
= 0;
1615 Debug( LDAP_DEBUG_TRACE
, "<==backsql_srch_query() returns %s\n",
1616 query
->bv_val
? query
->bv_val
: "NULL", 0, 0 );
1618 return ( rc
<= 0 ? 1 : 0 );
1622 backsql_oc_get_candidates( void *v_oc
, void *v_bsi
)
1624 backsql_oc_map_rec
*oc
= v_oc
;
1625 backsql_srch_info
*bsi
= v_bsi
;
1626 Operation
*op
= bsi
->bsi_op
;
1627 backsql_info
*bi
= (backsql_info
*)bsi
->bsi_op
->o_bd
->be_private
;
1628 struct berval query
;
1629 SQLHSTMT sth
= SQL_NULL_HSTMT
;
1632 BACKSQL_ROW_NTS row
;
1635 int n_candidates
= bsi
->bsi_n_candidates
;
1638 * + 1 because we need room for '%';
1639 * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1640 * this makes a subtree
1641 * search for a DN BACKSQL_MAX_DN_LEN long legal
1642 * if it returns that DN only
1644 char tmp_base_ndn
[ BACKSQL_MAX_DN_LEN
+ 1 + 1 ];
1646 bsi
->bsi_status
= LDAP_SUCCESS
;
1648 Debug( LDAP_DEBUG_TRACE
, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1649 BACKSQL_OC_NAME( oc
), 0, 0 );
1651 /* check for abandon */
1652 if ( op
->o_abandon
) {
1653 bsi
->bsi_status
= SLAPD_ABANDON
;
1654 return BACKSQL_AVL_STOP
;
1657 /* If paged results have already completed this objectClass, skip it */
1658 if ( get_pagedresults(op
) > SLAP_CONTROL_IGNORED
) {
1659 if ( oc
->bom_id
< PAGECOOKIE_TO_SQL_OC( ((PagedResultsState
*)op
->o_pagedresults_state
)->ps_cookie
) )
1661 return BACKSQL_AVL_CONTINUE
;
1665 if ( bsi
->bsi_n_candidates
== -1 ) {
1666 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1667 "unchecked limit has been overcome\n", 0, 0, 0 );
1668 /* should never get here */
1670 bsi
->bsi_status
= LDAP_ADMINLIMIT_EXCEEDED
;
1671 return BACKSQL_AVL_STOP
;
1675 res
= backsql_srch_query( bsi
, &query
);
1677 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1678 "error while constructing query for objectclass \"%s\"\n",
1679 oc
->bom_oc
->soc_cname
.bv_val
, 0, 0 );
1681 * FIXME: need to separate errors from legally
1682 * impossible filters
1684 switch ( bsi
->bsi_status
) {
1686 case LDAP_UNDEFINED_TYPE
:
1687 case LDAP_NO_SUCH_OBJECT
:
1688 /* we are conservative... */
1690 bsi
->bsi_status
= LDAP_SUCCESS
;
1692 return BACKSQL_AVL_CONTINUE
;
1694 case LDAP_ADMINLIMIT_EXCEEDED
:
1696 /* don't try any more */
1697 return BACKSQL_AVL_STOP
;
1701 if ( BER_BVISNULL( &query
) ) {
1702 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1703 "could not construct query for objectclass \"%s\"\n",
1704 oc
->bom_oc
->soc_cname
.bv_val
, 0, 0 );
1705 bsi
->bsi_status
= LDAP_SUCCESS
;
1706 return BACKSQL_AVL_CONTINUE
;
1709 Debug( LDAP_DEBUG_TRACE
, "Constructed query: %s\n",
1710 query
.bv_val
, 0, 0 );
1712 rc
= backsql_Prepare( bsi
->bsi_dbh
, &sth
, query
.bv_val
, 0 );
1713 bsi
->bsi_op
->o_tmpfree( query
.bv_val
, bsi
->bsi_op
->o_tmpmemctx
);
1714 BER_BVZERO( &query
);
1715 if ( rc
!= SQL_SUCCESS
) {
1716 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1717 "error preparing query\n", 0, 0, 0 );
1718 backsql_PrintErrors( bi
->sql_db_env
, bsi
->bsi_dbh
, sth
, rc
);
1719 bsi
->bsi_status
= LDAP_OTHER
;
1720 return BACKSQL_AVL_CONTINUE
;
1723 Debug( LDAP_DEBUG_TRACE
, "id: '%ld'\n", bsi
->bsi_oc
->bom_id
, 0, 0 );
1725 rc
= backsql_BindParamInt( sth
, 1, SQL_PARAM_INPUT
,
1726 &bsi
->bsi_oc
->bom_id
);
1727 if ( rc
!= SQL_SUCCESS
) {
1728 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1729 "error binding objectclass id parameter\n", 0, 0, 0 );
1730 bsi
->bsi_status
= LDAP_OTHER
;
1731 return BACKSQL_AVL_CONTINUE
;
1734 switch ( bsi
->bsi_scope
) {
1735 case LDAP_SCOPE_BASE
:
1736 case BACKSQL_SCOPE_BASE_LIKE
:
1738 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1739 * however this should be handled earlier
1741 if ( bsi
->bsi_base_ndn
->bv_len
> BACKSQL_MAX_DN_LEN
) {
1742 bsi
->bsi_status
= LDAP_OTHER
;
1743 return BACKSQL_AVL_CONTINUE
;
1746 AC_MEMCPY( tmp_base_ndn
, bsi
->bsi_base_ndn
->bv_val
,
1747 bsi
->bsi_base_ndn
->bv_len
+ 1 );
1749 /* uppercase DN only if the stored DN can be uppercased
1751 if ( BACKSQL_CANUPPERCASE( bi
) ) {
1752 ldap_pvt_str2upper( tmp_base_ndn
);
1755 Debug( LDAP_DEBUG_TRACE
, "(base)dn: \"%s\"\n",
1756 tmp_base_ndn
, 0, 0 );
1758 rc
= backsql_BindParamStr( sth
, 2, SQL_PARAM_INPUT
,
1759 tmp_base_ndn
, BACKSQL_MAX_DN_LEN
);
1760 if ( rc
!= SQL_SUCCESS
) {
1761 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1762 "error binding base_ndn parameter\n", 0, 0, 0 );
1763 backsql_PrintErrors( bi
->sql_db_env
, bsi
->bsi_dbh
,
1765 bsi
->bsi_status
= LDAP_OTHER
;
1766 return BACKSQL_AVL_CONTINUE
;
1770 case LDAP_SCOPE_SUBORDINATE
:
1771 case LDAP_SCOPE_SUBTREE
:
1773 /* if short-cutting the search base,
1774 * don't bind any parameter */
1775 if ( bsi
->bsi_use_subtree_shortcut
) {
1780 * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1781 * however this should be handled earlier
1783 if ( bsi
->bsi_base_ndn
->bv_len
> BACKSQL_MAX_DN_LEN
) {
1784 bsi
->bsi_status
= LDAP_OTHER
;
1785 return BACKSQL_AVL_CONTINUE
;
1789 * Sets the parameters for the SQL built earlier
1790 * NOTE that all the databases could actually use
1791 * the TimesTen version, which would be cleaner
1792 * and would also eliminate the need for the
1793 * subtree_cond line in the configuration file.
1794 * For now, I'm leaving it the way it is,
1795 * so non-TimesTen databases use the original code.
1796 * But at some point this should get cleaned up.
1798 * If "dn" is being used, do a suffix search.
1799 * If "dn_ru" is being used, do a prefix search.
1801 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi
) ) {
1802 tmp_base_ndn
[ 0 ] = '\0';
1804 for ( i
= 0, j
= bsi
->bsi_base_ndn
->bv_len
- 1;
1806 tmp_base_ndn
[ i
] = bsi
->bsi_base_ndn
->bv_val
[ j
];
1809 if ( bsi
->bsi_scope
== LDAP_SCOPE_SUBORDINATE
) {
1810 tmp_base_ndn
[ i
++ ] = ',';
1813 tmp_base_ndn
[ i
] = '%';
1814 tmp_base_ndn
[ i
+ 1 ] = '\0';
1819 tmp_base_ndn
[ i
++ ] = '%';
1821 if ( bsi
->bsi_scope
== LDAP_SCOPE_SUBORDINATE
) {
1822 tmp_base_ndn
[ i
++ ] = ',';
1825 AC_MEMCPY( &tmp_base_ndn
[ i
], bsi
->bsi_base_ndn
->bv_val
,
1826 bsi
->bsi_base_ndn
->bv_len
+ 1 );
1829 /* uppercase DN only if the stored DN can be uppercased
1831 if ( BACKSQL_CANUPPERCASE( bi
) ) {
1832 ldap_pvt_str2upper( tmp_base_ndn
);
1835 if ( bsi
->bsi_scope
== LDAP_SCOPE_SUBORDINATE
) {
1836 Debug( LDAP_DEBUG_TRACE
, "(children)dn: \"%s\"\n",
1837 tmp_base_ndn
, 0, 0 );
1839 Debug( LDAP_DEBUG_TRACE
, "(sub)dn: \"%s\"\n",
1840 tmp_base_ndn
, 0, 0 );
1843 rc
= backsql_BindParamStr( sth
, 2, SQL_PARAM_INPUT
,
1844 tmp_base_ndn
, BACKSQL_MAX_DN_LEN
);
1845 if ( rc
!= SQL_SUCCESS
) {
1846 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1847 "error binding base_ndn parameter (2)\n",
1849 backsql_PrintErrors( bi
->sql_db_env
, bsi
->bsi_dbh
,
1851 bsi
->bsi_status
= LDAP_OTHER
;
1852 return BACKSQL_AVL_CONTINUE
;
1857 case LDAP_SCOPE_ONELEVEL
:
1858 assert( !BER_BVISNULL( &bsi
->bsi_base_id
.eid_ndn
) );
1860 #ifdef BACKSQL_ARBITRARY_KEY
1861 Debug( LDAP_DEBUG_TRACE
, "(one)id: \"%s\"\n",
1862 bsi
->bsi_base_id
.eid_id
.bv_val
, 0, 0 );
1863 #else /* ! BACKSQL_ARBITRARY_KEY */
1864 Debug( LDAP_DEBUG_TRACE
, "(one)id: '%lu'\n",
1865 bsi
->bsi_base_id
.eid_id
, 0, 0 );
1866 #endif /* ! BACKSQL_ARBITRARY_KEY */
1867 rc
= backsql_BindParamID( sth
, 2, SQL_PARAM_INPUT
,
1868 &bsi
->bsi_base_id
.eid_id
);
1869 if ( rc
!= SQL_SUCCESS
) {
1870 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1871 "error binding base id parameter\n", 0, 0, 0 );
1872 bsi
->bsi_status
= LDAP_OTHER
;
1873 return BACKSQL_AVL_CONTINUE
;
1878 rc
= SQLExecute( sth
);
1879 if ( !BACKSQL_SUCCESS( rc
) ) {
1880 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1881 "error executing query\n", 0, 0, 0 );
1882 backsql_PrintErrors( bi
->sql_db_env
, bsi
->bsi_dbh
, sth
, rc
);
1883 SQLFreeStmt( sth
, SQL_DROP
);
1884 bsi
->bsi_status
= LDAP_OTHER
;
1885 return BACKSQL_AVL_CONTINUE
;
1888 backsql_BindRowAsStrings_x( sth
, &row
, bsi
->bsi_op
->o_tmpmemctx
);
1889 rc
= SQLFetch( sth
);
1890 for ( ; BACKSQL_SUCCESS( rc
); rc
= SQLFetch( sth
) ) {
1891 struct berval dn
, pdn
, ndn
;
1892 backsql_entryID
*c_id
= NULL
;
1895 ber_str2bv( row
.cols
[ 3 ], 0, 0, &dn
);
1897 if ( backsql_api_odbc2dn( bsi
->bsi_op
, bsi
->bsi_rs
, &dn
) ) {
1901 ret
= dnPrettyNormal( NULL
, &dn
, &pdn
, &ndn
, op
->o_tmpmemctx
);
1902 if ( dn
.bv_val
!= row
.cols
[ 3 ] ) {
1906 if ( ret
!= LDAP_SUCCESS
) {
1910 if ( bi
->sql_baseObject
&& dn_match( &ndn
, &bi
->sql_baseObject
->e_nname
) ) {
1914 c_id
= (backsql_entryID
*)op
->o_tmpcalloc( 1,
1915 sizeof( backsql_entryID
), op
->o_tmpmemctx
);
1916 #ifdef BACKSQL_ARBITRARY_KEY
1917 ber_str2bv_x( row
.cols
[ 0 ], 0, 1, &c_id
->eid_id
,
1919 ber_str2bv_x( row
.cols
[ 1 ], 0, 1, &c_id
->eid_keyval
,
1921 #else /* ! BACKSQL_ARBITRARY_KEY */
1922 if ( lutil_atoulx( &c_id
->eid_id
, row
.cols
[ 0 ], 0 ) != 0 ) {
1925 if ( lutil_atoulx( &c_id
->eid_keyval
, row
.cols
[ 1 ], 0 ) != 0 ) {
1928 #endif /* ! BACKSQL_ARBITRARY_KEY */
1929 c_id
->eid_oc
= bsi
->bsi_oc
;
1930 c_id
->eid_oc_id
= bsi
->bsi_oc
->bom_id
;
1933 c_id
->eid_ndn
= ndn
;
1935 /* append at end of list ... */
1936 c_id
->eid_next
= NULL
;
1937 *bsi
->bsi_id_listtail
= c_id
;
1938 bsi
->bsi_id_listtail
= &c_id
->eid_next
;
1940 #ifdef BACKSQL_ARBITRARY_KEY
1941 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1942 "added entry id=%s, keyval=%s dn=\"%s\"\n",
1943 c_id
->eid_id
.bv_val
, c_id
->eid_keyval
.bv_val
,
1945 #else /* ! BACKSQL_ARBITRARY_KEY */
1946 Debug( LDAP_DEBUG_TRACE
, "backsql_oc_get_candidates(): "
1947 "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1948 c_id
->eid_id
, c_id
->eid_keyval
, row
.cols
[ 3 ] );
1949 #endif /* ! BACKSQL_ARBITRARY_KEY */
1951 /* count candidates, for unchecked limit */
1952 bsi
->bsi_n_candidates
--;
1953 if ( bsi
->bsi_n_candidates
== -1 ) {
1959 if ( !BER_BVISNULL( &pdn
) ) {
1960 op
->o_tmpfree( pdn
.bv_val
, op
->o_tmpmemctx
);
1962 if ( !BER_BVISNULL( &ndn
) ) {
1963 op
->o_tmpfree( ndn
.bv_val
, op
->o_tmpmemctx
);
1965 if ( c_id
!= NULL
) {
1969 backsql_FreeRow_x( &row
, bsi
->bsi_op
->o_tmpmemctx
);
1970 SQLFreeStmt( sth
, SQL_DROP
);
1972 Debug( LDAP_DEBUG_TRACE
, "<==backsql_oc_get_candidates(): %d\n",
1973 n_candidates
- bsi
->bsi_n_candidates
, 0, 0 );
1975 return ( bsi
->bsi_n_candidates
== -1 ? BACKSQL_AVL_STOP
: BACKSQL_AVL_CONTINUE
);
1979 backsql_search( Operation
*op
, SlapReply
*rs
)
1981 backsql_info
*bi
= (backsql_info
*)op
->o_bd
->be_private
;
1982 SQLHDBC dbh
= SQL_NULL_HDBC
;
1984 Entry user_entry
= { 0 },
1986 int manageDSAit
= get_manageDSAit( op
);
1987 time_t stoptime
= 0;
1988 backsql_srch_info bsi
= { 0 };
1989 backsql_entryID
*eid
= NULL
;
1990 struct berval nbase
= BER_BVNULL
;
1991 unsigned long lastid
= 0;
1993 Debug( LDAP_DEBUG_TRACE
, "==>backsql_search(): "
1994 "base=\"%s\", filter=\"%s\", scope=%d,",
1995 op
->o_req_ndn
.bv_val
,
1996 op
->ors_filterstr
.bv_val
,
1998 Debug( LDAP_DEBUG_TRACE
, " deref=%d, attrsonly=%d, "
1999 "attributes to load: %s\n",
2002 op
->ors_attrs
== NULL
? "all" : "custom list" );
2004 if ( op
->o_req_ndn
.bv_len
> BACKSQL_MAX_DN_LEN
) {
2005 Debug( LDAP_DEBUG_TRACE
, "backsql_search(): "
2006 "search base length (%ld) exceeds max length (%d)\n",
2007 op
->o_req_ndn
.bv_len
, BACKSQL_MAX_DN_LEN
, 0 );
2009 * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
2010 * since it is impossible that such a long DN exists
2013 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
2014 send_ldap_result( op
, rs
);
2018 sres
= backsql_get_db_conn( op
, &dbh
);
2019 if ( sres
!= LDAP_SUCCESS
) {
2020 Debug( LDAP_DEBUG_TRACE
, "backsql_search(): "
2021 "could not get connection handle - exiting\n",
2024 rs
->sr_text
= sres
== LDAP_OTHER
? "SQL-backend error" : NULL
;
2025 send_ldap_result( op
, rs
);
2029 /* compute it anyway; root does not use it */
2030 stoptime
= op
->o_time
+ op
->ors_tlimit
;
2033 bsi
.bsi_e
= &base_entry
;
2034 rs
->sr_err
= backsql_init_search( &bsi
, &op
->o_req_ndn
,
2036 stoptime
, op
->ors_filter
,
2037 dbh
, op
, rs
, op
->ors_attrs
,
2038 ( BACKSQL_ISF_MATCHED
| BACKSQL_ISF_GET_ENTRY
) );
2039 switch ( rs
->sr_err
) {
2044 if ( manageDSAit
&& !BER_BVISNULL( &bsi
.bsi_e
->e_nname
) &&
2045 dn_match( &op
->o_req_ndn
, &bsi
.bsi_e
->e_nname
) )
2047 rs
->sr_err
= LDAP_SUCCESS
;
2049 rs
->sr_matched
= NULL
;
2051 ber_bvarray_free( rs
->sr_ref
);
2057 /* an entry was created; free it */
2058 entry_clean( bsi
.bsi_e
);
2063 if ( !BER_BVISNULL( &base_entry
.e_nname
)
2064 && !access_allowed( op
, &base_entry
,
2065 slap_schema
.si_ad_entry
, NULL
,
2066 ACL_DISCLOSE
, NULL
) )
2068 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
2070 ber_bvarray_free( rs
->sr_ref
);
2073 rs
->sr_matched
= NULL
;
2077 send_ldap_result( op
, rs
);
2080 ber_bvarray_free( rs
->sr_ref
);
2084 if ( !BER_BVISNULL( &base_entry
.e_nname
) ) {
2085 entry_clean( &base_entry
);
2090 /* NOTE: __NEW__ "search" access is required
2091 * on searchBase object */
2095 if ( get_assert( op
) &&
2096 ( test_filter( op
, &base_entry
, get_assertion( op
) )
2097 != LDAP_COMPARE_TRUE
) )
2099 rs
->sr_err
= LDAP_ASSERTION_FAILED
;
2102 if ( ! access_allowed_mask( op
, &base_entry
,
2103 slap_schema
.si_ad_entry
,
2104 NULL
, ACL_SEARCH
, NULL
, &mask
) )
2106 if ( rs
->sr_err
== LDAP_SUCCESS
) {
2107 rs
->sr_err
= LDAP_INSUFFICIENT_ACCESS
;
2111 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
2112 if ( !ACL_GRANT( mask
, ACL_DISCLOSE
) ) {
2113 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
2116 send_ldap_result( op
, rs
);
2123 bsi
.bsi_n_candidates
=
2124 ( op
->ors_limit
== NULL
/* isroot == TRUE */ ? -2 :
2125 ( op
->ors_limit
->lms_s_unchecked
== -1 ? -2 :
2126 ( op
->ors_limit
->lms_s_unchecked
) ) );
2128 /* If paged results are in effect, check the paging cookie */
2129 if ( get_pagedresults( op
) > SLAP_CONTROL_IGNORED
) {
2130 rs
->sr_err
= parse_paged_cookie( op
, rs
);
2131 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
2132 send_ldap_result( op
, rs
);
2137 switch ( bsi
.bsi_scope
) {
2138 case LDAP_SCOPE_BASE
:
2139 case BACKSQL_SCOPE_BASE_LIKE
:
2141 * probably already found...
2143 bsi
.bsi_id_list
= &bsi
.bsi_base_id
;
2144 bsi
.bsi_id_listtail
= &bsi
.bsi_base_id
.eid_next
;
2147 case LDAP_SCOPE_SUBTREE
:
2149 * if baseObject is defined, and if it is the root
2150 * of the search, add it to the candidate list
2152 if ( bi
->sql_baseObject
&& BACKSQL_IS_BASEOBJECT_ID( &bsi
.bsi_base_id
.eid_id
) )
2154 bsi
.bsi_id_list
= &bsi
.bsi_base_id
;
2155 bsi
.bsi_id_listtail
= &bsi
.bsi_base_id
.eid_next
;
2162 * for each objectclass we try to construct query which gets IDs
2163 * of entries matching LDAP query filter and scope (or at least
2164 * candidates), and get the IDs. Do this in ID order for paging.
2166 avl_apply( bi
->sql_oc_by_id
, backsql_oc_get_candidates
,
2167 &bsi
, BACKSQL_AVL_STOP
, AVL_INORDER
);
2169 /* check for abandon */
2170 if ( op
->o_abandon
) {
2171 eid
= bsi
.bsi_id_list
;
2172 rs
->sr_err
= SLAPD_ABANDON
;
2177 if ( op
->ors_limit
!= NULL
/* isroot == FALSE */
2178 && op
->ors_limit
->lms_s_unchecked
!= -1
2179 && bsi
.bsi_n_candidates
== -1 )
2181 rs
->sr_err
= LDAP_ADMINLIMIT_EXCEEDED
;
2182 send_ldap_result( op
, rs
);
2187 * now we load candidate entries (only those attributes
2188 * mentioned in attrs and filter), test it against full filter
2189 * and then send to client; don't free entry_id if baseObject...
2191 for ( eid
= bsi
.bsi_id_list
;
2193 eid
= backsql_free_entryID(
2194 eid
, eid
== &bsi
.bsi_base_id
? 0 : 1, op
->o_tmpmemctx
) )
2197 Attribute
*a_hasSubordinate
= NULL
,
2198 *a_entryUUID
= NULL
,
2203 /* check for abandon */
2204 if ( op
->o_abandon
) {
2205 rs
->sr_err
= SLAPD_ABANDON
;
2209 /* check time limit */
2210 if ( op
->ors_tlimit
!= SLAP_NO_LIMIT
2211 && slap_get_time() > stoptime
)
2213 rs
->sr_err
= LDAP_TIMELIMIT_EXCEEDED
;
2214 rs
->sr_ctrls
= NULL
;
2215 rs
->sr_ref
= rs
->sr_v2ref
;
2219 #ifdef BACKSQL_ARBITRARY_KEY
2220 Debug(LDAP_DEBUG_TRACE
, "backsql_search(): loading data "
2221 "for entry id=%s, oc_id=%ld, keyval=%s\n",
2222 eid
->eid_id
.bv_val
, eid
->eid_oc_id
,
2223 eid
->eid_keyval
.bv_val
);
2224 #else /* ! BACKSQL_ARBITRARY_KEY */
2225 Debug(LDAP_DEBUG_TRACE
, "backsql_search(): loading data "
2226 "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2227 eid
->eid_id
, eid
->eid_oc_id
, eid
->eid_keyval
);
2228 #endif /* ! BACKSQL_ARBITRARY_KEY */
2231 switch ( op
->ors_scope
) {
2232 case LDAP_SCOPE_BASE
:
2233 case BACKSQL_SCOPE_BASE_LIKE
:
2234 if ( !dn_match( &eid
->eid_ndn
, &op
->o_req_ndn
) ) {
2239 case LDAP_SCOPE_ONE
:
2241 struct berval rdn
= eid
->eid_ndn
;
2243 rdn
.bv_len
-= op
->o_req_ndn
.bv_len
+ STRLENOF( "," );
2244 if ( !dnIsOneLevelRDN( &rdn
) ) {
2250 case LDAP_SCOPE_SUBORDINATE
:
2251 /* discard the baseObject entry */
2252 if ( dn_match( &eid
->eid_ndn
, &op
->o_req_ndn
) ) {
2256 case LDAP_SCOPE_SUBTREE
:
2257 /* FIXME: this should never fail... */
2258 if ( !dnIsSuffix( &eid
->eid_ndn
, &op
->o_req_ndn
) ) {
2264 if ( BACKSQL_IS_BASEOBJECT_ID( &eid
->eid_id
) ) {
2265 /* don't recollect baseObject... */
2266 e
= bi
->sql_baseObject
;
2268 } else if ( eid
== &bsi
.bsi_base_id
) {
2269 /* don't recollect searchBase object... */
2273 bsi
.bsi_e
= &user_entry
;
2274 rc
= backsql_id2entry( &bsi
, eid
);
2275 if ( rc
!= LDAP_SUCCESS
) {
2276 Debug( LDAP_DEBUG_TRACE
, "backsql_search(): "
2277 "error %d in backsql_id2entry() "
2278 "- skipping\n", rc
, 0, 0 );
2284 if ( !manageDSAit
&&
2285 op
->ors_scope
!= LDAP_SCOPE_BASE
&&
2286 op
->ors_scope
!= BACKSQL_SCOPE_BASE_LIKE
&&
2287 is_entry_referral( e
) )
2291 refs
= get_entry_referrals( op
, e
);
2293 backsql_srch_info bsi2
= { 0 };
2294 Entry user_entry2
= { 0 };
2296 /* retry with the full entry... */
2297 bsi2
.bsi_e
= &user_entry2
;
2298 rc
= backsql_init_search( &bsi2
,
2303 BACKSQL_ISF_GET_ENTRY
);
2304 if ( rc
== LDAP_SUCCESS
) {
2305 if ( is_entry_referral( &user_entry2
) )
2307 refs
= get_entry_referrals( op
,
2310 rs
->sr_err
= LDAP_OTHER
;
2312 backsql_entry_clean( op
, &user_entry2
);
2314 if ( bsi2
.bsi_attrs
!= NULL
) {
2315 op
->o_tmpfree( bsi2
.bsi_attrs
,
2321 rs
->sr_ref
= referral_rewrite( refs
,
2325 ber_bvarray_free( refs
);
2329 rs
->sr_err
= LDAP_REFERRAL
;
2332 rs
->sr_text
= "bad referral object";
2336 rs
->sr_matched
= user_entry
.e_name
.bv_val
;
2337 send_search_reference( op
, rs
);
2339 ber_bvarray_free( rs
->sr_ref
);
2341 rs
->sr_matched
= NULL
;
2342 rs
->sr_entry
= NULL
;
2343 if ( rs
->sr_err
== LDAP_REFERRAL
) {
2344 rs
->sr_err
= LDAP_SUCCESS
;
2351 * We use this flag since we need to parse the filter
2352 * anyway; we should have used the frontend API function
2353 * filter_has_subordinates()
2355 if ( bsi
.bsi_flags
& BSQL_SF_FILTER_HASSUBORDINATE
) {
2356 rc
= backsql_has_children( op
, dbh
, &e
->e_nname
);
2359 case LDAP_COMPARE_TRUE
:
2360 case LDAP_COMPARE_FALSE
:
2361 a_hasSubordinate
= slap_operational_hasSubordinate( rc
== LDAP_COMPARE_TRUE
);
2362 if ( a_hasSubordinate
!= NULL
) {
2363 for ( ap
= &user_entry
.e_attrs
;
2365 ap
= &(*ap
)->a_next
);
2367 *ap
= a_hasSubordinate
;
2373 Debug(LDAP_DEBUG_TRACE
,
2374 "backsql_search(): "
2375 "has_children failed( %d)\n",
2382 if ( bsi
.bsi_flags
& BSQL_SF_FILTER_ENTRYUUID
) {
2383 a_entryUUID
= backsql_operational_entryUUID( bi
, eid
);
2384 if ( a_entryUUID
!= NULL
) {
2386 ap
= &user_entry
.e_attrs
;
2389 for ( ; *ap
; ap
= &(*ap
)->a_next
);
2395 #ifdef BACKSQL_SYNCPROV
2396 if ( bsi
.bsi_flags
& BSQL_SF_FILTER_ENTRYCSN
) {
2397 a_entryCSN
= backsql_operational_entryCSN( op
);
2398 if ( a_entryCSN
!= NULL
) {
2400 ap
= &user_entry
.e_attrs
;
2403 for ( ; *ap
; ap
= &(*ap
)->a_next
);
2408 #endif /* BACKSQL_SYNCPROV */
2410 if ( test_filter( op
, e
, op
->ors_filter
) == LDAP_COMPARE_TRUE
)
2412 /* If paged results are in effect, see if the page limit was exceeded */
2413 if ( get_pagedresults(op
) > SLAP_CONTROL_IGNORED
) {
2414 if ( rs
->sr_nentries
>= ((PagedResultsState
*)op
->o_pagedresults_state
)->ps_size
)
2417 send_paged_response( op
, rs
, &lastid
);
2420 lastid
= SQL_TO_PAGECOOKIE( eid
->eid_id
, eid
->eid_oc_id
);
2422 rs
->sr_attrs
= op
->ors_attrs
;
2423 rs
->sr_operational_attrs
= NULL
;
2425 e
->e_private
= (void *)eid
;
2426 rs
->sr_flags
= ( e
== &user_entry
) ? REP_ENTRY_MODIFIABLE
: 0;
2427 /* FIXME: need the whole entry (ITS#3480) */
2428 rs
->sr_err
= send_search_entry( op
, rs
);
2429 e
->e_private
= NULL
;
2430 rs
->sr_entry
= NULL
;
2431 rs
->sr_attrs
= NULL
;
2432 rs
->sr_operational_attrs
= NULL
;
2434 switch ( rs
->sr_err
) {
2435 case LDAP_UNAVAILABLE
:
2437 * FIXME: send_search_entry failed;
2440 Debug( LDAP_DEBUG_TRACE
, "backsql_search(): "
2441 "connection lost\n", 0, 0, 0 );
2444 case LDAP_SIZELIMIT_EXCEEDED
:
2450 if ( e
== &user_entry
) {
2451 backsql_entry_clean( op
, &user_entry
);
2458 if ( rs
->sr_nentries
> 0 ) {
2459 rs
->sr_ref
= rs
->sr_v2ref
;
2460 rs
->sr_err
= (rs
->sr_v2ref
== NULL
) ? LDAP_SUCCESS
2464 rs
->sr_err
= bsi
.bsi_status
;
2468 if ( rs
->sr_err
!= SLAPD_ABANDON
) {
2469 if ( get_pagedresults(op
) > SLAP_CONTROL_IGNORED
) {
2470 send_paged_response( op
, rs
, NULL
);
2472 send_ldap_result( op
, rs
);
2476 /* cleanup in case of abandon */
2477 for ( ; eid
!= NULL
;
2478 eid
= backsql_free_entryID(
2479 eid
, eid
== &bsi
.bsi_base_id
? 0 : 1, op
->o_tmpmemctx
) )
2482 backsql_entry_clean( op
, &base_entry
);
2484 /* in case we got here accidentally */
2485 backsql_entry_clean( op
, &user_entry
);
2487 if ( rs
->sr_v2ref
) {
2488 ber_bvarray_free( rs
->sr_v2ref
);
2489 rs
->sr_v2ref
= NULL
;
2492 #ifdef BACKSQL_SYNCPROV
2494 Operation op2
= *op
;
2495 SlapReply rs2
= { 0 };
2496 Entry
*e
= entry_alloc();
2497 slap_callback cb
= { 0 };
2499 op2
.o_tag
= LDAP_REQ_ADD
;
2500 op2
.o_bd
= select_backend( &op
->o_bd
->be_nsuffix
[0], 0 );
2502 op2
.o_callback
= &cb
;
2504 ber_dupbv( &e
->e_name
, op
->o_bd
->be_suffix
);
2505 ber_dupbv( &e
->e_nname
, op
->o_bd
->be_nsuffix
);
2507 cb
.sc_response
= slap_null_cb
;
2509 op2
.o_bd
->be_add( &op2
, &rs2
);
2511 if ( op2
.ora_e
== e
)
2514 #endif /* BACKSQL_SYNCPROV */
2517 (void)backsql_free_entryID( &bsi
.bsi_base_id
, 0, op
->o_tmpmemctx
);
2519 if ( bsi
.bsi_attrs
!= NULL
) {
2520 op
->o_tmpfree( bsi
.bsi_attrs
, op
->o_tmpmemctx
);
2523 if ( !BER_BVISNULL( &nbase
)
2524 && nbase
.bv_val
!= op
->o_req_ndn
.bv_val
)
2526 ch_free( nbase
.bv_val
);
2529 /* restore scope ... FIXME: this should be done before ANY
2530 * frontend call that uses op */
2531 if ( op
->ors_scope
== BACKSQL_SCOPE_BASE_LIKE
) {
2532 op
->ors_scope
= LDAP_SCOPE_BASE
;
2535 Debug( LDAP_DEBUG_TRACE
, "<==backsql_search()\n", 0, 0, 0 );
2540 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2547 AttributeDescription
*at
,
2551 backsql_srch_info bsi
= { 0 };
2552 SQLHDBC dbh
= SQL_NULL_HDBC
;
2554 SlapReply rs
= { 0 };
2555 AttributeName anlist
[ 2 ];
2559 rc
= backsql_get_db_conn( op
, &dbh
);
2560 if ( rc
!= LDAP_SUCCESS
) {
2565 anlist
[ 0 ].an_name
= at
->ad_cname
;
2566 anlist
[ 0 ].an_desc
= at
;
2567 BER_BVZERO( &anlist
[ 1 ].an_name
);
2570 bsi
.bsi_e
= entry_alloc();
2571 rc
= backsql_init_search( &bsi
,
2575 dbh
, op
, &rs
, at
? anlist
: NULL
,
2576 BACKSQL_ISF_GET_ENTRY
);
2578 if ( !BER_BVISNULL( &bsi
.bsi_base_id
.eid_ndn
) ) {
2579 (void)backsql_free_entryID( &bsi
.bsi_base_id
, 0, op
->o_tmpmemctx
);
2582 if ( rc
== LDAP_SUCCESS
) {
2584 #if 0 /* not supported at present */
2585 /* find attribute values */
2586 if ( is_entry_alias( bsi
.bsi_e
) ) {
2587 Debug( LDAP_DEBUG_ACL
,
2588 "<= backsql_entry_get: entry is an alias\n",
2590 rc
= LDAP_ALIAS_PROBLEM
;
2591 goto return_results
;
2595 if ( is_entry_referral( bsi
.bsi_e
) ) {
2596 Debug( LDAP_DEBUG_ACL
,
2597 "<= backsql_entry_get: entry is a referral\n",
2600 goto return_results
;
2603 if ( oc
&& !is_entry_objectclass( bsi
.bsi_e
, oc
, 0 ) ) {
2604 Debug( LDAP_DEBUG_ACL
,
2605 "<= backsql_entry_get: "
2606 "failed to find objectClass\n",
2608 rc
= LDAP_NO_SUCH_ATTRIBUTE
;
2609 goto return_results
;
2616 if ( bsi
.bsi_attrs
!= NULL
) {
2617 op
->o_tmpfree( bsi
.bsi_attrs
, op
->o_tmpmemctx
);
2620 if ( rc
!= LDAP_SUCCESS
) {
2622 entry_free( bsi
.bsi_e
);
2630 backsql_entry_clean(
2636 ctx
= ldap_pvt_thread_pool_context();
2638 if ( ctx
== NULL
|| ctx
!= op
->o_tmpmemctx
) {
2639 if ( !BER_BVISNULL( &e
->e_name
) ) {
2640 op
->o_tmpfree( e
->e_name
.bv_val
, op
->o_tmpmemctx
);
2641 BER_BVZERO( &e
->e_name
);
2644 if ( !BER_BVISNULL( &e
->e_nname
) ) {
2645 op
->o_tmpfree( e
->e_nname
.bv_val
, op
->o_tmpmemctx
);
2646 BER_BVZERO( &e
->e_nname
);
2654 backsql_entry_release(
2659 backsql_entry_clean( op
, e
);
2667 /* This function is copied verbatim from back-bdb/search.c */
2669 parse_paged_cookie( Operation
*op
, SlapReply
*rs
)
2671 int rc
= LDAP_SUCCESS
;
2672 PagedResultsState
*ps
= op
->o_pagedresults_state
;
2674 /* this function must be invoked only if the pagedResults
2675 * control has been detected, parsed and partially checked
2676 * by the frontend */
2677 assert( get_pagedresults( op
) > SLAP_CONTROL_IGNORED
);
2679 /* cookie decoding/checks deferred to backend... */
2680 if ( ps
->ps_cookieval
.bv_len
) {
2681 PagedResultsCookie reqcookie
;
2682 if( ps
->ps_cookieval
.bv_len
!= sizeof( reqcookie
) ) {
2684 rs
->sr_text
= "paged results cookie is invalid";
2685 rc
= LDAP_PROTOCOL_ERROR
;
2689 AC_MEMCPY( &reqcookie
, ps
->ps_cookieval
.bv_val
, sizeof( reqcookie
));
2691 if ( reqcookie
> ps
->ps_cookie
) {
2693 rs
->sr_text
= "paged results cookie is invalid";
2694 rc
= LDAP_PROTOCOL_ERROR
;
2697 } else if ( reqcookie
< ps
->ps_cookie
) {
2698 rs
->sr_text
= "paged results cookie is invalid or old";
2699 rc
= LDAP_UNWILLING_TO_PERFORM
;
2704 /* Initial request. Initialize state. */
2714 /* This function is copied nearly verbatim from back-bdb/search.c */
2716 send_paged_response(
2719 unsigned long *lastid
)
2721 LDAPControl ctrl
, *ctrls
[2];
2722 BerElementBuffer berbuf
;
2723 BerElement
*ber
= (BerElement
*)&berbuf
;
2724 PagedResultsCookie respcookie
;
2725 struct berval cookie
;
2727 Debug(LDAP_DEBUG_ARGS
,
2728 "send_paged_response: lastid=0x%08lx nentries=%d\n",
2729 lastid
? *lastid
: 0, rs
->sr_nentries
, NULL
);
2731 BER_BVZERO( &ctrl
.ldctl_value
);
2735 ber_init2( ber
, NULL
, LBER_USE_DER
);
2738 respcookie
= ( PagedResultsCookie
)(*lastid
);
2739 cookie
.bv_len
= sizeof( respcookie
);
2740 cookie
.bv_val
= (char *)&respcookie
;
2743 respcookie
= ( PagedResultsCookie
)0;
2744 BER_BVSTR( &cookie
, "" );
2747 op
->o_conn
->c_pagedresults_state
.ps_cookie
= respcookie
;
2748 op
->o_conn
->c_pagedresults_state
.ps_count
=
2749 ((PagedResultsState
*)op
->o_pagedresults_state
)->ps_count
+
2752 /* return size of 0 -- no estimate */
2753 ber_printf( ber
, "{iO}", 0, &cookie
);
2755 if ( ber_flatten2( ber
, &ctrls
[0]->ldctl_value
, 0 ) == -1 ) {
2759 ctrls
[0]->ldctl_oid
= LDAP_CONTROL_PAGEDRESULTS
;
2760 ctrls
[0]->ldctl_iscritical
= 0;
2762 rs
->sr_ctrls
= ctrls
;
2763 rs
->sr_err
= LDAP_SUCCESS
;
2764 send_ldap_result( op
, rs
);
2765 rs
->sr_ctrls
= NULL
;
2768 (void) ber_free_buf( ber
);