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