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