]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/search.c
Additional ITS#4728 patch from HEAD
[openldap] / servers / slapd / back-sql / search.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2006 The OpenLDAP Foundation.
5  * Portions Copyright 1999 Dmitry Kovalev.
6  * Portions Copyright 2002 Pierangelo Masarati.
7  * Portions Copyright 2004 Mark Adamson.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
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>.
17  */
18 /* ACKNOWLEDGEMENTS:
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.
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include "ac/string.h"
29 #include "ac/ctype.h"
30
31 #include "lutil.h"
32 #include "slap.h"
33 #include "proto-sql.h"
34
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 );
44
45 static int
46 backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
47 {
48         int             n_attrs = 0;
49         AttributeName   *an = NULL;
50
51         if ( bsi->bsi_attrs == NULL ) {
52                 return 1;
53         }
54
55         /*
56          * clear the list (retrieve all attrs)
57          */
58         if ( ad == NULL ) {
59                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx );
60                 bsi->bsi_attrs = NULL;
61                 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
62                 return 1;
63         }
64
65         for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
66                 an = &bsi->bsi_attrs[ n_attrs ];
67                 
68                 Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
69                         "attribute \"%s\" is in list\n", 
70                         an->an_name.bv_val, 0, 0 );
71                 /*
72                  * We can live with strcmp because the attribute 
73                  * list has been normalized before calling be_search
74                  */
75                 if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
76                         return 1;
77                 }
78         }
79         
80         Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
81                 "adding \"%s\" to list\n", ad->ad_cname.bv_val, 0, 0 );
82
83         an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
84                         sizeof( AttributeName ) * ( n_attrs + 2 ),
85                         bsi->bsi_op->o_tmpmemctx );
86         if ( an == NULL ) {
87                 return -1;
88         }
89
90         an[ n_attrs ].an_name = ad->ad_cname;
91         an[ n_attrs ].an_desc = ad;
92         BER_BVZERO( &an[ n_attrs + 1 ].an_name );
93
94         bsi->bsi_attrs = an;
95         
96         return 1;
97 }
98
99 /*
100  * Initializes the search structure.
101  * 
102  * If get_base_id != 0, the field bsi_base_id is filled 
103  * with the entryID of bsi_base_ndn; it must be freed
104  * by backsql_free_entryID() when no longer required.
105  *
106  * NOTE: base must be normalized
107  */
108 int
109 backsql_init_search(
110         backsql_srch_info       *bsi, 
111         struct berval           *nbase, 
112         int                     scope, 
113         time_t                  stoptime, 
114         Filter                  *filter, 
115         SQLHDBC                 dbh,
116         Operation               *op,
117         SlapReply               *rs,
118         AttributeName           *attrs,
119         unsigned                flags )
120 {
121         backsql_info            *bi = (backsql_info *)op->o_bd->be_private;
122         int                     rc = LDAP_SUCCESS;
123
124         bsi->bsi_base_ndn = nbase;
125         bsi->bsi_use_subtree_shortcut = 0;
126         BER_BVZERO( &bsi->bsi_base_id.eid_dn );
127         BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
128         bsi->bsi_scope = scope;
129         bsi->bsi_filter = filter;
130         bsi->bsi_dbh = dbh;
131         bsi->bsi_op = op;
132         bsi->bsi_rs = rs;
133         bsi->bsi_flags = BSQL_SF_NONE;
134
135         bsi->bsi_attrs = NULL;
136
137         if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) {
138                 /*
139                  * if requested, simply try to fetch all attributes
140                  */
141                 bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
142
143         } else {
144                 if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) {
145                         bsi->bsi_flags |= BSQL_SF_ALL_USER;
146
147                 } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) {
148                         bsi->bsi_flags |= BSQL_SF_ALL_OPER;
149                 }
150
151                 if ( attrs == NULL ) {
152                         /* NULL means all user attributes */
153                         bsi->bsi_flags |= BSQL_SF_ALL_USER;
154
155                 } else {
156                         AttributeName   *p;
157                         int             got_oc = 0;
158
159                         bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc(
160                                         sizeof( AttributeName ),
161                                         bsi->bsi_op->o_tmpmemctx );
162                         BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
163         
164                         for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
165                                 if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
166                                         /* handle "*" */
167                                         bsi->bsi_flags |= BSQL_SF_ALL_USER;
168
169                                         /* if all attrs are requested, there's
170                                          * no need to continue */
171                                         if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
172                                                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
173                                                                 bsi->bsi_op->o_tmpmemctx );
174                                                 bsi->bsi_attrs = NULL;
175                                                 break;
176                                         }
177                                         continue;
178
179                                 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
180                                         /* handle "+" */
181                                         bsi->bsi_flags |= BSQL_SF_ALL_OPER;
182
183                                         /* if all attrs are requested, there's
184                                          * no need to continue */
185                                         if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
186                                                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
187                                                                 bsi->bsi_op->o_tmpmemctx );
188                                                 bsi->bsi_attrs = NULL;
189                                                 break;
190                                         }
191                                         continue;
192
193                                 } else if ( BACKSQL_NCMP( &p->an_name, &NoAttrs ) == 0 ) {
194                                         /* ignore "1.1" */
195                                         continue;
196
197                                 } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
198                                         got_oc = 1;
199                                 }
200
201                                 backsql_attrlist_add( bsi, p->an_desc );
202                         }
203
204                         if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) {
205                                 /* add objectClass if not present,
206                                  * because it is required to understand
207                                  * if an entry is a referral, an alias 
208                                  * or so... */
209                                 backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
210                         }
211                 }
212
213                 if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
214                         AttributeName   *p;
215                         
216                         /* use hints if available */
217                         for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
218                                 if ( BACKSQL_NCMP( &p->an_name, &AllUser ) == 0 ) {
219                                         /* handle "*" */
220                                         bsi->bsi_flags |= BSQL_SF_ALL_USER;
221
222                                         /* if all attrs are requested, there's
223                                          * no need to continue */
224                                         if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
225                                                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
226                                                                 bsi->bsi_op->o_tmpmemctx );
227                                                 bsi->bsi_attrs = NULL;
228                                                 break;
229                                         }
230                                         continue;
231
232                                 } else if ( BACKSQL_NCMP( &p->an_name, &AllOper ) == 0 ) {
233                                         /* handle "+" */
234                                         bsi->bsi_flags |= BSQL_SF_ALL_OPER;
235
236                                         /* if all attrs are requested, there's
237                                          * no need to continue */
238                                         if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
239                                                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
240                                                                 bsi->bsi_op->o_tmpmemctx );
241                                                 bsi->bsi_attrs = NULL;
242                                                 break;
243                                         }
244                                         continue;
245                                 }
246
247                                 backsql_attrlist_add( bsi, p->an_desc );
248                         }
249
250                 }
251         }
252
253         bsi->bsi_id_list = NULL;
254         bsi->bsi_id_listtail = &bsi->bsi_id_list;
255         bsi->bsi_n_candidates = 0;
256         bsi->bsi_stoptime = stoptime;
257         BER_BVZERO( &bsi->bsi_sel.bb_val );
258         bsi->bsi_sel.bb_len = 0;
259         BER_BVZERO( &bsi->bsi_from.bb_val );
260         bsi->bsi_from.bb_len = 0;
261         BER_BVZERO( &bsi->bsi_join_where.bb_val );
262         bsi->bsi_join_where.bb_len = 0;
263         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
264         bsi->bsi_flt_where.bb_len = 0;
265         bsi->bsi_filter_oc = NULL;
266
267         if ( BACKSQL_IS_GET_ID( flags ) ) {
268                 int     matched = BACKSQL_IS_MATCHED( flags );
269                 int     getentry = BACKSQL_IS_GET_ENTRY( flags );
270                 int     gotit = 0;
271
272                 assert( op->o_bd->be_private != NULL );
273
274                 rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
275                                 matched, 1 );
276
277                 /* the entry is collected either if requested for by getentry
278                  * or if get noSuchObject and requested to climb the tree,
279                  * so that a matchedDN or a referral can be returned */
280                 if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) {
281                         if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) {
282                                 assert( bsi->bsi_e != NULL );
283                                 
284                                 if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) )
285                                 {
286                                         gotit = 1;
287                                 }
288                         
289                                 /*
290                                  * let's see if it is a referral and, in case, get it
291                                  */
292                                 backsql_attrlist_add( bsi, slap_schema.si_ad_ref );
293                                 rc = backsql_id2entry( bsi, &bsi->bsi_base_id );
294                                 if ( rc == LDAP_SUCCESS ) {
295                                         if ( is_entry_referral( bsi->bsi_e ) )
296                                         {
297                                                 BerVarray erefs = get_entry_referrals( op, bsi->bsi_e );
298                                                 if ( erefs ) {
299                                                         rc = rs->sr_err = LDAP_REFERRAL;
300                                                         rs->sr_ref = referral_rewrite( erefs,
301                                                                         &bsi->bsi_e->e_nname,
302                                                                         &op->o_req_dn,
303                                                                         scope );
304                                                         ber_bvarray_free( erefs );
305         
306                                                 } else {
307                                                         rc = rs->sr_err = LDAP_OTHER;
308                                                         rs->sr_text = "bad referral object";
309                                                 }
310
311                                         } else if ( !gotit ) {
312                                                 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
313                                         }
314                                 }
315
316                         } else {
317                                 rs->sr_err = rc;
318                         }
319                 }
320         }
321
322         bsi->bsi_status = rc;
323
324         switch ( rc ) {
325         case LDAP_SUCCESS:
326         case LDAP_REFERRAL:
327                 break;
328
329         default:
330                 bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
331                                 bsi->bsi_op->o_tmpmemctx );
332                 break;
333         }
334
335         return rc;
336 }
337
338 static int
339 backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
340 {
341         int             res;
342
343         if ( !f ) {
344                 return 0;
345         }
346
347         backsql_strfcat_x( &bsi->bsi_flt_where,
348                         bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */  );
349
350         while ( 1 ) {
351                 res = backsql_process_filter( bsi, f );
352                 if ( res < 0 ) {
353                         /*
354                          * TimesTen : If the query has no answers,
355                          * don't bother to run the query.
356                          */
357                         return -1;
358                 }
359  
360                 f = f->f_next;
361                 if ( f == NULL ) {
362                         break;
363                 }
364
365                 switch ( op ) {
366                 case LDAP_FILTER_AND:
367                         backsql_strfcat_x( &bsi->bsi_flt_where,
368                                         bsi->bsi_op->o_tmpmemctx, "l",
369                                         (ber_len_t)STRLENOF( " AND " ), 
370                                                 " AND " );
371                         break;
372
373                 case LDAP_FILTER_OR:
374                         backsql_strfcat_x( &bsi->bsi_flt_where,
375                                         bsi->bsi_op->o_tmpmemctx, "l",
376                                         (ber_len_t)STRLENOF( " OR " ),
377                                                 " OR " );
378                         break;
379                 }
380         }
381
382         backsql_strfcat_x( &bsi->bsi_flt_where,
383                         bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
384
385         return 1;
386 }
387
388 static int
389 backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
390         backsql_at_map_rec *at )
391 {
392         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
393         int                     i;
394         int                     casefold = 0;
395
396         if ( !f ) {
397                 return 0;
398         }
399
400         /* always uppercase strings by now */
401 #ifdef BACKSQL_UPPERCASE_FILTER
402         if ( f->f_sub_desc->ad_type->sat_substr &&
403                         SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
404                                 bi->sql_caseIgnoreMatch ) )
405 #endif /* BACKSQL_UPPERCASE_FILTER */
406         {
407                 casefold = 1;
408         }
409
410         if ( f->f_sub_desc->ad_type->sat_substr &&
411                         SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
412                                 bi->sql_telephoneNumberMatch ) )
413         {
414
415                 struct berval   bv;
416                 ber_len_t       i, s, a;
417
418                 /*
419                  * to check for matching telephone numbers
420                  * with intermixed chars, e.g. val='1234'
421                  * use
422                  * 
423                  * val LIKE '%1%2%3%4%'
424                  */
425
426                 BER_BVZERO( &bv );
427                 if ( f->f_sub_initial.bv_val ) {
428                         bv.bv_len += f->f_sub_initial.bv_len;
429                 }
430                 if ( f->f_sub_any != NULL ) {
431                         for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
432                                 bv.bv_len += f->f_sub_any[ a ].bv_len;
433                         }
434                 }
435                 if ( f->f_sub_final.bv_val ) {
436                         bv.bv_len += f->f_sub_final.bv_len;
437                 }
438                 bv.bv_len = 2 * bv.bv_len - 1;
439                 bv.bv_val = ch_malloc( bv.bv_len + 1 );
440
441                 s = 0;
442                 if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
443                         bv.bv_val[ s ] = f->f_sub_initial.bv_val[ 0 ];
444                         for ( i = 1; i < f->f_sub_initial.bv_len; i++ ) {
445                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
446                                 bv.bv_val[ s + 2 * i ] = f->f_sub_initial.bv_val[ i ];
447                         }
448                         bv.bv_val[ s + 2 * i - 1 ] = '%';
449                         s += 2 * i;
450                 }
451
452                 if ( f->f_sub_any != NULL ) {
453                         for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
454                                 bv.bv_val[ s ] = f->f_sub_any[ a ].bv_val[ 0 ];
455                                 for ( i = 1; i < f->f_sub_any[ a ].bv_len; i++ ) {
456                                         bv.bv_val[ s + 2 * i - 1 ] = '%';
457                                         bv.bv_val[ s + 2 * i ] = f->f_sub_any[ a ].bv_val[ i ];
458                                 }
459                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
460                                 s += 2 * i;
461                         }
462                 }
463
464                 if ( !BER_BVISNULL( &f->f_sub_final ) ) {
465                         bv.bv_val[ s ] = f->f_sub_final.bv_val[ 0 ];
466                         for ( i = 1; i < f->f_sub_final.bv_len; i++ ) {
467                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
468                                 bv.bv_val[ s + 2 * i ] = f->f_sub_final.bv_val[ i ];
469                         }
470                                 bv.bv_val[ s + 2 * i - 1 ] = '%';
471                         s += 2 * i;
472                 }
473
474                 bv.bv_val[ s - 1 ] = '\0';
475
476                 (void)backsql_process_filter_like( bsi, at, casefold, &bv );
477                 ch_free( bv.bv_val );
478
479                 return 1;
480         }
481
482         /*
483          * When dealing with case-sensitive strings 
484          * we may omit normalization; however, normalized
485          * SQL filters are more liberal.
486          */
487
488         backsql_strfcat_x( &bsi->bsi_flt_where,
489                         bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */  );
490
491         /* TimesTen */
492         Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
493                 at->bam_ad->ad_cname.bv_val, 0, 0 );
494         Debug(LDAP_DEBUG_TRACE, "   expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
495                 at->bam_sel_expr_u.bv_val ? "' '" : "",
496                 at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
497         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
498                 /*
499                  * If a pre-upper-cased version of the column 
500                  * or a precompiled upper function exists, use it
501                  */
502                 backsql_strfcat_x( &bsi->bsi_flt_where, 
503                                 bsi->bsi_op->o_tmpmemctx,
504                                 "bl",
505                                 &at->bam_sel_expr_u,
506                                 (ber_len_t)STRLENOF( " LIKE '" ),
507                                         " LIKE '" );
508
509         } else {
510                 backsql_strfcat_x( &bsi->bsi_flt_where,
511                                 bsi->bsi_op->o_tmpmemctx,
512                                 "bl",
513                                 &at->bam_sel_expr,
514                                 (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
515         }
516  
517         if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
518                 ber_len_t       start;
519
520 #ifdef BACKSQL_TRACE
521                 Debug( LDAP_DEBUG_TRACE, 
522                         "==>backsql_process_sub_filter(%s): "
523                         "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
524                         f->f_sub_initial.bv_val, 0 );
525 #endif /* BACKSQL_TRACE */
526
527                 start = bsi->bsi_flt_where.bb_val.bv_len;
528                 backsql_strfcat_x( &bsi->bsi_flt_where,
529                                 bsi->bsi_op->o_tmpmemctx,
530                                 "b",
531                                 &f->f_sub_initial );
532                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
533                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
534                 }
535         }
536
537         backsql_strfcat_x( &bsi->bsi_flt_where,
538                         bsi->bsi_op->o_tmpmemctx,
539                         "c", '%' );
540
541         if ( f->f_sub_any != NULL ) {
542                 for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
543                         ber_len_t       start;
544
545 #ifdef BACKSQL_TRACE
546                         Debug( LDAP_DEBUG_TRACE, 
547                                 "==>backsql_process_sub_filter(%s): "
548                                 "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val, 
549                                 i, f->f_sub_any[ i ].bv_val );
550 #endif /* BACKSQL_TRACE */
551
552                         start = bsi->bsi_flt_where.bb_val.bv_len;
553                         backsql_strfcat_x( &bsi->bsi_flt_where,
554                                         bsi->bsi_op->o_tmpmemctx,
555                                         "bc",
556                                         &f->f_sub_any[ i ],
557                                         '%' );
558                         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
559                                 /*
560                                  * Note: toupper('%') = '%'
561                                  */
562                                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
563                         }
564                 }
565         }
566
567         if ( !BER_BVISNULL( &f->f_sub_final ) ) {
568                 ber_len_t       start;
569
570 #ifdef BACKSQL_TRACE
571                 Debug( LDAP_DEBUG_TRACE, 
572                         "==>backsql_process_sub_filter(%s): "
573                         "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
574                         f->f_sub_final.bv_val, 0 );
575 #endif /* BACKSQL_TRACE */
576
577                 start = bsi->bsi_flt_where.bb_val.bv_len;
578                 backsql_strfcat_x( &bsi->bsi_flt_where,
579                                 bsi->bsi_op->o_tmpmemctx,
580                                 "b",
581                                 &f->f_sub_final );
582                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
583                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
584                 }
585         }
586
587         backsql_strfcat_x( &bsi->bsi_flt_where,
588                         bsi->bsi_op->o_tmpmemctx,
589                         "l", 
590                         (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
591  
592         return 1;
593 }
594
595 static int
596 backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
597 {
598         if ( BER_BVISNULL( from_tbls ) ) {
599                 return LDAP_SUCCESS;
600         }
601
602         if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
603                 char            *start, *end;
604                 struct berval   tmp;
605
606                 ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
607
608                 for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
609                         if ( end ) {
610                                 end[0] = '\0';
611                         }
612
613                         if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
614                         {
615                                 backsql_strfcat_x( &bsi->bsi_from,
616                                                 bsi->bsi_op->o_tmpmemctx,
617                                                 "cs", ',', start );
618                         }
619
620                         if ( end ) {
621                                 /* in case there are spaces after the comma... */
622                                 for ( start = &end[1]; isspace( start[0] ); start++ );
623                                 if ( start[0] ) {
624                                         end = strchr( start, ',' );
625                                 } else {
626                                         start = NULL;
627                                 }
628                         } else {
629                                 start = NULL;
630                         }
631                 }
632
633                 bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
634
635         } else {
636                 backsql_strfcat_x( &bsi->bsi_from,
637                                 bsi->bsi_op->o_tmpmemctx,
638                                 "b", from_tbls );
639         }
640
641         return LDAP_SUCCESS;
642 }
643
644 static int
645 backsql_process_filter( backsql_srch_info *bsi, Filter *f )
646 {
647         backsql_at_map_rec      **vat = NULL;
648         AttributeDescription    *ad = NULL;
649         unsigned                i;
650         int                     done = 0;
651         int                     rc = 0;
652
653         Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n", 0, 0, 0 );
654         if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
655                 struct berval   flt;
656                 char            *msg = NULL;
657
658                 switch ( f->f_result ) {
659                 case LDAP_COMPARE_TRUE:
660                         BER_BVSTR( &flt, "10=10" );
661                         msg = "TRUE";
662                         break;
663
664                 case LDAP_COMPARE_FALSE:
665                         BER_BVSTR( &flt, "11=0" );
666                         msg = "FALSE";
667                         break;
668
669                 case SLAPD_COMPARE_UNDEFINED:
670                         BER_BVSTR( &flt, "12=0" );
671                         msg = "UNDEFINED";
672                         break;
673
674                 default:
675                         rc = -1;
676                         goto done;
677                 }
678
679                 Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
680                         "filter computed (%s)\n", msg, 0, 0 );
681                 backsql_strfcat_x( &bsi->bsi_flt_where,
682                                 bsi->bsi_op->o_tmpmemctx, "b", &flt );
683                 rc = 1;
684                 goto done;
685         }
686
687         switch( f->f_choice ) {
688         case LDAP_FILTER_OR:
689                 rc = backsql_process_filter_list( bsi, f->f_or, 
690                                 LDAP_FILTER_OR );
691                 done = 1;
692                 break;
693                 
694         case LDAP_FILTER_AND:
695                 rc = backsql_process_filter_list( bsi, f->f_and,
696                                 LDAP_FILTER_AND );
697                 done = 1;
698                 break;
699
700         case LDAP_FILTER_NOT:
701                 backsql_strfcat_x( &bsi->bsi_flt_where,
702                                 bsi->bsi_op->o_tmpmemctx,
703                                 "l",
704                                 (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
705                                         "NOT (" /* ) */ );
706                 rc = backsql_process_filter( bsi, f->f_not );
707                 backsql_strfcat_x( &bsi->bsi_flt_where,
708                                 bsi->bsi_op->o_tmpmemctx,
709                                 "c", /* ( */ ')' );
710                 done = 1;
711                 break;
712
713         case LDAP_FILTER_PRESENT:
714                 ad = f->f_desc;
715                 break;
716                 
717         case LDAP_FILTER_EXT:
718                 ad = f->f_mra->ma_desc;
719                 if ( f->f_mr_dnattrs ) {
720                         /*
721                          * if dn attrs filtering is requested, better return 
722                          * success and let test_filter() deal with candidate
723                          * selection; otherwise we'd need to set conditions
724                          * on the contents of the DN, e.g. "SELECT ... FROM
725                          * ldap_entries AS attributeName WHERE attributeName.dn
726                          * like '%attributeName=value%'"
727                          */
728                         backsql_strfcat_x( &bsi->bsi_flt_where,
729                                         bsi->bsi_op->o_tmpmemctx,
730                                         "l",
731                                         (ber_len_t)STRLENOF( "1=1" ), "1=1" );
732                         bsi->bsi_status = LDAP_SUCCESS;
733                         rc = 1;
734                         goto done;
735                 }
736                 break;
737                 
738         default:
739                 ad = f->f_av_desc;
740                 break;
741         }
742
743         if ( rc == -1 ) {
744                 goto done;
745         }
746  
747         if ( done ) {
748                 rc = 1;
749                 goto done;
750         }
751
752         /*
753          * Turn structuralObjectClass into objectClass
754          */
755         if ( ad == slap_schema.si_ad_objectClass 
756                         || ad == slap_schema.si_ad_structuralObjectClass )
757         {
758                 /*
759                  * If the filter is LDAP_FILTER_PRESENT, then it's done;
760                  * otherwise, let's see if we are lucky: filtering
761                  * for "structural" objectclass or ancestor...
762                  */
763                 switch ( f->f_choice ) {
764                 case LDAP_FILTER_EQUALITY:
765                 {
766                         ObjectClass     *oc = oc_bvfind( &f->f_av_value );
767
768                         if ( oc == NULL ) {
769                                 Debug( LDAP_DEBUG_TRACE,
770                                                 "backsql_process_filter(): "
771                                                 "unknown objectClass \"%s\" "
772                                                 "in filter\n",
773                                                 f->f_av_value.bv_val, 0, 0 );
774                                 bsi->bsi_status = LDAP_OTHER;
775                                 rc = -1;
776                                 goto done;
777                         }
778
779                         /*
780                          * "structural" objectClass inheritance:
781                          * - a search for "person" will also return 
782                          *   "inetOrgPerson"
783                          * - a search for "top" will return everything
784                          */
785                         if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
786                                 static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
787
788                                 backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
789
790                                 backsql_strfcat_x( &bsi->bsi_flt_where,
791                                                 bsi->bsi_op->o_tmpmemctx,
792                                                 "lbl",
793                                                 (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
794                                                         "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
795                                                 &bsi->bsi_oc->bom_oc->soc_cname,
796                                                 (ber_len_t)STRLENOF( /* ((' */ "'))" ),
797                                                         /* ((' */ "'))" );
798                                 bsi->bsi_status = LDAP_SUCCESS;
799                                 rc = 1;
800                                 goto done;
801                         }
802
803                         break;
804                 }
805
806                 case LDAP_FILTER_PRESENT:
807                         backsql_strfcat_x( &bsi->bsi_flt_where,
808                                         bsi->bsi_op->o_tmpmemctx,
809                                         "l",
810                                         (ber_len_t)STRLENOF( "3=3" ), "3=3" );
811                         bsi->bsi_status = LDAP_SUCCESS;
812                         rc = 1;
813                         goto done;
814
815                         /* FIXME: LDAP_FILTER_EXT? */
816                         
817                 default:
818                         Debug( LDAP_DEBUG_TRACE,
819                                         "backsql_process_filter(): "
820                                         "illegal/unhandled filter "
821                                         "on objectClass attribute",
822                                         0, 0, 0 );
823                         bsi->bsi_status = LDAP_OTHER;
824                         rc = -1;
825                         goto done;
826                 }
827
828         } else if ( ad == slap_schema.si_ad_entryUUID ) {
829                 unsigned long   oc_id;
830 #ifdef BACKSQL_ARBITRARY_KEY
831                 struct berval   keyval;
832 #else /* ! BACKSQL_ARBITRARY_KEY */
833                 unsigned long   keyval;
834                 char            keyvalbuf[] = "18446744073709551615";
835 #endif /* ! BACKSQL_ARBITRARY_KEY */
836
837                 switch ( f->f_choice ) {
838                 case LDAP_FILTER_EQUALITY:
839                         backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
840
841                         if ( oc_id != bsi->bsi_oc->bom_id ) {
842                                 bsi->bsi_status = LDAP_SUCCESS;
843                                 rc = -1;
844                                 goto done;
845                         }
846
847 #ifdef BACKSQL_ARBITRARY_KEY
848                         backsql_strfcat_x( &bsi->bsi_flt_where,
849                                         bsi->bsi_op->o_tmpmemctx,
850                                         "bcblbc",
851                                         &bsi->bsi_oc->bom_keytbl, '.',
852                                         &bsi->bsi_oc->bom_keycol,
853                                         STRLENOF( " LIKE '" ), " LIKE '",
854                                         &keyval, '\'' );
855 #else /* ! BACKSQL_ARBITRARY_KEY */
856                         snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
857                         backsql_strfcat_x( &bsi->bsi_flt_where,
858                                         bsi->bsi_op->o_tmpmemctx,
859                                         "bcbcs",
860                                         &bsi->bsi_oc->bom_keytbl, '.',
861                                         &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
862 #endif /* ! BACKSQL_ARBITRARY_KEY */
863                         break;
864
865                 case LDAP_FILTER_PRESENT:
866                         backsql_strfcat_x( &bsi->bsi_flt_where,
867                                         bsi->bsi_op->o_tmpmemctx,
868                                         "l",
869                                         (ber_len_t)STRLENOF( "4=4" ), "4=4" );
870                         break;
871
872                 default:
873                         rc = -1;
874                         goto done;
875                 }
876
877                 bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
878                 rc = 1;
879                 goto done;
880
881 #ifdef BACKSQL_SYNCPROV
882         } else if ( ad == slap_schema.si_ad_entryCSN ) {
883                 /*
884                  * support for syncrepl as producer...
885                  */
886 #if 0
887                 if ( !bsi->bsi_op->o_sync ) {
888                         /* unsupported at present... */
889                         bsi->bsi_status = LDAP_OTHER;
890                         rc = -1;
891                         goto done;
892                 }
893 #endif
894
895                 bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
896
897                 /* if doing a syncrepl, try to return as much as possible,
898                  * and always match the filter */
899                 backsql_strfcat_x( &bsi->bsi_flt_where,
900                                 bsi->bsi_op->o_tmpmemctx,
901                                 "l",
902                                 (ber_len_t)STRLENOF( "5=5" ), "5=5" );
903
904                 /* save for later use in operational attributes */
905                 /* FIXME: saves only the first occurrence, because 
906                  * the filter during updates is written as
907                  * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
908                  * so we want our fake entryCSN to match the greatest
909                  * value
910                  */
911                 if ( bsi->bsi_op->o_private == NULL ) {
912                         bsi->bsi_op->o_private = &f->f_av_value;
913                 }
914                 bsi->bsi_status = LDAP_SUCCESS;
915
916                 rc = 1;
917                 goto done;
918 #endif /* BACKSQL_SYNCPROV */
919
920         } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
921                 /*
922                  * FIXME: this is not robust; e.g. a filter
923                  * '(!(hasSubordinates=TRUE))' fails because
924                  * in SQL it would read 'NOT (1=1)' instead 
925                  * of no condition.  
926                  * Note however that hasSubordinates is boolean, 
927                  * so a more appropriate filter would be 
928                  * '(hasSubordinates=FALSE)'
929                  *
930                  * A more robust search for hasSubordinates
931                  * would * require joining the ldap_entries table
932                  * selecting if there are descendants of the
933                  * candidate.
934                  */
935                 backsql_strfcat_x( &bsi->bsi_flt_where,
936                                 bsi->bsi_op->o_tmpmemctx,
937                                 "l",
938                                 (ber_len_t)STRLENOF( "6=6" ), "6=6" );
939                 if ( ad == slap_schema.si_ad_hasSubordinates ) {
940                         /*
941                          * instruct candidate selection algorithm
942                          * and attribute list to try to detect
943                          * if an entry has subordinates
944                          */
945                         bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
946
947                 } else {
948                         /*
949                          * clear attributes to fetch, to require ALL
950                          * and try extended match on all attributes
951                          */
952                         backsql_attrlist_add( bsi, NULL );
953                 }
954                 rc = 1;
955                 goto done;
956         }
957
958         /*
959          * attribute inheritance:
960          */
961         if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
962                 bsi->bsi_status = LDAP_OTHER;
963                 rc = -1;
964                 goto done;
965         }
966
967         if ( vat == NULL ) {
968                 /* search anyway; other parts of the filter
969                  * may succeeed */
970                 backsql_strfcat_x( &bsi->bsi_flt_where,
971                                 bsi->bsi_op->o_tmpmemctx,
972                                 "l",
973                                 (ber_len_t)STRLENOF( "7=7" ), "7=7" );
974                 bsi->bsi_status = LDAP_SUCCESS;
975                 rc = 1;
976                 goto done;
977         }
978
979         /* if required, open extra level of parens */
980         done = 0;
981         if ( vat[0]->bam_next || vat[1] ) {
982                 backsql_strfcat_x( &bsi->bsi_flt_where,
983                                 bsi->bsi_op->o_tmpmemctx,
984                                 "c", '(' );
985                 done = 1;
986         }
987
988         i = 0;
989 next:;
990         /* apply attr */
991         if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
992                 return -1;
993         }
994
995         /* if more definitions of the same attr, apply */
996         if ( vat[i]->bam_next ) {
997                 backsql_strfcat_x( &bsi->bsi_flt_where,
998                                 bsi->bsi_op->o_tmpmemctx,
999                                 "l",
1000                         STRLENOF( " OR " ), " OR " );
1001                 vat[i] = vat[i]->bam_next;
1002                 goto next;
1003         }
1004
1005         /* if more descendants of the same attr, apply */
1006         i++;
1007         if ( vat[i] ) {
1008                 backsql_strfcat_x( &bsi->bsi_flt_where,
1009                                 bsi->bsi_op->o_tmpmemctx,
1010                                 "l",
1011                         STRLENOF( " OR " ), " OR " );
1012                 goto next;
1013         }
1014
1015         /* if needed, close extra level of parens */
1016         if ( done ) {
1017                 backsql_strfcat_x( &bsi->bsi_flt_where,
1018                                 bsi->bsi_op->o_tmpmemctx,
1019                                 "c", ')' );
1020         }
1021
1022         rc = 1;
1023
1024 done:;
1025         if ( vat ) {
1026                 ch_free( vat );
1027         }
1028
1029         Debug( LDAP_DEBUG_TRACE,
1030                         "<==backsql_process_filter() %s\n",
1031                         rc == 1 ? "succeeded" : "failed", 0, 0);
1032
1033         return rc;
1034 }
1035
1036 static int
1037 backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
1038                 int casefold, struct berval *filter_value )
1039 {
1040         /*
1041          * maybe we should check type of at->sel_expr here somehow,
1042          * to know whether upper_func is applicable, but for now
1043          * upper_func stuff is made for Oracle, where UPPER is
1044          * safely applicable to NUMBER etc.
1045          */
1046         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1047                 ber_len_t       start;
1048
1049                 backsql_strfcat_x( &bsi->bsi_flt_where,
1050                                 bsi->bsi_op->o_tmpmemctx,
1051                                 "cbl",
1052                                 '(', /* ) */
1053                                 &at->bam_sel_expr_u, 
1054                                 (ber_len_t)STRLENOF( "='" ),
1055                                         "='" );
1056
1057                 start = bsi->bsi_flt_where.bb_val.bv_len;
1058
1059                 backsql_strfcat_x( &bsi->bsi_flt_where,
1060                                 bsi->bsi_op->o_tmpmemctx,
1061                                 "bl",
1062                                 filter_value, 
1063                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
1064                                         /* (' */ "')" );
1065
1066                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1067
1068         } else {
1069                 backsql_strfcat_x( &bsi->bsi_flt_where,
1070                                 bsi->bsi_op->o_tmpmemctx,
1071                                 "cblbl",
1072                                 '(', /* ) */
1073                                 &at->bam_sel_expr,
1074                                 (ber_len_t)STRLENOF( "='" ), "='",
1075                                 filter_value,
1076                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
1077                                         /* (' */ "')" );
1078         }
1079
1080         return 1;
1081 }
1082         
1083 static int
1084 backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
1085                 int casefold, struct berval *filter_value )
1086 {
1087         /*
1088          * maybe we should check type of at->sel_expr here somehow,
1089          * to know whether upper_func is applicable, but for now
1090          * upper_func stuff is made for Oracle, where UPPER is
1091          * safely applicable to NUMBER etc.
1092          */
1093         if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1094                 ber_len_t       start;
1095
1096                 backsql_strfcat_x( &bsi->bsi_flt_where,
1097                                 bsi->bsi_op->o_tmpmemctx,
1098                                 "cbl",
1099                                 '(', /* ) */
1100                                 &at->bam_sel_expr_u, 
1101                                 (ber_len_t)STRLENOF( " LIKE '%" ),
1102                                         " LIKE '%" );
1103
1104                 start = bsi->bsi_flt_where.bb_val.bv_len;
1105
1106                 backsql_strfcat_x( &bsi->bsi_flt_where,
1107                                 bsi->bsi_op->o_tmpmemctx,
1108                                 "bl",
1109                                 filter_value, 
1110                                 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1111                                         /* (' */ "%')" );
1112
1113                 ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1114
1115         } else {
1116                 backsql_strfcat_x( &bsi->bsi_flt_where,
1117                                 bsi->bsi_op->o_tmpmemctx,
1118                                 "cblbl",
1119                                 '(', /* ) */
1120                                 &at->bam_sel_expr,
1121                                 (ber_len_t)STRLENOF( " LIKE '%" ),
1122                                         " LIKE '%",
1123                                 filter_value,
1124                                 (ber_len_t)STRLENOF( /* (' */ "%')" ),
1125                                         /* (' */ "%')" );
1126         }
1127
1128         return 1;
1129 }
1130
1131 static int
1132 backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
1133 {
1134         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1135         int                     casefold = 0;
1136         struct berval           *filter_value = NULL;
1137         MatchingRule            *matching_rule = NULL;
1138         struct berval           ordering = BER_BVC("<=");
1139
1140         Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
1141                 at->bam_ad->ad_cname.bv_val, 0, 0 );
1142
1143         /*
1144          * need to add this attribute to list of attrs to load,
1145          * so that we can do test_filter() later
1146          */
1147         backsql_attrlist_add( bsi, at->bam_ad );
1148
1149         backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
1150
1151         if ( !BER_BVISNULL( &at->bam_join_where )
1152                         && strstr( bsi->bsi_join_where.bb_val.bv_val,
1153                                 at->bam_join_where.bv_val ) == NULL )
1154         {
1155                 backsql_strfcat_x( &bsi->bsi_join_where,
1156                                 bsi->bsi_op->o_tmpmemctx,
1157                                 "lb",
1158                                 (ber_len_t)STRLENOF( " AND " ), " AND ",
1159                                 &at->bam_join_where );
1160         }
1161
1162         switch ( f->f_choice ) {
1163         case LDAP_FILTER_EQUALITY:
1164                 filter_value = &f->f_av_value;
1165                 matching_rule = at->bam_ad->ad_type->sat_equality;
1166
1167                 goto equality_match;
1168
1169                 /* fail over into next case */
1170                 
1171         case LDAP_FILTER_EXT:
1172                 filter_value = &f->f_mra->ma_value;
1173                 matching_rule = f->f_mr_rule;
1174
1175 equality_match:;
1176                 /* always uppercase strings by now */
1177 #ifdef BACKSQL_UPPERCASE_FILTER
1178                 if ( SLAP_MR_ASSOCIATED( matching_rule,
1179                                         bi->sql_caseIgnoreMatch ) )
1180 #endif /* BACKSQL_UPPERCASE_FILTER */
1181                 {
1182                         casefold = 1;
1183                 }
1184
1185                 /* FIXME: directoryString filtering should use a similar
1186                  * approach to deal with non-prettified values like
1187                  * " A  non    prettified   value  ", by using a LIKE
1188                  * filter with all whitespaces collapsed to a single '%' */
1189                 if ( SLAP_MR_ASSOCIATED( matching_rule,
1190                                         bi->sql_telephoneNumberMatch ) )
1191                 {
1192                         struct berval   bv;
1193                         ber_len_t       i;
1194
1195                         /*
1196                          * to check for matching telephone numbers
1197                          * with intermized chars, e.g. val='1234'
1198                          * use
1199                          * 
1200                          * val LIKE '%1%2%3%4%'
1201                          */
1202
1203                         bv.bv_len = 2 * filter_value->bv_len - 1;
1204                         bv.bv_val = ch_malloc( bv.bv_len + 1 );
1205
1206                         bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
1207                         for ( i = 1; i < filter_value->bv_len; i++ ) {
1208                                 bv.bv_val[ 2 * i - 1 ] = '%';
1209                                 bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
1210                         }
1211                         bv.bv_val[ 2 * i - 1 ] = '\0';
1212
1213                         (void)backsql_process_filter_like( bsi, at, casefold, &bv );
1214                         ch_free( bv.bv_val );
1215
1216                         break;
1217                 }
1218
1219                 /* NOTE: this is required by objectClass inheritance 
1220                  * and auxiliary objectClass use in filters for slightly
1221                  * more efficient candidate selection. */
1222                 /* FIXME: a bit too many specializations to deal with
1223                  * very specific cases... */
1224                 if ( at->bam_ad == slap_schema.si_ad_objectClass
1225                                 || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
1226                 {
1227                         backsql_strfcat_x( &bsi->bsi_flt_where,
1228                                         bsi->bsi_op->o_tmpmemctx,
1229                                         "lbl",
1230                                         (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
1231                                                 "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
1232                                         filter_value,
1233                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
1234                                                 /* (' */ "')" );
1235                         break;
1236                 }
1237
1238                 /*
1239                  * maybe we should check type of at->sel_expr here somehow,
1240                  * to know whether upper_func is applicable, but for now
1241                  * upper_func stuff is made for Oracle, where UPPER is
1242                  * safely applicable to NUMBER etc.
1243                  */
1244                 (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
1245                 break;
1246
1247         case LDAP_FILTER_GE:
1248                 ordering.bv_val = ">=";
1249
1250                 /* fall thru to next case */
1251                 
1252         case LDAP_FILTER_LE:
1253                 filter_value = &f->f_av_value;
1254                 
1255                 /* always uppercase strings by now */
1256 #ifdef BACKSQL_UPPERCASE_FILTER
1257                 if ( at->bam_ad->ad_type->sat_ordering &&
1258                                 SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
1259                                         bi->sql_caseIgnoreMatch ) )
1260 #endif /* BACKSQL_UPPERCASE_FILTER */
1261                 {
1262                         casefold = 1;
1263                 }
1264
1265                 /*
1266                  * FIXME: should we uppercase the operands?
1267                  */
1268                 if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
1269                         ber_len_t       start;
1270
1271                         backsql_strfcat_x( &bsi->bsi_flt_where,
1272                                         bsi->bsi_op->o_tmpmemctx,
1273                                         "cbbc",
1274                                         '(', /* ) */
1275                                         &at->bam_sel_expr_u, 
1276                                         &ordering,
1277                                         '\'' );
1278
1279                         start = bsi->bsi_flt_where.bb_val.bv_len;
1280
1281                         backsql_strfcat_x( &bsi->bsi_flt_where,
1282                                         bsi->bsi_op->o_tmpmemctx,
1283                                         "bl",
1284                                         filter_value, 
1285                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
1286                                                 /* (' */ "')" );
1287
1288                         ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
1289                 
1290                 } else {
1291                         backsql_strfcat_x( &bsi->bsi_flt_where,
1292                                         bsi->bsi_op->o_tmpmemctx,
1293                                         "cbbcbl",
1294                                         '(' /* ) */ ,
1295                                         &at->bam_sel_expr,
1296                                         &ordering,
1297                                         '\'',
1298                                         &f->f_av_value,
1299                                         (ber_len_t)STRLENOF( /* (' */ "')" ),
1300                                                 /* ( */ "')" );
1301                 }
1302                 break;
1303
1304         case LDAP_FILTER_PRESENT:
1305                 backsql_strfcat_x( &bsi->bsi_flt_where,
1306                                 bsi->bsi_op->o_tmpmemctx,
1307                                 "lbl",
1308                                 (ber_len_t)STRLENOF( "NOT (" /* ) */),
1309                                         "NOT (", /* ) */
1310                                 &at->bam_sel_expr, 
1311                                 (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
1312                                         /* ( */ " IS NULL)" );
1313                 break;
1314
1315         case LDAP_FILTER_SUBSTRINGS:
1316                 backsql_process_sub_filter( bsi, f, at );
1317                 break;
1318
1319         case LDAP_FILTER_APPROX:
1320                 /* we do our best */
1321
1322                 /*
1323                  * maybe we should check type of at->sel_expr here somehow,
1324                  * to know whether upper_func is applicable, but for now
1325                  * upper_func stuff is made for Oracle, where UPPER is
1326                  * safely applicable to NUMBER etc.
1327                  */
1328                 (void)backsql_process_filter_like( bsi, at, 1, &f->f_av_value );
1329                 break;
1330
1331         default:
1332                 /* unhandled filter type; should not happen */
1333                 assert( 0 );
1334                 backsql_strfcat_x( &bsi->bsi_flt_where,
1335                                 bsi->bsi_op->o_tmpmemctx,
1336                                 "l",
1337                                 (ber_len_t)STRLENOF( "8=8" ), "8=8" );
1338                 break;
1339
1340         }
1341
1342         Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
1343                 at->bam_ad->ad_cname.bv_val, 0, 0 );
1344
1345         return 1;
1346 }
1347
1348 static int
1349 backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
1350 {
1351         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1352         int                     rc;
1353
1354         assert( query != NULL );
1355         BER_BVZERO( query );
1356
1357         bsi->bsi_use_subtree_shortcut = 0;
1358
1359         Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n", 0, 0, 0 );
1360         BER_BVZERO( &bsi->bsi_sel.bb_val );
1361         BER_BVZERO( &bsi->bsi_sel.bb_val );
1362         bsi->bsi_sel.bb_len = 0;
1363         BER_BVZERO( &bsi->bsi_from.bb_val );
1364         bsi->bsi_from.bb_len = 0;
1365         BER_BVZERO( &bsi->bsi_join_where.bb_val );
1366         bsi->bsi_join_where.bb_len = 0;
1367         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1368         bsi->bsi_flt_where.bb_len = 0;
1369
1370         backsql_strfcat_x( &bsi->bsi_sel,
1371                         bsi->bsi_op->o_tmpmemctx,
1372                         "lbcbc",
1373                         (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
1374                                 "SELECT DISTINCT ldap_entries.id,", 
1375                         &bsi->bsi_oc->bom_keytbl, 
1376                         '.', 
1377                         &bsi->bsi_oc->bom_keycol, 
1378                         ',' );
1379
1380         if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
1381                 backsql_strfcat_x( &bsi->bsi_sel,
1382                                 bsi->bsi_op->o_tmpmemctx,
1383                                 "blbl",
1384                                 &bi->sql_strcast_func, 
1385                                 (ber_len_t)STRLENOF( "('" /* ') */ ),
1386                                         "('" /* ') */ ,
1387                                 &bsi->bsi_oc->bom_oc->soc_cname,
1388                                 (ber_len_t)STRLENOF( /* (' */ "')" ),
1389                                         /* (' */ "')" );
1390         } else {
1391                 backsql_strfcat_x( &bsi->bsi_sel,
1392                                 bsi->bsi_op->o_tmpmemctx,
1393                                 "cbc",
1394                                 '\'',
1395                                 &bsi->bsi_oc->bom_oc->soc_cname,
1396                                 '\'' );
1397         }
1398
1399         backsql_strfcat_x( &bsi->bsi_sel,
1400                         bsi->bsi_op->o_tmpmemctx,
1401                         "b",
1402                         &bi->sql_dn_oc_aliasing );
1403         backsql_strfcat_x( &bsi->bsi_from,
1404                         bsi->bsi_op->o_tmpmemctx,
1405                         "lb",
1406                         (ber_len_t)STRLENOF( " FROM ldap_entries," ),
1407                                 " FROM ldap_entries,",
1408                         &bsi->bsi_oc->bom_keytbl );
1409
1410         backsql_strfcat_x( &bsi->bsi_join_where,
1411                         bsi->bsi_op->o_tmpmemctx,
1412                         "lbcbl",
1413                         (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
1414                         &bsi->bsi_oc->bom_keytbl,
1415                         '.',
1416                         &bsi->bsi_oc->bom_keycol,
1417                         (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
1418                                 "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
1419
1420         switch ( bsi->bsi_scope ) {
1421         case LDAP_SCOPE_BASE:
1422                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1423                         backsql_strfcat_x( &bsi->bsi_join_where,
1424                                         bsi->bsi_op->o_tmpmemctx, 
1425                                         "bl",
1426                                         &bi->sql_upper_func,
1427                                         (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
1428                                                 "(ldap_entries.dn)=?" );
1429                 } else {
1430                         backsql_strfcat_x( &bsi->bsi_join_where,
1431                                         bsi->bsi_op->o_tmpmemctx,
1432                                         "l",
1433                                         (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
1434                                                 "ldap_entries.dn=?" );
1435                 }
1436                 break;
1437                 
1438         case BACKSQL_SCOPE_BASE_LIKE:
1439                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1440                         backsql_strfcat_x( &bsi->bsi_join_where,
1441                                         bsi->bsi_op->o_tmpmemctx,
1442                                         "bl",
1443                                         &bi->sql_upper_func,
1444                                         (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1445                                                 "(ldap_entries.dn) LIKE ?" );
1446                 } else {
1447                         backsql_strfcat_x( &bsi->bsi_join_where,
1448                                         bsi->bsi_op->o_tmpmemctx,
1449                                         "l",
1450                                         (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1451                                                 "ldap_entries.dn LIKE ?" );
1452                 }
1453                 break;
1454                 
1455         case LDAP_SCOPE_ONELEVEL:
1456                 backsql_strfcat_x( &bsi->bsi_join_where,
1457                                 bsi->bsi_op->o_tmpmemctx,
1458                                 "l",
1459                                 (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
1460                                         "ldap_entries.parent=?" );
1461                 break;
1462
1463         case LDAP_SCOPE_SUBORDINATE:
1464         case LDAP_SCOPE_SUBTREE:
1465                 if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
1466                         int             i;
1467                         BackendDB       *bd = bsi->bsi_op->o_bd;
1468
1469                         assert( bd->be_nsuffix != NULL );
1470
1471                         for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
1472                         {
1473                                 if ( dn_match( &bd->be_nsuffix[ i ],
1474                                                         bsi->bsi_base_ndn ) )
1475                                 {
1476                                         /* pass this to the candidate selection
1477                                          * routine so that the DN is not bound
1478                                          * to the select statement */
1479                                         bsi->bsi_use_subtree_shortcut = 1;
1480                                         break;
1481                                 }
1482                         }
1483                 }
1484
1485                 if ( bsi->bsi_use_subtree_shortcut ) {
1486                         /* Skip the base DN filter, as every entry will match it */
1487                         backsql_strfcat_x( &bsi->bsi_join_where,
1488                                         bsi->bsi_op->o_tmpmemctx,
1489                                         "l",
1490                                         (ber_len_t)STRLENOF( "9=9"), "9=9");
1491
1492                 } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
1493                         backsql_strfcat_x( &bsi->bsi_join_where,
1494                                         bsi->bsi_op->o_tmpmemctx,
1495                                         "b",
1496                                         &bi->sql_subtree_cond );
1497
1498                 } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
1499                         backsql_strfcat_x( &bsi->bsi_join_where,
1500                                         bsi->bsi_op->o_tmpmemctx,
1501                                         "bl",
1502                                         &bi->sql_upper_func,
1503                                         (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
1504                                                 "(ldap_entries.dn) LIKE ?"  );
1505
1506                 } else {
1507                         backsql_strfcat_x( &bsi->bsi_join_where,
1508                                         bsi->bsi_op->o_tmpmemctx,
1509                                         "l",
1510                                         (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
1511                                                 "ldap_entries.dn LIKE ?" );
1512                 }
1513
1514                 break;
1515
1516         default:
1517                 assert( 0 );
1518         }
1519
1520         rc = backsql_process_filter( bsi, bsi->bsi_filter );
1521         if ( rc > 0 ) {
1522                 struct berbuf   bb = BB_NULL;
1523
1524                 backsql_strfcat_x( &bb,
1525                                 bsi->bsi_op->o_tmpmemctx,
1526                                 "bbblb",
1527                                 &bsi->bsi_sel.bb_val,
1528                                 &bsi->bsi_from.bb_val, 
1529                                 &bsi->bsi_join_where.bb_val,
1530                                 (ber_len_t)STRLENOF( " AND " ), " AND ",
1531                                 &bsi->bsi_flt_where.bb_val );
1532
1533                 *query = bb.bb_val;
1534
1535         } else if ( rc < 0 ) {
1536                 /* 
1537                  * Indicates that there's no possible way the filter matches
1538                  * anything.  No need to issue the query
1539                  */
1540                 free( query->bv_val );
1541                 BER_BVZERO( query );
1542         }
1543  
1544         bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1545         BER_BVZERO( &bsi->bsi_sel.bb_val );
1546         bsi->bsi_sel.bb_len = 0;
1547         bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1548         BER_BVZERO( &bsi->bsi_from.bb_val );
1549         bsi->bsi_from.bb_len = 0;
1550         bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1551         BER_BVZERO( &bsi->bsi_join_where.bb_val );
1552         bsi->bsi_join_where.bb_len = 0;
1553         bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
1554         BER_BVZERO( &bsi->bsi_flt_where.bb_val );
1555         bsi->bsi_flt_where.bb_len = 0;
1556         
1557         Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
1558                 query->bv_val ? query->bv_val : "NULL", 0, 0 );
1559         
1560         return ( rc <= 0 ? 1 : 0 );
1561 }
1562
1563 static int
1564 backsql_oc_get_candidates( void *v_oc, void *v_bsi )
1565 {
1566         backsql_oc_map_rec      *oc = v_oc;
1567         backsql_srch_info       *bsi = v_bsi;
1568         Operation               *op = bsi->bsi_op;
1569         backsql_info            *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
1570         struct berval           query;
1571         SQLHSTMT                sth = SQL_NULL_HSTMT;
1572         RETCODE                 rc;
1573         int                     res;
1574         BACKSQL_ROW_NTS         row;
1575         int                     i;
1576         int                     j;
1577         int                     n_candidates = bsi->bsi_n_candidates;
1578
1579         /* 
1580          * + 1 because we need room for '%';
1581          * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
1582          * this makes a subtree
1583          * search for a DN BACKSQL_MAX_DN_LEN long legal 
1584          * if it returns that DN only
1585          */
1586         char                    tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
1587
1588         bsi->bsi_status = LDAP_SUCCESS;
1589  
1590         Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
1591                         BACKSQL_OC_NAME( oc ), 0, 0 );
1592
1593         /* check for abandon */
1594         if ( op->o_abandon ) {
1595                 bsi->bsi_status = SLAPD_ABANDON;
1596                 return BACKSQL_AVL_STOP;
1597         }
1598
1599         if ( bsi->bsi_n_candidates == -1 ) {
1600                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1601                         "unchecked limit has been overcome\n", 0, 0, 0 );
1602                 /* should never get here */
1603                 assert( 0 );
1604                 bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
1605                 return BACKSQL_AVL_STOP;
1606         }
1607         
1608         bsi->bsi_oc = oc;
1609         res = backsql_srch_query( bsi, &query );
1610         if ( res ) {
1611                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1612                         "error while constructing query for objectclass \"%s\"\n",
1613                         oc->bom_oc->soc_cname.bv_val, 0, 0 );
1614                 /*
1615                  * FIXME: need to separate errors from legally
1616                  * impossible filters
1617                  */
1618                 switch ( bsi->bsi_status ) {
1619                 case LDAP_SUCCESS:
1620                 case LDAP_UNDEFINED_TYPE:
1621                 case LDAP_NO_SUCH_OBJECT:
1622                         /* we are conservative... */
1623                 default:
1624                         bsi->bsi_status = LDAP_SUCCESS;
1625                         /* try next */
1626                         return BACKSQL_AVL_CONTINUE;
1627
1628                 case LDAP_ADMINLIMIT_EXCEEDED:
1629                 case LDAP_OTHER:
1630                         /* don't try any more */
1631                         return BACKSQL_AVL_STOP;
1632                 }
1633         }
1634
1635         if ( BER_BVISNULL( &query ) ) {
1636                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1637                         "could not construct query for objectclass \"%s\"\n",
1638                         oc->bom_oc->soc_cname.bv_val, 0, 0 );
1639                 bsi->bsi_status = LDAP_SUCCESS;
1640                 return BACKSQL_AVL_CONTINUE;
1641         }
1642
1643         Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n", 
1644                         query.bv_val, 0, 0 );
1645
1646         rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
1647         bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
1648         BER_BVZERO( &query );
1649         if ( rc != SQL_SUCCESS ) {
1650                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1651                         "error preparing query\n", 0, 0, 0 );
1652                 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1653                 bsi->bsi_status = LDAP_OTHER;
1654                 return BACKSQL_AVL_CONTINUE;
1655         }
1656         
1657         Debug( LDAP_DEBUG_TRACE, "id: '%ld'\n", bsi->bsi_oc->bom_id, 0, 0 );
1658
1659         rc = backsql_BindParamInt( sth, 1, SQL_PARAM_INPUT,
1660                         &bsi->bsi_oc->bom_id );
1661         if ( rc != SQL_SUCCESS ) {
1662                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1663                         "error binding objectclass id parameter\n", 0, 0, 0 );
1664                 bsi->bsi_status = LDAP_OTHER;
1665                 return BACKSQL_AVL_CONTINUE;
1666         }
1667
1668         switch ( bsi->bsi_scope ) {
1669         case LDAP_SCOPE_BASE:
1670         case BACKSQL_SCOPE_BASE_LIKE:
1671                 /*
1672                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1673                  * however this should be handled earlier
1674                  */
1675                 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1676                         bsi->bsi_status = LDAP_OTHER;
1677                         return BACKSQL_AVL_CONTINUE;
1678                 }
1679
1680                 AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
1681                                 bsi->bsi_base_ndn->bv_len + 1 );
1682
1683                 /* uppercase DN only if the stored DN can be uppercased
1684                  * for comparison */
1685                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1686                         ldap_pvt_str2upper( tmp_base_ndn );
1687                 }
1688
1689                 Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
1690                                 tmp_base_ndn, 0, 0 );
1691
1692                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1693                                 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1694                 if ( rc != SQL_SUCCESS ) {
1695                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1696                                 "error binding base_ndn parameter\n", 0, 0, 0 );
1697                         backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 
1698                                         sth, rc );
1699                         bsi->bsi_status = LDAP_OTHER;
1700                         return BACKSQL_AVL_CONTINUE;
1701                 }
1702                 break;
1703
1704         case LDAP_SCOPE_SUBORDINATE:
1705         case LDAP_SCOPE_SUBTREE:
1706         {
1707                 /* if short-cutting the search base,
1708                  * don't bind any parameter */
1709                 if ( bsi->bsi_use_subtree_shortcut ) {
1710                         break;
1711                 }
1712                 
1713                 /*
1714                  * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
1715                  * however this should be handled earlier
1716                  */
1717                 if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
1718                         bsi->bsi_status = LDAP_OTHER;
1719                         return BACKSQL_AVL_CONTINUE;
1720                 }
1721
1722                 /* 
1723                  * Sets the parameters for the SQL built earlier
1724                  * NOTE that all the databases could actually use 
1725                  * the TimesTen version, which would be cleaner 
1726                  * and would also eliminate the need for the
1727                  * subtree_cond line in the configuration file.  
1728                  * For now, I'm leaving it the way it is, 
1729                  * so non-TimesTen databases use the original code.
1730                  * But at some point this should get cleaned up.
1731                  *
1732                  * If "dn" is being used, do a suffix search.
1733                  * If "dn_ru" is being used, do a prefix search.
1734                  */
1735                 if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
1736                         tmp_base_ndn[ 0 ] = '\0';
1737
1738                         for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
1739                                         j >= 0; i++, j--) {
1740                                 tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
1741                         }
1742
1743                         if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1744                                 tmp_base_ndn[ i++ ] = ',';
1745                         }
1746
1747                         tmp_base_ndn[ i ] = '%';
1748                         tmp_base_ndn[ i + 1 ] = '\0';
1749
1750                 } else {
1751                         i = 0;
1752
1753                         tmp_base_ndn[ i++ ] = '%';
1754
1755                         if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1756                                 tmp_base_ndn[ i++ ] = ',';
1757                         }
1758
1759                         AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
1760                                 bsi->bsi_base_ndn->bv_len + 1 );
1761                 }
1762
1763                 /* uppercase DN only if the stored DN can be uppercased
1764                  * for comparison */
1765                 if ( BACKSQL_CANUPPERCASE( bi ) ) {
1766                         ldap_pvt_str2upper( tmp_base_ndn );
1767                 }
1768
1769                 if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
1770                         Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
1771                                 tmp_base_ndn, 0, 0 );
1772                 } else {
1773                         Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
1774                                 tmp_base_ndn, 0, 0 );
1775                 }
1776
1777                 rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
1778                                 tmp_base_ndn, BACKSQL_MAX_DN_LEN );
1779                 if ( rc != SQL_SUCCESS ) {
1780                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1781                                 "error binding base_ndn parameter (2)\n",
1782                                 0, 0, 0 );
1783                         backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, 
1784                                         sth, rc );
1785                         bsi->bsi_status = LDAP_OTHER;
1786                         return BACKSQL_AVL_CONTINUE;
1787                 }
1788                 break;
1789         }
1790
1791         case LDAP_SCOPE_ONELEVEL:
1792                 assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
1793
1794 #ifdef BACKSQL_ARBITRARY_KEY
1795                 Debug( LDAP_DEBUG_TRACE, "(one)id: \"%s\"\n",
1796                                 bsi->bsi_base_id.eid_id.bv_val, 0, 0 );
1797 #else /* ! BACKSQL_ARBITRARY_KEY */
1798                 Debug( LDAP_DEBUG_TRACE, "(one)id: '%lu'\n",
1799                                 bsi->bsi_base_id.eid_id, 0, 0 );
1800 #endif /* ! BACKSQL_ARBITRARY_KEY */
1801                 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
1802                                 &bsi->bsi_base_id.eid_id );
1803                 if ( rc != SQL_SUCCESS ) {
1804                         Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1805                                 "error binding base id parameter\n", 0, 0, 0 );
1806                         bsi->bsi_status = LDAP_OTHER;
1807                         return BACKSQL_AVL_CONTINUE;
1808                 }
1809                 break;
1810         }
1811         
1812         rc = SQLExecute( sth );
1813         if ( !BACKSQL_SUCCESS( rc ) ) {
1814                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1815                         "error executing query\n", 0, 0, 0 );
1816                 backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
1817                 SQLFreeStmt( sth, SQL_DROP );
1818                 bsi->bsi_status = LDAP_OTHER;
1819                 return BACKSQL_AVL_CONTINUE;
1820         }
1821
1822         backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
1823         rc = SQLFetch( sth );
1824         for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
1825                 struct berval           dn, pdn, ndn;
1826                 backsql_entryID         *c_id = NULL;
1827                 int                     ret;
1828
1829                 ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
1830
1831                 if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
1832                         continue;
1833                 }
1834
1835                 ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
1836                 if ( dn.bv_val != row.cols[ 3 ] ) {
1837                         free( dn.bv_val );
1838                 }
1839
1840                 if ( ret != LDAP_SUCCESS ) {
1841                         continue;
1842                 }
1843
1844                 if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
1845                         goto cleanup;
1846                 }
1847
1848                 c_id = (backsql_entryID *)op->o_tmpcalloc( 1, 
1849                                 sizeof( backsql_entryID ), op->o_tmpmemctx );
1850 #ifdef BACKSQL_ARBITRARY_KEY
1851                 ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
1852                                 op->o_tmpmemctx );
1853                 ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
1854                                 op->o_tmpmemctx );
1855 #else /* ! BACKSQL_ARBITRARY_KEY */
1856                 if ( lutil_atoulx( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
1857                         goto cleanup;
1858                 }
1859                 if ( lutil_atoulx( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
1860                         goto cleanup;
1861                 }
1862 #endif /* ! BACKSQL_ARBITRARY_KEY */
1863                 c_id->eid_oc_id = bsi->bsi_oc->bom_id;
1864
1865                 c_id->eid_dn = pdn;
1866                 c_id->eid_ndn = ndn;
1867
1868                 /* append at end of list ... */
1869                 c_id->eid_next = NULL;
1870                 *bsi->bsi_id_listtail = c_id;
1871                 bsi->bsi_id_listtail = &c_id->eid_next;
1872
1873 #ifdef BACKSQL_ARBITRARY_KEY
1874                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1875                         "added entry id=%s, keyval=%s dn=\"%s\"\n",
1876                         c_id->eid_id.bv_val, c_id->eid_keyval.bv_val,
1877                         row.cols[ 3 ] );
1878 #else /* ! BACKSQL_ARBITRARY_KEY */
1879                 Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
1880                         "added entry id=%ld, keyval=%ld dn=\"%s\"\n",
1881                         c_id->eid_id, c_id->eid_keyval, row.cols[ 3 ] );
1882 #endif /* ! BACKSQL_ARBITRARY_KEY */
1883
1884                 /* count candidates, for unchecked limit */
1885                 bsi->bsi_n_candidates--;
1886                 if ( bsi->bsi_n_candidates == -1 ) {
1887                         break;
1888                 }
1889                 continue;
1890
1891 cleanup:;
1892                 if ( !BER_BVISNULL( &pdn ) ) {
1893                         op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
1894                 }
1895                 if ( !BER_BVISNULL( &ndn ) ) {
1896                         op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
1897                 }
1898                 if ( c_id != NULL ) {
1899                         ch_free( c_id );
1900                 }
1901         }
1902         backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
1903         SQLFreeStmt( sth, SQL_DROP );
1904
1905         Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
1906                         n_candidates - bsi->bsi_n_candidates, 0, 0 );
1907
1908         return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
1909 }
1910
1911 int
1912 backsql_search( Operation *op, SlapReply *rs )
1913 {
1914         backsql_info            *bi = (backsql_info *)op->o_bd->be_private;
1915         SQLHDBC                 dbh = SQL_NULL_HDBC;
1916         int                     sres;
1917         Entry                   user_entry = { 0 },
1918                                 base_entry = { 0 };
1919         int                     manageDSAit = get_manageDSAit( op );
1920         time_t                  stoptime = 0;
1921         backsql_srch_info       bsi = { 0 };
1922         backsql_entryID         *eid = NULL;
1923         struct berval           nbase = BER_BVNULL;
1924
1925         Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
1926                 "base=\"%s\", filter=\"%s\", scope=%d,", 
1927                 op->o_req_ndn.bv_val,
1928                 op->ors_filterstr.bv_val,
1929                 op->ors_scope );
1930         Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
1931                 "attributes to load: %s\n",
1932                 op->ors_deref,
1933                 op->ors_attrsonly,
1934                 op->ors_attrs == NULL ? "all" : "custom list" );
1935
1936         if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
1937                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1938                         "search base length (%ld) exceeds max length (%d)\n", 
1939                         op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN, 0 );
1940                 /*
1941                  * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
1942                  * since it is impossible that such a long DN exists
1943                  * in the backend
1944                  */
1945                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
1946                 send_ldap_result( op, rs );
1947                 return 1;
1948         }
1949
1950         sres = backsql_get_db_conn( op, &dbh );
1951         if ( sres != LDAP_SUCCESS ) {
1952                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
1953                         "could not get connection handle - exiting\n", 
1954                         0, 0, 0 );
1955                 rs->sr_err = sres;
1956                 rs->sr_text = sres == LDAP_OTHER ?  "SQL-backend error" : NULL;
1957                 send_ldap_result( op, rs );
1958                 return 1;
1959         }
1960
1961         /* compute it anyway; root does not use it */
1962         stoptime = op->o_time + op->ors_tlimit;
1963
1964         /* init search */
1965         bsi.bsi_e = &base_entry;
1966         rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
1967                         op->ors_scope,
1968                         stoptime, op->ors_filter,
1969                         dbh, op, rs, op->ors_attrs,
1970                         ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
1971         switch ( rs->sr_err ) {
1972         case LDAP_SUCCESS:
1973                 break;
1974
1975         case LDAP_REFERRAL:
1976                 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
1977                                 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
1978                 {
1979                         rs->sr_err = LDAP_SUCCESS;
1980                         rs->sr_text = NULL;
1981                         rs->sr_matched = NULL;
1982                         if ( rs->sr_ref ) {
1983                                 ber_bvarray_free( rs->sr_ref );
1984                                 rs->sr_ref = NULL;
1985                         }
1986                         break;
1987                 }
1988
1989                 /* an entry was created; free it */
1990                 entry_clean( bsi.bsi_e );
1991
1992                 /* fall thru */
1993
1994         default:
1995 #ifdef SLAP_ACL_HONOR_DISCLOSE
1996                 if ( !BER_BVISNULL( &base_entry.e_nname )
1997                                 && !access_allowed( op, &base_entry,
1998                                         slap_schema.si_ad_entry, NULL,
1999                                         ACL_DISCLOSE, NULL ) )
2000                 {
2001                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
2002                         if ( rs->sr_ref ) {
2003                                 ber_bvarray_free( rs->sr_ref );
2004                                 rs->sr_ref = NULL;
2005                         }
2006                         rs->sr_matched = NULL;
2007                         rs->sr_text = NULL;
2008                 }
2009 #endif /* SLAP_ACL_HONOR_DISCLOSE */
2010
2011                 send_ldap_result( op, rs );
2012
2013                 if ( rs->sr_ref ) {
2014                         ber_bvarray_free( rs->sr_ref );
2015                         rs->sr_ref = NULL;
2016                 }
2017
2018                 if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
2019                         entry_clean( &base_entry );
2020                 }
2021
2022                 goto done;
2023         }
2024 #ifdef SLAP_ACL_HONOR_DISCLOSE
2025         /* NOTE: __NEW__ "search" access is required
2026          * on searchBase object */
2027         {
2028                 slap_mask_t     mask;
2029                 
2030                 if ( get_assert( op ) &&
2031                                 ( test_filter( op, &base_entry, get_assertion( op ) )
2032                                   != LDAP_COMPARE_TRUE ) )
2033                 {
2034                         rs->sr_err = LDAP_ASSERTION_FAILED;
2035                         
2036                 }
2037                 if ( ! access_allowed_mask( op, &base_entry,
2038                                         slap_schema.si_ad_entry,
2039                                         NULL, ACL_SEARCH, NULL, &mask ) )
2040                 {
2041                         if ( rs->sr_err == LDAP_SUCCESS ) {
2042                                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
2043                         }
2044                 }
2045
2046                 if ( rs->sr_err != LDAP_SUCCESS ) {
2047                         if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
2048                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
2049                                 rs->sr_text = NULL;
2050                         }
2051                         send_ldap_result( op, rs );
2052                         goto done;
2053                 }
2054         }
2055 #endif /* SLAP_ACL_HONOR_DISCLOSE */
2056
2057         bsi.bsi_e = NULL;
2058
2059         bsi.bsi_n_candidates =
2060                 ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 : 
2061                 ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
2062                 ( op->ors_limit->lms_s_unchecked ) ) );
2063
2064         switch ( bsi.bsi_scope ) {
2065         case LDAP_SCOPE_BASE:
2066         case BACKSQL_SCOPE_BASE_LIKE:
2067                 /*
2068                  * probably already found...
2069                  */
2070                 bsi.bsi_id_list = &bsi.bsi_base_id;
2071                 bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2072                 break;
2073
2074         case LDAP_SCOPE_SUBTREE:
2075                 /*
2076                  * if baseObject is defined, and if it is the root 
2077                  * of the search, add it to the candidate list
2078                  */
2079                 if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
2080                 {
2081                         bsi.bsi_id_list = &bsi.bsi_base_id;
2082                         bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
2083                 }
2084
2085                 /* FALLTHRU */
2086         default:
2087
2088                 /*
2089                  * for each objectclass we try to construct query which gets IDs
2090                  * of entries matching LDAP query filter and scope (or at least 
2091                  * candidates), and get the IDs
2092                  */
2093                 avl_apply( bi->sql_oc_by_oc, backsql_oc_get_candidates,
2094                                 &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
2095
2096                 /* check for abandon */
2097                 if ( op->o_abandon ) {
2098                         eid = bsi.bsi_id_list;
2099                         rs->sr_err = SLAPD_ABANDON;
2100                         goto send_results;
2101                 }
2102         }
2103
2104         if ( op->ors_limit != NULL      /* isroot == FALSE */
2105                         && op->ors_limit->lms_s_unchecked != -1
2106                         && bsi.bsi_n_candidates == -1 )
2107         {
2108                 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
2109                 send_ldap_result( op, rs );
2110                 goto done;
2111         }
2112
2113         /*
2114          * now we load candidate entries (only those attributes 
2115          * mentioned in attrs and filter), test it against full filter 
2116          * and then send to client; don't free entry_id if baseObject...
2117          */
2118         for ( eid = bsi.bsi_id_list;
2119                         eid != NULL; 
2120                         eid = backsql_free_entryID( op,
2121                                 eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
2122         {
2123                 int             rc;
2124                 Attribute       *a_hasSubordinate = NULL,
2125                                 *a_entryUUID = NULL,
2126                                 *a_entryCSN = NULL,
2127                                 **ap = NULL;
2128                 Entry           *e = NULL;
2129
2130                 /* check for abandon */
2131                 if ( op->o_abandon ) {
2132                         rs->sr_err = SLAPD_ABANDON;
2133                         goto send_results;
2134                 }
2135
2136                 /* check time limit */
2137                 if ( op->ors_tlimit != SLAP_NO_LIMIT
2138                                 && slap_get_time() > stoptime )
2139                 {
2140                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
2141                         rs->sr_ctrls = NULL;
2142                         rs->sr_ref = rs->sr_v2ref;
2143                         goto send_results;
2144                 }
2145
2146 #ifdef BACKSQL_ARBITRARY_KEY
2147                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2148                         "for entry id=%s, oc_id=%ld, keyval=%s\n",
2149                         eid->eid_id.bv_val, eid->eid_oc_id,
2150                         eid->eid_keyval.bv_val );
2151 #else /* ! BACKSQL_ARBITRARY_KEY */
2152                 Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
2153                         "for entry id=%ld, oc_id=%ld, keyval=%ld\n",
2154                         eid->eid_id, eid->eid_oc_id, eid->eid_keyval );
2155 #endif /* ! BACKSQL_ARBITRARY_KEY */
2156
2157                 /* check scope */
2158                 switch ( op->ors_scope ) {
2159                 case LDAP_SCOPE_BASE:
2160                 case BACKSQL_SCOPE_BASE_LIKE:
2161                         if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2162                                 goto next_entry2;
2163                         }
2164                         break;
2165
2166                 case LDAP_SCOPE_ONE:
2167                 {
2168                         struct berval   rdn = eid->eid_ndn;
2169
2170                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
2171                         if ( !dnIsOneLevelRDN( &rdn ) ) {
2172                                 goto next_entry2;
2173                         }
2174                         /* fall thru */
2175                 }
2176
2177                 case LDAP_SCOPE_SUBORDINATE:
2178                         /* discard the baseObject entry */
2179                         if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
2180                                 goto next_entry2;
2181                         }
2182                         /* FALLTHRU */
2183                 case LDAP_SCOPE_SUBTREE:
2184                         /* FIXME: this should never fail... */
2185                         if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
2186                                 assert( 0 );
2187                                 goto next_entry2;
2188                         }
2189                         break;
2190                 }
2191
2192                 if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
2193                         /* don't recollect baseObject... */
2194                         e = bi->sql_baseObject;
2195
2196                 } else if ( eid == &bsi.bsi_base_id ) {
2197                         /* don't recollect searchBase object... */
2198                         e = &base_entry;
2199
2200                 } else {
2201                         bsi.bsi_e = &user_entry;
2202                         rc = backsql_id2entry( &bsi, eid );
2203                         if ( rc != LDAP_SUCCESS ) {
2204                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2205                                         "error %d in backsql_id2entry() "
2206                                         "- skipping\n", rc, 0, 0 );
2207                                 continue;
2208                         }
2209                         e = &user_entry;
2210                 }
2211
2212                 if ( !manageDSAit &&
2213                                 op->ors_scope != LDAP_SCOPE_BASE &&
2214                                 op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
2215                                 is_entry_referral( e ) )
2216                 {
2217                         BerVarray refs;
2218
2219                         refs = get_entry_referrals( op, e );
2220                         if ( !refs ) {
2221                                 backsql_srch_info       bsi2 = { 0 };
2222                                 Entry                   user_entry2 = { 0 };
2223
2224                                 /* retry with the full entry... */
2225                                 bsi2.bsi_e = &user_entry2;
2226                                 rc = backsql_init_search( &bsi2,
2227                                                 &e->e_nname,
2228                                                 LDAP_SCOPE_BASE, 
2229                                                 (time_t)(-1), NULL,
2230                                                 dbh, op, rs, NULL,
2231                                                 BACKSQL_ISF_GET_ENTRY );
2232                                 if ( rc == LDAP_SUCCESS ) {
2233                                         if ( is_entry_referral( &user_entry2 ) )
2234                                         {
2235                                                 refs = get_entry_referrals( op,
2236                                                                 &user_entry2 );
2237                                         } else {
2238                                                 rs->sr_err = LDAP_OTHER;
2239                                         }
2240                                         backsql_entry_clean( op, &user_entry2 );
2241                                 }
2242                                 if ( bsi2.bsi_attrs != NULL ) {
2243                                         op->o_tmpfree( bsi2.bsi_attrs,
2244                                                         op->o_tmpmemctx );
2245                                 }
2246                         }
2247
2248                         if ( refs ) {
2249                                 rs->sr_ref = referral_rewrite( refs,
2250                                                 &e->e_name,
2251                                                 &op->o_req_dn,
2252                                                 op->ors_scope );
2253                                 ber_bvarray_free( refs );
2254                         }
2255
2256                         if ( rs->sr_ref ) {
2257                                 rs->sr_err = LDAP_REFERRAL;
2258
2259                         } else {
2260                                 rs->sr_text = "bad referral object";
2261                         }
2262
2263                         rs->sr_entry = e;
2264                         rs->sr_matched = user_entry.e_name.bv_val;
2265                         send_search_reference( op, rs );
2266
2267                         ber_bvarray_free( rs->sr_ref );
2268                         rs->sr_ref = NULL;
2269                         rs->sr_matched = NULL;
2270                         rs->sr_entry = NULL;
2271
2272                         goto next_entry;
2273                 }
2274
2275                 /*
2276                  * We use this flag since we need to parse the filter
2277                  * anyway; we should have used the frontend API function
2278                  * filter_has_subordinates()
2279                  */
2280                 if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
2281                         rc = backsql_has_children( op, dbh, &e->e_nname );
2282
2283                         switch ( rc ) {
2284                         case LDAP_COMPARE_TRUE:
2285                         case LDAP_COMPARE_FALSE:
2286                                 a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
2287                                 if ( a_hasSubordinate != NULL ) {
2288                                         for ( ap = &user_entry.e_attrs; 
2289                                                         *ap; 
2290                                                         ap = &(*ap)->a_next );
2291
2292                                         *ap = a_hasSubordinate;
2293                                 }
2294                                 rc = 0;
2295                                 break;
2296
2297                         default:
2298                                 Debug(LDAP_DEBUG_TRACE, 
2299                                         "backsql_search(): "
2300                                         "has_children failed( %d)\n", 
2301                                         rc, 0, 0 );
2302                                 rc = 1;
2303                                 goto next_entry;
2304                         }
2305                 }
2306
2307                 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
2308                         a_entryUUID = backsql_operational_entryUUID( bi, eid );
2309                         if ( a_entryUUID != NULL ) {
2310                                 if ( ap == NULL ) {
2311                                         ap = &user_entry.e_attrs;
2312                                 }
2313
2314                                 for ( ; *ap; ap = &(*ap)->a_next );
2315
2316                                 *ap = a_entryUUID;
2317                         }
2318                 }
2319
2320 #ifdef BACKSQL_SYNCPROV
2321                 if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
2322                         a_entryCSN = backsql_operational_entryCSN( op );
2323                         if ( a_entryCSN != NULL ) {
2324                                 if ( ap == NULL ) {
2325                                         ap = &user_entry.e_attrs;
2326                                 }
2327
2328                                 for ( ; *ap; ap = &(*ap)->a_next );
2329
2330                                 *ap = a_entryCSN;
2331                         }
2332                 }
2333 #endif /* BACKSQL_SYNCPROV */
2334
2335                 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
2336                 {
2337                         rs->sr_attrs = op->ors_attrs;
2338                         rs->sr_operational_attrs = NULL;
2339                         rs->sr_entry = e;
2340                         rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
2341                         /* FIXME: need the whole entry (ITS#3480) */
2342                         rs->sr_err = send_search_entry( op, rs );
2343                         rs->sr_entry = NULL;
2344                         rs->sr_attrs = NULL;
2345                         rs->sr_operational_attrs = NULL;
2346
2347                         switch ( rs->sr_err ) {
2348                         case LDAP_UNAVAILABLE:
2349                                 /*
2350                                  * FIXME: send_search_entry failed;
2351                                  * better stop
2352                                  */
2353                                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
2354                                         "connection lost\n", 0, 0, 0 );
2355                                 goto end_of_search;
2356
2357                         case LDAP_SIZELIMIT_EXCEEDED:
2358                                 goto send_results;
2359                         }
2360                 }
2361
2362 next_entry:;
2363                 if ( e == &user_entry ) {
2364                         backsql_entry_clean( op, &user_entry );
2365                 }
2366
2367 next_entry2:;
2368         }
2369
2370 end_of_search:;
2371         if ( rs->sr_nentries > 0 ) {
2372                 rs->sr_ref = rs->sr_v2ref;
2373                 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
2374                         : LDAP_REFERRAL;
2375
2376         } else {
2377                 rs->sr_err = bsi.bsi_status;
2378         }
2379
2380 send_results:;
2381         if ( rs->sr_err != SLAPD_ABANDON ) {
2382                 send_ldap_result( op, rs );
2383         }
2384
2385         /* cleanup in case of abandon */
2386         for ( ; eid != NULL; 
2387                         eid = backsql_free_entryID( op,
2388                                 eid, eid == &bsi.bsi_base_id ? 0 : 1 ) )
2389                 ;
2390
2391         backsql_entry_clean( op, &base_entry );
2392
2393         /* in case we got here accidentally */
2394         backsql_entry_clean( op, &user_entry );
2395
2396         if ( rs->sr_v2ref ) {
2397                 ber_bvarray_free( rs->sr_v2ref );
2398                 rs->sr_v2ref = NULL;
2399         }
2400
2401 #ifdef BACKSQL_SYNCPROV
2402         if ( op->o_sync ) {
2403                 Operation       op2 = *op;
2404                 SlapReply       rs2 = { 0 };
2405                 Entry           e = { 0 };
2406                 slap_callback   cb = { 0 };
2407
2408                 op2.o_tag = LDAP_REQ_ADD;
2409                 op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0, 0 );
2410                 op2.ora_e = &e;
2411                 op2.o_callback = &cb;
2412
2413                 e.e_name = op->o_bd->be_suffix[0];
2414                 e.e_nname = op->o_bd->be_nsuffix[0];
2415
2416                 cb.sc_response = slap_null_cb;
2417
2418                 op2.o_bd->be_add( &op2, &rs2 );
2419         }
2420 #endif /* BACKSQL_SYNCPROV */
2421
2422 done:;
2423         (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
2424
2425         if ( bsi.bsi_attrs != NULL ) {
2426                 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2427         }
2428
2429         if ( !BER_BVISNULL( &nbase )
2430                         && nbase.bv_val != op->o_req_ndn.bv_val )
2431         {
2432                 ch_free( nbase.bv_val );
2433         }
2434
2435         /* restore scope ... FIXME: this should be done before ANY
2436          * frontend call that uses op */
2437         if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
2438                 op->ors_scope = LDAP_SCOPE_BASE;
2439         }
2440
2441         Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n", 0, 0, 0 );
2442
2443         return rs->sr_err;
2444 }
2445
2446 /* return LDAP_SUCCESS IFF we can retrieve the specified entry.
2447  */
2448 int
2449 backsql_entry_get(
2450                 Operation               *op,
2451                 struct berval           *ndn,
2452                 ObjectClass             *oc,
2453                 AttributeDescription    *at,
2454                 int                     rw,
2455                 Entry                   **ent )
2456 {
2457         backsql_srch_info       bsi = { 0 };
2458         SQLHDBC                 dbh = SQL_NULL_HDBC;
2459         int                     rc;
2460         SlapReply               rs = { 0 };
2461         AttributeName           anlist[ 2 ];
2462
2463         *ent = NULL;
2464
2465         rc = backsql_get_db_conn( op, &dbh );
2466         if ( !dbh ) {
2467                 return LDAP_OTHER;
2468         }
2469
2470         if ( at ) {
2471                 anlist[ 0 ].an_name = at->ad_cname;
2472                 anlist[ 0 ].an_desc = at;
2473                 BER_BVZERO( &anlist[ 1 ].an_name );
2474         }
2475
2476         bsi.bsi_e = ch_malloc( sizeof( Entry ) );
2477         rc = backsql_init_search( &bsi,
2478                         ndn,
2479                         LDAP_SCOPE_BASE, 
2480                         (time_t)(-1), NULL,
2481                         dbh, op, &rs, at ? anlist : NULL,
2482                         BACKSQL_ISF_GET_ENTRY );
2483
2484         if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
2485                 (void)backsql_free_entryID( op, &bsi.bsi_base_id, 0 );
2486         }
2487
2488         if ( rc == LDAP_SUCCESS ) {
2489
2490 #if 0 /* not supported at present */
2491                 /* find attribute values */
2492                 if ( is_entry_alias( bsi.bsi_e ) ) {
2493                         Debug( LDAP_DEBUG_ACL,
2494                                 "<= backsql_entry_get: entry is an alias\n",
2495                                 0, 0, 0 );
2496                         rc = LDAP_ALIAS_PROBLEM;
2497                         goto return_results;
2498                 }
2499 #endif
2500
2501                 if ( is_entry_referral( bsi.bsi_e ) ) {
2502                         Debug( LDAP_DEBUG_ACL,
2503                                 "<= backsql_entry_get: entry is a referral\n",
2504                                 0, 0, 0 );
2505                         rc = LDAP_REFERRAL;
2506                         goto return_results;
2507                 }
2508
2509                 if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
2510                         Debug( LDAP_DEBUG_ACL,
2511                                         "<= backsql_entry_get: "
2512                                         "failed to find objectClass\n",
2513                                         0, 0, 0 ); 
2514                         rc = LDAP_NO_SUCH_ATTRIBUTE;
2515                         goto return_results;
2516                 }
2517
2518                 *ent = bsi.bsi_e;
2519         }
2520
2521 return_results:;
2522         if ( bsi.bsi_attrs != NULL ) {
2523                 op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
2524         }
2525
2526         if ( rc != LDAP_SUCCESS ) {
2527                 if ( bsi.bsi_e ) {
2528                         entry_free( bsi.bsi_e );
2529                 }
2530         }
2531
2532         return rc;
2533 }
2534
2535 void
2536 backsql_entry_clean(
2537                 Operation       *op,
2538                 Entry           *e )
2539 {
2540         void *ctx;
2541
2542         ctx = ldap_pvt_thread_pool_context();
2543
2544         if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
2545                 if ( !BER_BVISNULL( &e->e_name ) ) {
2546                         op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
2547                         BER_BVZERO( &e->e_name );
2548                 }
2549
2550                 if ( !BER_BVISNULL( &e->e_nname ) ) {
2551                         op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
2552                         BER_BVZERO( &e->e_nname );
2553                 }
2554         }
2555
2556         entry_clean( e );
2557 }
2558
2559 int
2560 backsql_entry_release(
2561                 Operation       *op,
2562                 Entry           *e,
2563                 int             rw )
2564 {
2565         backsql_entry_clean( op, e );
2566
2567         ch_free( e );
2568
2569         return 0;
2570 }