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