2 * Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
4 * Redistribution and use in source and binary forms are permitted only
5 * as authorized by the OpenLDAP Public License. A copy of this
6 * license is available at http://www.OpenLDAP.org/license.html or
7 * in file LICENSE in the top-level directory of the distribution.
15 #include <sys/types.h>
20 #include "schema-map.h"
24 int backsql_attrlist_add(backsql_srch_info *bsi,char *at_name)
34 Debug(LDAP_DEBUG_TRACE,"==>backsql_attrlist_add(): attribute '%s' is in list\n",*p,0,0);
35 if (!strcasecmp(*p,at_name))
40 Debug(LDAP_DEBUG_TRACE,"==>backsql_attrlist_add(): adding '%s' to list\n",at_name,0,0);
41 bsi->attrs=(char**)ch_realloc(bsi->attrs,(n_attrs+2)*sizeof(char*));
42 bsi->attrs[n_attrs]=ch_strdup(at_name);
43 bsi->attrs[n_attrs+1]=NULL;
47 void backsql_init_search(backsql_srch_info *bsi,backsql_info *bi,char *nbase,int scope,
48 int slimit,int tlimit,time_t stoptime,Filter *filter,
49 SQLHDBC dbh,BackendDB *be,Connection *conn,Operation *op,char **attrs)
63 bsi->attrs=(char**)ch_calloc(1,sizeof(char*));
65 for(p=attrs;*p!=NULL;p++)
66 backsql_attrlist_add(bsi,*p);
72 bsi->stoptime=stoptime;
74 bsi->sel=NULL; bsi->from=NULL; bsi->join_where=NULL; bsi->flt_where=NULL;
75 bsi->sel_len=0; bsi->from_len=0; bsi->jwhere_len=0; bsi->fwhere_len=0;
78 int backsql_process_filter_list(backsql_srch_info *bsi,Filter *f,int op)
80 char *sub_clause=NULL;
85 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",NULL);
88 res=backsql_process_filter(bsi,f);
90 return -1; /* TimesTen : If the query has no answers,
91 don't bother to run the query. */
100 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," AND ",NULL);
103 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," OR ",NULL);
109 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,")",NULL);
113 int backsql_process_sub_filter(backsql_srch_info *bsi,Filter *f)
116 backsql_at_map_rec *at=backsql_at_with_name(bsi->oc,f->f_sub_desc->ad_cname->bv_val);
121 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",NULL);
123 Debug(LDAP_DEBUG_TRACE,"expr: '%s' '%s'\n",at->sel_expr,
124 at->sel_expr_u?at->sel_expr_u:"<NULL>",0);
125 if (bsi->bi->upper_func)
127 /* If a pre-upper-cased version of the column exists, use it. */
128 if (at->sel_expr_u) {
129 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,
130 at->sel_expr_u," LIKE '",NULL);
133 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,
134 bsi->bi->upper_func,"(",at->sel_expr,")",
140 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,at->sel_expr,
143 if (f->f_sub_initial!=NULL)
145 if (bsi->bi->upper_func)
147 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,ldap_pvt_str2upper(f->f_sub_initial->bv_val),NULL);
150 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_initial->bv_val,NULL);
153 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"%",NULL);
155 if (f->f_sub_any!=NULL)
156 for(i=0;f->f_sub_any[i]!=NULL;i++)
158 //Debug(LDAP_DEBUG_TRACE,"==>backsql_process_sub_filter(): sub_any='%s'\n",f->f_sub_any[i]->bv_val,0,0);
159 if (bsi->bi->upper_func)
161 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,ldap_pvt_str2upper(f->f_sub_any[i]->bv_val),"%",NULL);
164 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_any[i]->bv_val,"%",NULL);
167 if (f->f_sub_final!=NULL)
168 if (bsi->bi->upper_func)
170 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,ldap_pvt_str2upper(f->f_sub_final->bv_val),NULL);
173 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_final->bv_val,NULL);
175 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"')",NULL);
180 int backsql_process_filter(backsql_srch_info *bsi,Filter *f)
182 backsql_at_map_rec *at;
183 backsql_at_map_rec oc_attr={"objectClass","","",NULL,NULL,NULL,NULL};
186 int rc=0; // TimesTen
188 Debug(LDAP_DEBUG_TRACE,"==>backsql_process_filter()\n",0,0,0);
189 if (f==NULL || f->f_choice==SLAPD_FILTER_COMPUTED)
197 rc = backsql_process_filter_list(bsi,f->f_or,LDAP_FILTER_OR);
200 case LDAP_FILTER_AND:
201 rc = backsql_process_filter_list(bsi,f->f_and,LDAP_FILTER_AND);
204 case LDAP_FILTER_NOT:
205 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"NOT (",NULL);
206 rc = backsql_process_filter(bsi,f->f_not);
207 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,")",NULL);
210 case LDAP_FILTER_PRESENT:
211 at_name=f->f_desc->ad_cname->bv_val;
214 at_name=f->f_av_desc->ad_cname->bv_val;
219 goto impossible; /* TimesTen : Don't run the query */
224 if (strcasecmp(at_name,"objectclass"))
225 at=backsql_at_with_name(bsi->oc,at_name);
229 at->sel_expr=backsql_strcat(at->sel_expr,&len,"'",bsi->oc->name,"'",NULL);
233 Debug(LDAP_DEBUG_TRACE,"backsql_process_filter(): attribute '%s' is not defined for objectclass '%s'\n",
234 at_name,bsi->oc->name,0);
235 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," 1=0 ",NULL);
239 backsql_merge_from_clause(&bsi->from,&bsi->from_len,at->from_tbls);
240 //need to add this attribute to list of attrs to load, so that we could do test_filter() later
241 backsql_attrlist_add(bsi,at_name);
243 if (at->join_where != NULL && strstr(bsi->join_where,at->join_where)==NULL)
244 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len," AND ",at->join_where,NULL);
247 // bsi->sel=backsql_strcat(bsi->sel,&bsi->sel_len,",",at->sel_expr," AS ",at->name,NULL);
251 case LDAP_FILTER_EQUALITY:
252 //maybe we should check type of at->sel_expr here somehow,
253 //to know whether upper_func is applicable, but for now
254 //upper_func stuff is made for Oracle, where UPPER is
255 //safely applicable to NUMBER etc.
256 if (bsi->bi->upper_func) {
258 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",
260 ldap_pvt_str2upper(f->f_av_value->bv_val),"')",
263 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",
264 bsi->bi->upper_func,"(",at->sel_expr,")='",
265 ldap_pvt_str2upper(f->f_av_value->bv_val),"')",NULL);
268 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,"='",
269 f->f_av_value->bv_val,"')",NULL);
273 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,">=",
274 f->f_av_value->bv_val,")",NULL);
278 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,"<=",
279 f->f_av_value->bv_val,")",NULL);
281 case LDAP_FILTER_PRESENT:
282 bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"NOT (",at->sel_expr,
285 case LDAP_FILTER_SUBSTRINGS:
286 backsql_process_sub_filter(bsi,f);
291 if (oc_attr.sel_expr!=NULL)
292 free(oc_attr.sel_expr);
293 Debug(LDAP_DEBUG_TRACE,"<==backsql_process_filter()\n",0,0,0);
297 if (oc_attr.sel_expr!=NULL)
298 free(oc_attr.sel_expr);
299 Debug(LDAP_DEBUG_TRACE,"<==backsql_process_filter() returns -1\n",0,0,0);
304 char* backsql_srch_query(backsql_srch_info *bsi)
310 Debug(LDAP_DEBUG_TRACE,"==>backsql_srch_query()\n",0,0,0);
313 bsi->join_where=NULL;
315 bsi->sel_len=bsi->from_len=bsi->jwhere_len=bsi->fwhere_len=0;
317 bsi->sel=backsql_strcat(bsi->sel,&bsi->sel_len,
318 "SELECT DISTINCT ldap_entries.id,",bsi->oc->keytbl,".",bsi->oc->keycol,
319 ", '",bsi->oc->name,"' AS objectClass",
320 ", ldap_entries.dn AS dn",
322 bsi->from=backsql_strcat(bsi->from,&bsi->from_len," FROM ldap_entries,",bsi->oc->keytbl,NULL);
323 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len," WHERE ",
324 bsi->oc->keytbl,".",bsi->oc->keycol,"=ldap_entries.keyval AND ",
325 "ldap_entries.oc_map_id=? AND ",NULL);
329 case LDAP_SCOPE_BASE:
330 if (bsi->bi->upper_func)
332 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
333 bsi->bi->upper_func,"(","ldap_entries.dn)=(?)",NULL);
337 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
338 "ldap_entries.dn=?",NULL);
341 case LDAP_SCOPE_ONELEVEL:
342 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
343 "ldap_entries.parent=?",NULL);
345 case LDAP_SCOPE_SUBTREE:
346 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
347 bsi->bi->subtree_cond,NULL);
351 rc = backsql_process_filter(bsi, bsi->filter);
353 query=backsql_strcat(query,&q_len,bsi->sel,bsi->from,bsi->join_where," AND ",bsi->flt_where,NULL);
356 /* Indicates that there's no possible way the filter matches
357 anything. No need to issue the query. */
359 Debug(LDAP_DEBUG_TRACE,"<==backsql_srch_query() returns NULL\n",0,0,0);
366 free(bsi->join_where);
367 free(bsi->flt_where);
368 bsi->sel_len=bsi->from_len=bsi->jwhere_len=bsi->fwhere_len=0;
369 Debug(LDAP_DEBUG_TRACE,"<==backsql_srch_query()\n",0,0,0);
373 int backsql_oc_get_candidates(backsql_oc_map_rec *oc,backsql_srch_info *bsi)
378 backsql_entryID base_id,*res,*c_id;
383 char temp_base_dn[BACKSQL_MAX_DN_LEN+1]; // TimesTen
385 Debug(LDAP_DEBUG_TRACE,"==>backsql_oc_get_candidates(): oc='%s'\n",oc->name,0,0);
387 query=backsql_srch_query(bsi);
390 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): could not construct query for objectclass\n",0,0,0);
394 Debug(LDAP_DEBUG_TRACE,"Constructed query: %s\n",query,0,0);
396 if ((rc=backsql_Prepare(bsi->dbh,&sth,query,0)) != SQL_SUCCESS)
398 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error preparing query\n",0,0,0);
399 backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
405 if (backsql_BindParamID(sth,1,&bsi->oc->id) != SQL_SUCCESS)
407 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding objectclass id parameter\n",0,0,0);
412 case LDAP_SCOPE_BASE:
413 if ((rc=backsql_BindParamStr(sth,2,bsi->base_dn,BACKSQL_MAX_DN_LEN)) != SQL_SUCCESS)
415 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding base_dn parameter\n",0,0,0);
416 backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
421 case LDAP_SCOPE_SUBTREE:
422 /* Sets the parameters for the SQL built earlier */
423 /* NOTE that all the databases could actually use the TimesTen version,
424 which would be cleaner and would also eliminate the need for the
425 subtree_cond line in the configuration file. For now, I'm leaving
426 it the way it is, so non-TimesTen databases use the original code.
427 But at some point this should get cleaned up. */
428 /* If "dn" is being used, do a suffix search.
429 If "dn_ru" is being used, do a prefix search. */
431 if (bsi->bi->has_ldapinfo_dn_ru) {
432 temp_base_dn[0] = '\0';
433 for ((i=0, j=strlen(bsi->base_dn)-1); j >= 0; (i++, j--)) {
434 *(temp_base_dn+i) = toupper(*(bsi->base_dn+j));
436 *(temp_base_dn+i) = '%';
437 *(temp_base_dn+i+1) = '\0';
440 strcpy(temp_base_dn, "%");
441 for (i = 0; *(bsi->base_dn+i); i++) {
442 *(temp_base_dn+i+1) = toupper(*(bsi->base_dn+i));
444 *(temp_base_dn+i+1) = '\0';
446 Debug(LDAP_DEBUG_TRACE, "dn '%s'\n", temp_base_dn, 0, 0);
448 if ((rc=backsql_BindParamStr(sth,2,temp_base_dn,BACKSQL_MAX_DN_LEN)) !=
451 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding base
452 _dn parameter (2)\n",0,0,0);
453 backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
458 case LDAP_SCOPE_ONELEVEL:
459 res=backsql_dn2id(bsi->bi,&base_id,bsi->dbh,bsi->base_dn);
462 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): could not retrieve base_dn id - no such entry\n",0,0,0);
463 bsi->status=LDAP_NO_SUCH_OBJECT;
466 if (backsql_BindParamID(sth,2,&base_id.id) != SQL_SUCCESS)
468 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding base id parameter\n",0,0,0);
476 if ((rc=SQLExecute(sth)) != SQL_SUCCESS && rc!= SQL_SUCCESS_WITH_INFO)
478 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error executing query\n",0,0,0);
479 backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
480 SQLFreeStmt(sth,SQL_DROP);
484 backsql_BindRowAsStrings(sth,&row);
485 while ((rc=SQLFetch(sth)) == SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO)
488 e=(Entry*)ch_calloc(1,sizeof(Entry));
489 for (i=1;i<row.ncols;i++)
491 if (row.is_null[i]>0)
493 backsql_entry_addattr(e,row.col_names[i],row.cols[i],row.col_prec[i]);
494 // Debug(LDAP_DEBUG_TRACE,"prec=%d\n",(int)row.col_prec[i],0,0);
497 // Debug(LDAP_DEBUG_TRACE,"NULL value in this row for attribute '%s'\n",row.col_names[i],0,0);
501 c_id=(backsql_entryID*)ch_calloc(1,sizeof(backsql_entryID));
502 c_id->id=atoi(row.cols[0]);
503 c_id->keyval=atoi(row.cols[1]);
504 c_id->oc_id=bsi->oc->id;
505 c_id->dn=ch_strdup(row.cols[3]);
506 c_id->next=bsi->id_list;
508 Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): added entry id=%d, keyval=%d dn='%s'\n",
509 c_id->id,c_id->keyval,row.cols[3]);
511 backsql_FreeRow(&row);
512 SQLFreeStmt(sth,SQL_DROP);
514 Debug(LDAP_DEBUG_TRACE,"<==backsql_oc_get_candidates()\n",0,0,0);
518 int backsql_search(BackendDB *be,Connection *conn,Operation *op,
519 const char *base, const char *nbase, int scope,int deref,int slimit,int tlimit,
520 Filter *filter, const char *filterstr,char **attrs,int attrsonly)
522 backsql_info *bi=(backsql_info*)be->be_private;
527 int manageDSAit = get_manageDSAit( op );
528 struct berval **v2refs = NULL;
530 backsql_srch_info srch_info;
531 backsql_entryID *eid=NULL;
533 Debug(LDAP_DEBUG_TRACE,"==>backsql_search(): base='%s', filter='%s', scope=%d,",
534 nbase,filterstr,scope);
535 Debug(LDAP_DEBUG_TRACE," deref=%d, attrsonly=%d, attributes to load: %s\n",
536 deref,attrsonly,attrs==NULL?"all":"custom list");
537 dbh=backsql_get_db_conn(be,conn);
541 Debug(LDAP_DEBUG_TRACE,"backsql_search(): could not get connection handle - exiting\n",0,0,0);
542 send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
546 /* TimesTen : Pass it along to the lower level routines */
547 srch_info.isTimesTen = bi->isTimesTen;
549 if (tlimit == 0 && be_isroot(be,op->o_dn))
551 tlimit = -1; /* allow root to set no limit */
555 tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
556 be->be_timelimit : tlimit;
557 stoptime = op->o_time + tlimit;
560 if (slimit == 0 && be_isroot(be,op->o_dn))
562 slimit = -1; /* allow root to set no limit */
566 slimit = (slimit > be->be_sizelimit || slimit < 1) ?
567 be->be_sizelimit : slimit;
570 backsql_init_search(&srch_info,bi,(char*)nbase,scope,slimit,tlimit,stoptime,filter,dbh,
573 //for each objectclass we try to construct query which gets IDs
574 //of entries matching LDAP query filter and scope (or at least candidates),
576 avl_apply(bi->oc_by_name,(AVL_APPLY)backsql_oc_get_candidates,&srch_info,0,AVL_INORDER);
579 //now we load candidate entries (only those attrubutes mentioned in attrs and filter),
580 //test it against full filter and then send to client
581 for(eid=srch_info.id_list;eid!=NULL;eid=eid->next)
583 /* check for abandon */
584 ldap_pvt_thread_mutex_lock(&op->o_abandonmutex);
587 ldap_pvt_thread_mutex_unlock(&op->o_abandonmutex);
590 ldap_pvt_thread_mutex_unlock(&op->o_abandonmutex);
592 /* check time limit */
593 if ( tlimit != -1 && slap_get_time() > stoptime)
595 send_search_result( conn, op, LDAP_TIMELIMIT_EXCEEDED,
596 NULL, NULL, v2refs, NULL, nentries );
601 Debug(LDAP_DEBUG_TRACE,"backsql_search(): loading data for entry id=%d, oc_id=%d, keyval=%d\n",
602 eid->id,eid->oc_id,eid->keyval);
604 entry=(Entry *)ch_calloc(sizeof(Entry),1);
605 res=backsql_id2entry(&srch_info,entry,eid);
608 Debug(LDAP_DEBUG_TRACE,"backsql_search(): error in backsql_id2entry() - skipping entry\n",0,0,0);
612 if ( !manageDSAit && scope != LDAP_SCOPE_BASE &&
613 is_entry_referral( entry ) )
615 struct berval **refs = get_entry_referrals(be,conn,op,entry);
617 send_search_reference( be, conn, op, entry, refs, scope, NULL, &v2refs );
618 ber_bvecfree( refs );
622 if (test_filter(be,conn,op,entry,filter)==LDAP_COMPARE_TRUE)
624 if ((sres=send_search_entry(be,conn,op,entry,attrs,attrsonly,NULL))==-1)
626 Debug(LDAP_DEBUG_TRACE,"backsql_search(): connection lost\n",0,0,0);
634 for(eid=srch_info.id_list;eid!=NULL;eid=backsql_free_entryID(eid));
636 charray_free(srch_info.attrs);
639 send_search_result( conn, op,
640 v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
641 NULL, NULL, v2refs, NULL, nentries );
643 send_ldap_result(conn,op,LDAP_NO_SUCH_OBJECT,NULL,NULL,NULL,0);
645 Debug(LDAP_DEBUG_TRACE,"<==backsql_search()\n",0,0,0);
649 #endif /* SLAPD_SQL */