]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/search.c
wrap sql *.c files with #ifdef SLAPD_SQL to facilate NT builds
[openldap] / servers / slapd / back-sql / search.c
1 /*
2  *       Copyright 1999, Dmitry Kovalev (zmit@mail.ru), All rights reserved.
3  *
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.
8  */
9
10 #include "portable.h"
11
12 #ifdef SLAPD_SQL
13
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <string.h>
17 #include "slap.h"
18 #include "back-sql.h"
19 #include "sql-wrap.h"
20 #include "schema-map.h"
21 #include "entry-id.h"
22 #include "util.h"
23
24 int backsql_attrlist_add(backsql_srch_info *bsi,char *at_name)
25 {
26  char **p=bsi->attrs;
27  int n_attrs=0;
28
29  if (bsi->attrs==NULL)
30   return 1;
31
32  while(*p)
33  {
34   Debug(LDAP_DEBUG_TRACE,"==>backsql_attrlist_add(): attribute '%s' is in list\n",*p,0,0);
35   if (!strcasecmp(*p,at_name))
36    return 1;
37   n_attrs++;
38   p++;
39  }
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]=strdup(at_name);
43  bsi->attrs[n_attrs+1]=NULL;
44  return 1;
45 }
46
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,Backend *be,Connection *conn,Operation *op,char **attrs)
50 {
51  char **p;
52  bsi->base_dn=nbase;
53  bsi->scope=scope;
54  bsi->slimit=slimit;
55  bsi->tlimit=tlimit;
56  bsi->filter=filter;
57  bsi->dbh=dbh;
58  bsi->be=be;
59  bsi->conn=conn;
60  bsi->op=op;
61  if (attrs!=NULL)
62  {
63   bsi->attrs=(char**)ch_calloc(1,sizeof(char*));
64   bsi->attrs[0]=NULL;
65   for(p=attrs;*p!=NULL;p++)
66    backsql_attrlist_add(bsi,*p);
67  }
68  else
69   bsi->attrs=attrs;
70  bsi->abandon=0;
71  bsi->id_list=NULL;
72  bsi->stoptime=stoptime;
73  bsi->bi=bi;
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;
76 }
77
78 int backsql_process_filter_list(backsql_srch_info *bsi,Filter *f,int op)
79 {
80  char *sub_clause=NULL;
81  int len=0,res;
82
83  bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",NULL);
84  while(1)
85  {
86   res=backsql_process_filter(bsi,f);
87   
88   if (res==-1)
89         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," 1=0 ",NULL);
90
91   f=f->f_next;
92   if (f==NULL)
93    break;
94
95   switch (op)
96   {
97    case LDAP_FILTER_AND:
98                         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," AND ",NULL);
99                         break;
100    case LDAP_FILTER_OR:
101                         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len," OR ",NULL);
102                         break;
103   }
104  }
105
106  
107  bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,")",NULL);
108  return 1;
109 }
110
111 int backsql_process_sub_filter(backsql_srch_info *bsi,Filter *f)
112 {
113  int i;
114
115  backsql_at_map_rec *at=backsql_at_with_name(bsi->oc,f->f_sub_type);
116
117  bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,
118                                 " LIKE '",NULL);
119  if (f->f_sub_initial!=NULL)
120   bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_initial,NULL);
121
122  bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"%",NULL);
123
124  if (f->f_sub_any!=NULL)
125   for(i=0;f->f_sub_any[i]!=NULL;i++)
126    bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_any[i],"%",NULL);
127
128  if (f->f_sub_final!=NULL)
129   bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,f->f_sub_final,NULL);
130
131  bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"')",NULL);
132  
133  return 1;
134 }
135
136 int backsql_process_filter(backsql_srch_info *bsi,Filter *f)
137 {
138  backsql_at_map_rec *at;
139  backsql_at_map_rec oc_attr={"objectClass","","",NULL,NULL,NULL,NULL};
140  char *at_name=NULL;
141  int done=0,len=0;
142
143  Debug(LDAP_DEBUG_TRACE,"==>backsql_process_filter()\n",0,0,0);
144  switch(f->f_choice)
145  {
146   case LDAP_FILTER_OR:
147                         backsql_process_filter_list(bsi,f->f_or,LDAP_FILTER_OR);
148                         done=1;
149                         break;
150   case LDAP_FILTER_AND:
151                         backsql_process_filter_list(bsi,f->f_and,LDAP_FILTER_AND);
152                         done=1;
153                         break;
154   case LDAP_FILTER_NOT:
155                         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"NOT (",NULL);
156                         backsql_process_filter(bsi,f->f_not);
157                         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,")",NULL);
158                         done=1;
159                         break;
160   case LDAP_FILTER_PRESENT:
161                         at_name=f->f_type;
162                         break;
163   default:
164                         at_name=f->f_avtype;
165                         break;
166  }
167  
168  if (done)
169   goto done;
170
171  if (strcasecmp(at_name,"objectclass"))
172   at=backsql_at_with_name(bsi->oc,at_name);
173  else
174  {
175   at=&oc_attr;
176   at->sel_expr=backsql_strcat(at->sel_expr,&len,"'",bsi->oc->name,"'",NULL);
177  }
178  if (at==NULL)
179  {
180   Debug(LDAP_DEBUG_TRACE,"backsql_process_filter(): attribute '%s' is not defined for objectclass '%s'\n",
181                       at_name,bsi->oc->name,0);
182   return -1;
183  }
184                         
185  backsql_merge_from_clause(&bsi->from,&bsi->from_len,at->from_tbls);
186  //need to add this attribute to list of attrs to load, so that we could do test_filter() later
187  backsql_attrlist_add(bsi,at_name);
188
189  if (at->join_where != NULL && strstr(bsi->join_where,at->join_where)==NULL)
190   bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len," AND ",at->join_where,NULL);
191
192  //if (at!=&oc_attr)
193  // bsi->sel=backsql_strcat(bsi->sel,&bsi->sel_len,",",at->sel_expr," AS ",at->name,NULL);
194
195  switch(f->f_choice)
196  {
197   case LDAP_FILTER_EQUALITY:
198                         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,"='",
199                                                                                                                         f->f_avvalue.bv_val,"')",NULL);
200                         break;
201   case LDAP_FILTER_GE:
202                         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,">=",
203                                                                                                                         f->f_avvalue.bv_val,")",NULL);
204                         break;
205   case LDAP_FILTER_LE:
206                         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"(",at->sel_expr,"<=",
207                                                                                                                         f->f_avvalue.bv_val,")",NULL);
208                         break;
209   case LDAP_FILTER_PRESENT:
210                         bsi->flt_where=backsql_strcat(bsi->flt_where,&bsi->fwhere_len,"NOT (",at->sel_expr,
211                                                 " IS NULL)",NULL);
212                         break;
213   case LDAP_FILTER_SUBSTRINGS:
214                         backsql_process_sub_filter(bsi,f);
215                         break;
216  }
217
218 done:
219  if (oc_attr.sel_expr!=NULL)
220   free(oc_attr.sel_expr);
221  Debug(LDAP_DEBUG_TRACE,"<==backsql_process_filter()\n",0,0,0);
222  return 1;
223 }
224
225 char* backsql_srch_query(backsql_srch_info *bsi)
226 {
227  char *query=NULL;
228  int q_len=0;
229
230  Debug(LDAP_DEBUG_TRACE,"==>backsql_srch_query()\n",0,0,0);
231  bsi->sel=NULL;
232  bsi->from=NULL;
233  bsi->join_where=NULL;
234  bsi->flt_where=NULL;
235  bsi->sel_len=bsi->from_len=bsi->jwhere_len=bsi->fwhere_len=0;
236
237  bsi->sel=backsql_strcat(bsi->sel,&bsi->sel_len,
238                                 "SELECT ldap_entries.id,",bsi->oc->keytbl,".",bsi->oc->keycol,
239                                 ", '",bsi->oc->name,"' AS objectClass",
240                                 ", ldap_entries.dn AS dn",
241                                 NULL);
242  bsi->from=backsql_strcat(bsi->from,&bsi->from_len," FROM ldap_entries,",bsi->oc->keytbl,NULL);
243  bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len," WHERE ",
244          bsi->oc->keytbl,".",bsi->oc->keycol,"=ldap_entries.keyval AND ",
245          "ldap_entries.objclass=? AND ",NULL);
246
247  switch(bsi->scope)
248  {
249   case LDAP_SCOPE_BASE:
250                 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
251                                 "ldap_entries.dn=?",NULL);
252                 break;
253   case LDAP_SCOPE_ONELEVEL:
254                 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
255                                 "ldap_entries.parent=?",NULL);
256                 break;
257   case LDAP_SCOPE_SUBTREE:
258                 bsi->join_where=backsql_strcat(bsi->join_where,&bsi->jwhere_len,
259                                 bsi->bi->subtree_cond,NULL);
260                 break;
261  }
262  if (backsql_process_filter(bsi,bsi->filter))
263   query=backsql_strcat(query,&q_len,bsi->sel,bsi->from,bsi->join_where," AND ",bsi->flt_where,NULL);
264
265  
266  free(bsi->sel);
267  free(bsi->from);
268  free(bsi->join_where);
269  free(bsi->flt_where);
270  bsi->sel_len=bsi->from_len=bsi->jwhere_len=bsi->fwhere_len=0;
271  Debug(LDAP_DEBUG_TRACE,"<==backsql_srch_query()\n",0,0,0);
272  return query;
273 }
274
275 int backsql_oc_get_candidates(backsql_oc_map_rec *oc,backsql_srch_info *bsi)
276 {
277  char *query=NULL;
278  SQLHSTMT sth;
279  RETCODE rc;
280  backsql_entryID base_id,*res,*c_id;
281  //Entry *e;
282  BACKSQL_ROW_NTS row;
283  //int i;
284  
285  Debug(LDAP_DEBUG_TRACE,"==>backsql_oc_get_candidates(): oc='%s'\n",oc->name,0,0);
286  bsi->oc=oc;
287  query=backsql_srch_query(bsi);
288  if (query==NULL)
289  {
290   Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): could not construct query for objectclass\n",0,0,0);
291   return 1;
292  }
293
294  Debug(LDAP_DEBUG_TRACE,"Constructed query: %s\n",query,0,0);
295  if ((rc=backsql_Prepare(bsi->dbh,&sth,query,0)) != SQL_SUCCESS)
296   {
297    Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error preparing query\n",0,0,0);
298    backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
299    free(query);
300    return 1;
301   }
302  free(query);
303
304  if (backsql_BindParamID(sth,1,&bsi->oc->id) != SQL_SUCCESS)
305  {
306   Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding objectclass id parameter\n",0,0,0);
307   return 1;
308  }
309  switch(bsi->scope)
310  {
311   case LDAP_SCOPE_BASE:
312   case LDAP_SCOPE_SUBTREE:
313                 if ((rc=backsql_BindParamStr(sth,2,bsi->base_dn,BACKSQL_MAX_DN_LEN)) != SQL_SUCCESS)
314                 {
315          Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding base_dn parameter\n",0,0,0);
316                  backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
317          return 1;
318                 }
319                 break;
320   case LDAP_SCOPE_ONELEVEL:
321                 res=backsql_dn2id(&base_id,bsi->dbh,bsi->base_dn);
322                 if (res==NULL)
323                 {
324                  Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): could not retrieve base_dn id - no such entry\n",0,0,0);
325                  bsi->status=LDAP_NO_SUCH_OBJECT;
326                  return 0;
327                 }
328                 if (backsql_BindParamID(sth,2,&base_id.id) != SQL_SUCCESS)
329                 {
330                  Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error binding base id parameter\n",0,0,0);
331                  free(base_id.dn);
332                  return 1;
333                 }               
334                 free(base_id.dn);
335                 break;
336  }
337  
338  if ((rc=SQLExecute(sth)) != SQL_SUCCESS && rc!= SQL_SUCCESS_WITH_INFO)
339   {
340    Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): error executing query\n",0,0,0);
341    backsql_PrintErrors(bsi->bi->db_env,bsi->dbh,sth,rc);
342    SQLFreeStmt(sth,SQL_DROP);
343    return 1;
344   }
345
346  backsql_BindRowAsStrings(sth,&row);
347  while ((rc=SQLFetch(sth)) == SQL_SUCCESS || rc==SQL_SUCCESS_WITH_INFO)
348   {
349    /*
350    e=(Entry*)ch_calloc(1,sizeof(Entry)); 
351    for (i=1;i<row.ncols;i++)
352     {
353      if (row.is_null[i]>0)
354       {
355        backsql_entry_addattr(e,row.col_names[i],row.cols[i],row.col_prec[i]);
356 //       Debug(LDAP_DEBUG_TRACE,"prec=%d\n",(int)row.col_prec[i],0,0);
357       }
358     // else
359     //  Debug(LDAP_DEBUG_TRACE,"NULL value in this row for attribute '%s'\n",row.col_names[i],0,0);
360     }
361    */
362
363    Debug(LDAP_DEBUG_TRACE,"backsql_oc_get_candidates(): adding entry id=%s, keyval=%s dn='%s'\n",
364                 row.cols[0],row.cols[1],row.cols[3]);
365    c_id=(backsql_entryID*)ch_calloc(1,sizeof(backsql_entryID));
366    c_id->id=atoi(row.cols[0]);
367    c_id->keyval=atoi(row.cols[1]);
368    c_id->oc_id=bsi->oc->id;
369    c_id->dn=strdup(row.cols[3]);
370    c_id->next=bsi->id_list;
371    bsi->id_list=c_id;
372   }
373  backsql_FreeRow(&row);
374  SQLFreeStmt(sth,SQL_DROP);
375  Debug(LDAP_DEBUG_TRACE,"<==backsql_oc_get_candidates()\n",0,0,0);
376  return 1;
377 }
378
379
380 int backsql_search(Backend *be,Connection *conn,Operation *op,
381         char *base, char *nbase, int scope,int deref,int slimit,int tlimit,
382         Filter *filter, char *filterstr,char **attrs,int attrsonly)
383 {
384  backsql_info *bi=(backsql_info*)be->be_private;
385  SQLHDBC dbh;
386  int sres;
387  int nentries;
388  Entry entry,*res;
389  int manageDSAit = get_manageDSAit( op );
390  struct berval **v2refs = NULL;
391  time_t stoptime;
392  backsql_srch_info srch_info;
393  backsql_entryID *eid=NULL;
394
395  base=dn_validate(base);
396  Debug(LDAP_DEBUG_TRACE,"==>backsql_search(): base='%s', filter='%s', scope=%d,",
397                      base,filterstr,scope);
398  Debug(LDAP_DEBUG_TRACE," deref=%d, attrsonly=%d, attributes to load: %s\n",
399          deref,attrsonly,attrs==NULL?"all":"custom list");
400  dbh=backsql_get_db_conn(be,conn);
401
402  if (!dbh)
403  {
404   Debug(LDAP_DEBUG_TRACE,"backsql_search(): could not get connection handle - exiting\n",0,0,0);
405   send_ldap_result(conn,op,LDAP_OTHER,"","SQL-backend error",NULL,NULL);
406   return 1;
407  }
408  
409  if (tlimit == 0 && be_isroot(be,op->o_dn))
410   {
411    tlimit = -1; /* allow root to set no limit */
412   } 
413  else
414   {
415    tlimit = (tlimit > be->be_timelimit || tlimit < 1) ?
416                     be->be_timelimit : tlimit;
417    stoptime = op->o_time + tlimit;
418   }
419   
420  if (slimit == 0 && be_isroot(be,op->o_dn))
421   {
422    slimit = -1; /* allow root to set no limit */
423   }
424  else
425   {
426    slimit = (slimit > be->be_sizelimit || slimit < 1) ?
427                     be->be_sizelimit : slimit;
428   }
429
430  //backsql_init_search(&srch_info,bi,nbase/*!!!!!!!!*/,scope,slimit,tlimit,stoptime,filter,dbh,
431 //               be,conn,op,attrs);
432  backsql_init_search(&srch_info,bi,base/*don't know so far how to make Oracle do CIS search on VARCHAR2*/,
433                         scope,slimit,tlimit,stoptime,filter,dbh,
434          be,conn,op,attrs);
435
436  //for each objectclass we try to construct query which gets IDs
437  //of entries matching LDAP query filter and scope (or at least candidates),
438  //and get the IDs
439  avl_apply(bi->oc_by_name,(AVL_APPLY)backsql_oc_get_candidates,&srch_info,0,AVL_INORDER);
440              
441  nentries=0;
442  //now we load candidate entries (only those attrubutes mentioned in attrs and filter),
443  //test it against full filter and then send to client
444  for(eid=srch_info.id_list;eid!=NULL;eid=eid->next)
445   {
446    /* check for abandon */
447    ldap_pvt_thread_mutex_lock(&op->o_abandonmutex);
448    if (op->o_abandon)
449     {
450      ldap_pvt_thread_mutex_unlock(&op->o_abandonmutex);
451      break;
452     }
453    ldap_pvt_thread_mutex_unlock(&op->o_abandonmutex);
454
455    /* check time limit */
456    if ( tlimit != -1 && slap_get_time() > stoptime)
457     {
458          send_search_result( conn, op, LDAP_TIMELIMIT_EXCEEDED,
459                                 NULL, NULL, v2refs, NULL, nentries );
460      
461      break;
462     }
463      
464    Debug(LDAP_DEBUG_TRACE,"backsql_search(): loading data for entry id=%d, oc_id=%d, keyval=%d\n",
465                eid->id,eid->oc_id,eid->keyval);
466    
467    res=backsql_id2entry(&srch_info,&entry,eid);
468    if (res==NULL)
469     {
470      Debug(LDAP_DEBUG_TRACE,"backsql_search(): error in backsql_id2entry() - skipping entry\n",0,0,0);
471      continue;
472     }
473
474    if ( !manageDSAit && scope != LDAP_SCOPE_BASE &&
475                         is_entry_referral( &entry ) )
476     {
477      struct berval **refs = get_entry_referrals(be,conn,op,&entry);
478
479      send_search_reference( be, conn, op, &entry, refs, scope, NULL, &v2refs );
480      ber_bvecfree( refs );
481      continue;
482     }
483
484   // if (test_filter(be,conn,op,&entry,filter)==0)
485     {
486      if ((sres=send_search_entry(be,conn,op,&entry,attrs,attrsonly,NULL))==-1)
487       {
488        Debug(LDAP_DEBUG_TRACE,"backsql_search(): connection lost\n",0,0,0);
489        break;
490       }
491      nentries+=!sres;                                   
492     }
493   }
494
495  for(eid=srch_info.id_list;eid!=NULL;eid=backsql_free_entryID(eid));
496
497  //free bsi->attrs!!!!!!!!!!!!!!!!!!!!!!!!!!!
498
499  if (nentries>0)
500   send_search_result( conn, op,
501                 v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
502                 NULL, NULL, v2refs, NULL, nentries );
503  else
504   send_ldap_result(conn,op,LDAP_NO_SUCH_OBJECT,NULL,NULL,NULL,0);
505  
506  Debug(LDAP_DEBUG_TRACE,"<==backsql_search()\n",0,0,0);
507  return 0;
508 }
509
510 #endif /* SLAPD_SQL */