1    | /***************************************
2    |   $Revision: 1.83 $
3    | 
4    |   Query instructions (qi).  This is where the queries are executed.
5    | 
6    |   Status: NOT REVUED, TESTED
7    | 
8    |   ******************/ /******************
9    |   Filename            : query_instructions.c
10   |   Authors             : ottrey@ripe.net - framework and draft implementation
11   |                         marek@ripe.net - cleaned and extended, added referral,
12   | 			accounting support and watchdog cancellation.
13   |   OSs Tested          : Solaris
14   |   ******************/ /******************
15   |   Copyright (c) 1999                              RIPE NCC
16   |  
17   |   All Rights Reserved
18   |   
19   |   Permission to use, copy, modify, and distribute this software and its
20   |   documentation for any purpose and without fee is hereby granted,
21   |   provided that the above copyright notice appear in all copies and that
22   |   both that copyright notice and this permission notice appear in
23   |   supporting documentation, and that the name of the author not be
24   |   used in advertising or publicity pertaining to distribution of the
25   |   software without specific, written prior permission.
26   |   
27   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
28   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
29   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
30   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
31   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33   |   ***************************************/
34   | #include <stdio.h>
35   | #include <string.h>
36   | #include <glib.h>
37   | 
38   | #include "which_keytypes.h"
39   | #include "query_instructions.h"
40   | #include "mysql_driver.h"
41   | #include "rp.h"
42   | #include "stubs.h"
43   | #include "constants.h"
44   | #include "memwrap.h"
45   | #include "wh_queries.h"
46   | #include "ud.h"
47   | 
48   | #include "defs.h"
49   | 
50   | /*+ String sizes +*/
51   | #define STR_S   63
52   | #define STR_M   255
53   | #define STR_L   1023
54   | #define STR_XL  4095
55   | #define STR_XXL 16383
56   | 
57   | /* the TEMPORARY table is only available in 3.23 or later MySQL */
58   | #if MYSQL_VERSION_ID >= 32300
59   | #define USE_TEMPORARY_TABLE 
60   | #endif
61   | 
62   | /* string to stick in CREATE TABLE statments */
63   | #ifdef USE_TEMPORARY_TABLE
64   | #define TEMPORARY "TEMPORARY"
65   | #else
66   | #define TEMPORARY ""
67   | #endif
68   | 
69   | /*++++++++++++++++++++++++++++++++++++++
70   |   Function invoked on query cancellation by the watchdog,
71   |   used from the sql_execute_watched() function.
72   |   
73   |   It aborts the running query (the abort function in sq kills and
74   |   reestablished the connection).
75   | 
76   |   void *qi_kill_body         result of sq_execute_query, int cast to (void*)
77   | 
78   |   void *arg                  pointer to sql connection
79   | 
80   |   Author:
81   |     marek.
82   |   
83   |   ++++++++++++++++++++++++++++++++++++++*/
84   | static
85   | void *qi_kill_body(void *arg)
86   | {
87   |   SQ_connection_t *sql_connection = arg;
88   |   ER_dbg_va(FAC_QI, ASP_QI_WATCH,
89   | 	      "rtc: killing SQL connection %d", (sql_connection)->thread_id);
90   |   /* abort the running query */
91   |   SQ_abort_query(sql_connection);
92   | 
93   |   return NULL;
94   | }
95   | 
96   | 
97   | 
98   | /*++++++++++++++++++++++++++++++++++++++
99   |   wrapper around sq_execute_query: starts a query 
100  |   in a separate thread and starts the socket watchdog to cancel the query 
101  |   if the socket is closed. If the socket has problems already (its 
102  |   reason-to-close flag is set) no query is attempted.
103  |   
104  |   The execution of the query or watchdog is not guaranteed at all!
105  | 
106  |   int sql_execute_watched        Returns the return code of SQ_execute_query,
107  |                                  Returns 0 for cancelled queries.
108  | 
109  |   sk_conn_st *condat             connection to watch
110  | 
111  |   SQ_connection_t **sql_connection  sql connection
112  | 
113  |   const char *query                 sql query to execute
114  | 
115  |   SQ_result_set_t **result_ptr      storage for the query result structure
116  |                                     (passed to SQ_execute_query). Must either
117  | 				    be NULL, or the pointer it points to must
118  | 				    be NULL - in that case the result struct.
119  | 				    will be allocated in SQ.
120  | 
121  |   Author:
122  |     marek.
123  |   ++++++++++++++++++++++++++++++++++++++*/
124  | int sql_execute_watched(sk_conn_st *condat, SQ_connection_t **sql_connection, 
125  | 			const char *query, SQ_result_set_t **result_ptr)
126  | {
127  |   int retval = 0; /* return value of sq_execute_query */
128  |   SQ_connection_t *tempcon;
129  | 
130  |   /* assert that, if defined, result_ptr is initialised to NULL 
131  |      prior to calling this function */
132  |   if( result_ptr != NULL ) {
133  |     dieif( *result_ptr != NULL );
134  |   }
135  | 
136  |   /* don't even try to perform the query/fire up watchdog
137  |      if rtc is already set. Do this only if not set yet. */
138  |   if( condat->rtc == 0 ) {
139  |     
140  |     /* make clean */
141  |     SK_watch_setclear(condat);
142  |     
143  |     /* set watchdog to execute the abort function */
144  |     SK_watch_setexec(condat, qi_kill_body, *sql_connection);
145  |     
146  |     /* start the watchdog */
147  |     SK_watchstart(condat);
148  |     
149  |     /* start query. An error may be returned if the query is aborted */
150  |     retval = SQ_execute_query(*sql_connection, query, result_ptr);
151  |     
152  |     /* but short queries will complete before the watchdog kills the
153  |        connection */
154  |     
155  |     SK_watchstop(condat);
156  |     
157  | 
158  |     /* if the watchdog triggered, then it is guaranteed that
159  |        the kill_body function was invoked and therefore the sql-connection
160  |        is now unusable... 
161  |        Close and reopen it for cleanup, use temporary connection
162  |        to keep the login details */
163  |     if( condat->rtc != 0 ) {
164  |       /* can't rely on the error code from mysql!
165  |        */ 
166  |     
167  |       /* one thing: this code must be entered ONLY if the kill_body
168  | 	 thing was invoked by the watchdog. 
169  |       */
170  |     
171  |       /* if result is defined, free it here before destroying the 
172  | 	 associated connection */
173  |       if( retval == 0 && result_ptr && *result_ptr ) {
174  | 	SQ_free_result( *result_ptr );
175  | 	*result_ptr = NULL;
176  |       }
177  |     
178  |       tempcon = SQ_duplicate_connection(*sql_connection);
179  |     
180  |       ER_dbg_va(FAC_QI, ASP_QI_WATCH,
181  | 		"rtc: closing SQL thread %d", (*sql_connection)->thread_id);
182  |       SQ_close_connection(*sql_connection);
183  |     
184  |       *sql_connection = tempcon;
185  |       ER_dbg_va(FAC_QI, ASP_QI_WATCH,
186  | 		"rtc: reopened as thread %d", (*sql_connection)->thread_id);
187  |     
188  |       /* make it look as if there was no error and 
189  | 	 the result is empty */
190  |       retval = 0;
191  |     } /* if watchdog set rtc */
192  |   
193  |   } /* if rtc not set before */
194  | 
195  |   return retval; 
196  | }
197  | 
198  | /* create_name_query() */
199  | /*++++++++++++++++++++++++++++++++++++++
200  |   Create an sql query for the names table. 
201  | 
202  |   char *query_str
203  | 
204  |   const char *sql_query
205  | 
206  |   const char *keys
207  |    
208  |   More:
209  |   +html+ <PRE>
210  |   Authors:
211  |     ottrey
212  |   +html+ </PRE>
213  |   ++++++++++++++++++++++++++++++++++++++*/
214  | static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
215  |   int i;
216  |   /* Allocate stuff - use dynamic strings (initialised to some length) */
217  |   GString *from_clause = g_string_sized_new(STR_L);
218  |   GString *where_clause = g_string_sized_new(STR_L);
219  |   gchar **words = g_strsplit(keys, " ", 0);
220  | 
221  |   /* double quotes " are used in queries to allow querying for 
222  |      names like O'Hara */
223  | 
224  |   g_string_sprintfa(from_clause, "names N%.2d", 0);
225  |   g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
226  | 
227  |   for (i=1; words[i] != NULL; i++) {
228  |     g_string_sprintfa(from_clause, ", names N%.2d", i);
229  |     g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i);
230  |   }
231  | 
232  |   sprintf(query_str, sql_query, from_clause->str, where_clause->str);
233  | 
234  |   /* Free up stuff */
235  |   g_strfreev(words);
236  |   g_string_free(where_clause,/* CONSTCOND */ TRUE);
237  |   g_string_free(from_clause, /* CONSTCOND */ TRUE);
238  | 
239  | } /* create_name_query() */
240  | 
241  | 
242  | /*++++++++++++++++++++++++++++++++++++++
243  |   construct a range query for the as_block table
244  |   (a query for an AS block object) given a string like: 
245  |   AS1
246  |   AS1 - AS10
247  |   AS1-AS10
248  | 
249  |   int create_asblock_query    Returns 0 on success, -1 on failure 
250  |                              (search term not an AS# nor range)
251  | 
252  |   char *query_str             buffer for the final query (must be big enough)
253  | 
254  |   const char *sql_query       rest of the sql query (with %d %d formats for
255  |                               AS numbers)
256  | 
257  |   const char *keys            user-supplied search term.
258  | 
259  |   Author:
260  |     marek
261  |   ++++++++++++++++++++++++++++++++++++++*/
262  | static int create_asblock_query(char *query_str, 
263  | 				const char *sql_query, 
264  | 				const char *keys) {
265  |   char *keycopy = wr_string(keys);
266  |   char *token, *cursor = keycopy;
267  |   int  asnums[2] = {0,0};
268  |   int index = 0; /* index into the asnums array */
269  | 
270  | 
271  |   while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {  
272  |     /* discard the letters (or leading whitespace), take the number */
273  |     if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
274  |       return -1; /* error */
275  |     }
276  |   }
277  |   /* if only beginning was supplied, copy it as end */
278  |   if( index == 1 ) {
279  |     asnums[1] = asnums[0];
280  |   }
281  |   
282  |   /* now construct the query */
283  |   sprintf(query_str, sql_query, asnums[0], asnums[1]);
284  | 
285  |   wr_free(keycopy);
286  |   return 0;
287  | }
288  | 
289  | 
290  | /*++++++++++++++++++++++++++++++++++++++
291  |   add_filter(): construct a query to limit the objects returned from the last
292  |   table to predefined types. 
293  | 
294  |   char *query_str           buffer for the final query, containing the initial
295  |                             part of the query (must be big enough)
296  | 
297  |   const Query_command *qc   query command structure with the bitmap of 
298  |                             object types to be included.
299  | 			    
300  |   Author:
301  |     ottrey.
302  |   ++++++++++++++++++++++++++++++++++++++*/
303  | static void add_filter(char *query_str, const Query_command *qc) {
304  |   unsigned i;
305  |   int qlen;
306  |   char filter_atom[STR_M];
307  | 
308  | #if 0
309  |   /* glib string manipulation - untested yet */
310  |   
311  |     if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) { 
312  |       g_string_sprintfa(query_str, " AND (");
313  |       for (i=0; i < C_END; i++) {
314  | 	if (MA_isset(qc->object_type_bitmap, i)) {
315  | 	  g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
316  | 	}
317  |       }
318  |       g_string_truncate(query_str, query_str->len-3);
319  |       g_string_append_c(query_str, ')');
320  |     }
321  | 
322  | #else /* classic string operations */
323  | 
324  |   /* add filters only if any bits are 0 (the number of 1's is < MAX_MAX */
325  |   if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) { 
326  |     strcat(query_str, " AND (");
327  |     for (i=0; i < C_END; i++) {
328  |       if (MA_isset(qc->object_type_bitmap, i)) {
329  |         strcpy(filter_atom, "");
330  |         sprintf(filter_atom, "i.object_type = %d OR ", i);
331  | 	                /* XXX class codes should be used instead:
332  | 			   DF_get_class_dbase_code(i)) 
333  | 			   but currently the tables contain values of enums
334  | 			   (C_IN, etc) and not codes
335  | 			*/
336  |         strcat(query_str, filter_atom);
337  |       }
338  |     }
339  |     qlen = strlen(query_str);
340  |     query_str[qlen-3] = ')';
341  |     query_str[qlen-2] = '\0';
342  |     query_str[qlen-1] = '\0';
343  |   }
344  | 
345  | #endif
346  |   
347  | } /* add_filter() */
348  | 
349  | /* create_query() */
350  | /*++++++++++++++++++++++++++++++++++++++
351  |   Create an sql query from the query_command and the matching keytype and the
352  |   selected inverse attributes.
353  |   Note this clears the first inv_attribute it sees, so is called sequentially
354  |   until there are no inv_attributes left.
355  | 
356  |   WK_Type keytype The matching keytype.
357  | 
358  |   const Query_command *qc The query command.
359  | 
360  |   mask_t *inv_attrs_bitmap The selected inverse attributes.
361  |    
362  |   More:
363  |   +html+ <PRE>
364  |   Authors:
365  |         ottrey
366  |   +html+ </PRE>
367  | 
368  |   ++++++++++++++++++++++++++++++++++++++*/
369  | static char *create_query(const Query_t q, const Query_command *qc) {
370  |   char *result=NULL;
371  |   char result_buff[STR_XL];
372  |   Q_Type_t querytype;
373  |   int addquery = 0; /* controls if the query should be added to the list */
374  | 
375  |   if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
376  |     querytype = Q_INVERSE;
377  |   }
378  |   else {
379  |     querytype = Q_LOOKUP;
380  |   }
381  | 
382  |   if ( (q.query != NULL) 
383  |     && (q.querytype == querytype) ) {
384  |     
385  |     /* addquery = 1; */
386  |     /* if it got here, it should be added, unless.(see asblock)*/
387  |     
388  |     if (q.keytype == WK_NAME) { 
389  |       /* Name queries require special treatment. */
390  |       create_name_query(result_buff, q.query, qc->keys);
391  |       addquery = 1;
392  |     }
393  |     else if( q.keytype == WK_IPADDRESS ) {  /* ifaddr sql lookups */
394  |       ip_range_t myrang;
395  |       unsigned   begin, end;
396  |       ip_keytype_t key_type;
397  |       
398  |       /* The only inverse query for IPADDRESS is nserver. */
399  |       /* We need to insure that we don't try to use the numeric values for this
400  |        * query, because the address of the server is stored as a string, and
401  |        * the SQL query is formatted appropriately. */
402  |       if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
403  |         if(IP_rang_b2_space(&myrang) == IP_V4 ) {
404  | 	  IP_rang_b2v4(&myrang, &begin, &end);
405  | 	  if (querytype == Q_INVERSE) {
406  | 	      /* for inverse queries, convert number to dotted-quad */
407  | 	      char buf[64];
408  | 	      const char *inet_ntop_ret;
409  | 	      inet_ntop_ret = inet_ntop(AF_INET, &begin, buf, sizeof(buf));
410  | 	      dieif(inet_ntop_ret == NULL);
411  | 	      sprintf(result_buff, q.query, buf);
412  | 	  } else {
413  | 	      /* otherwise, execute appropriate query on numeric values */
414  | 	      sprintf(result_buff, q.query, begin, end);
415  | 	  }
416  | 	  addquery = 1;
417  | 	}
418  | 	else {
419  | 	  die;
420  | 	}
421  |       }
422  |     }
423  |     else if( q.keytype == WK_ASRANGE ) {   /* as_block range composition */
424  |       if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
425  | 	addquery = 0; /* ... unless it's not correct */
426  |       }
427  |       else {
428  | 	addquery = 1;
429  |       }
430  |     }
431  |     else {
432  |       sprintf(result_buff, q.query, qc->keys);
433  |       addquery = 1;
434  |     }
435  | 
436  |     if (q.class == C_ANY && addquery == 1 ) {
437  |       /* It is class type ANY so add the object filtering */
438  |       add_filter(result_buff, qc);
439  |     }
440  |   }
441  |   
442  |   if( addquery == 1 ) {
443  |     dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);  
444  |     strcpy(result, result_buff);
445  |     return result;
446  |   } 
447  |   else {
448  |     return NULL;
449  |   }
450  | } /* create_query() */
451  | 
452  | /* QI_fast_output() */
453  | /*++++++++++++++++++++++++++++++++++++++
454  |   This is for the '-F' flag.
455  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
456  |   Fast isn't fast anymore - it's just there for compatibility reasons.
457  | 
458  |   const char *str        The object to be "fast output'ed".
459  |    
460  |   More:
461  |   +html+ <PRE>
462  |   Authors:
463  |         ottrey,
464  | 	marek - glib strings + small changes
465  |   +html+ </PRE>
466  |   ++++++++++++++++++++++++++++++++++++++*/
467  | char *QI_fast_output(const char *str) 
468  | {
469  |   int i,j;
470  |   char *result;
471  |   GString *result_buff = g_string_sized_new(STR_XL);
472  |   gchar **lines = g_strsplit(str, "\n", 0);
473  |   unsigned char *value, *colon;
474  |   char *attr;
475  | 
476  |   g_string_assign(result_buff, "");
477  |   
478  |   for (j=0; lines[j] != NULL; j++) {
479  | 
480  |     switch (lines[j][0]) {
481  |       /* line continuation */
482  |     case ' ':
483  |     case '\t':
484  |     case '+':
485  |       value = (unsigned char *) lines[j]+1;
486  |       while(*value != '\0' && isspace(*value)) {
487  | 	value++;
488  |       }      
489  |       g_string_append(result_buff, "\n+ ");
490  |       g_string_append(result_buff, (char *)value);
491  |       break;
492  |       
493  |     default:
494  |       /* a line of the form "attribute: value" */
495  |       /* first: close the last line (if there was any, i.e. j>0) */
496  |       if( j > 0 ) { 
497  | 	g_string_append_c(result_buff, '\n');
498  |       }
499  |       
500  |       /* get attribute name */
501  |       attr =  lines[j];
502  |       colon = (unsigned char *) strchr(lines[j], ':');
503  |       /* if there's no colon for whatever reason, dump the object
504  | 	 and report the condition */
505  |       if( colon == NULL ) {
506  | 	ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
507  | 	goto fast_output_cleanup;
508  |       }
509  |       *colon = '\0';
510  |       for(value = colon+1; *value != '\0' && isspace(*value) ; value++) {
511  | 	;
512  |       }
513  | 
514  |       if( (i = DF_attribute_name2type(attr)) == -1 ) {
515  | 	  /* warning! error in the object format */
516  | 	ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
517  | 	goto fast_output_cleanup;
518  | 		
519  |       }
520  |       else {
521  | 	/* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
522  | 	g_string_append_c(result_buff, '*');
523  | 	g_string_append(result_buff, DF_get_attribute_code(i));
524  | 	g_string_append(result_buff, ": ");
525  | 	g_string_append(result_buff, (char *)value);
526  |       }
527  |     } /* switch */  
528  |   } /* for every line */ 
529  | 
530  |  fast_output_cleanup:
531  | 
532  |   g_strfreev(lines);
533  |   
534  |   g_string_append_c(result_buff, '\n');
535  |   result = strdup(result_buff->str);
536  |   dieif(result == NULL);
537  |   
538  |   g_string_free(result_buff,/* CONSTCOND */ TRUE);
539  |   
540  |   return result;
541  | } /* fast_output() */
542  | 
543  | /* filter() */
544  | /*++++++++++++++++++++++++++++++++++++++
545  |   Basically it's for the '-K' flag for non-set (and non-radix) objects.
546  |   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
547  | 
548  |   This could be speed up if there were breaks out of the loops, once it matched something.
549  | 
550  |   const char *string          The string to be filtered.
551  |    
552  |   More:
553  |   +html+ <PRE>
554  |   Authors:
555  |         ottrey
556  |   +html+ </PRE>
557  | 
558  |   ++++++++++++++++++++++++++++++++++++++*/
559  | char *filter(const char *str) {
560  |   int i,j, passed=0;
561  |   char *result;
562  |   GString *result_buff = g_string_sized_new(STR_XL);
563  |   gchar **lines = g_strsplit(str, "\n", 0);
564  |   char * const *filter_names;
565  |   gboolean filtering_an_attribute = FALSE;
566  |   
567  |   filter_names = DF_get_filter_names();
568  | 
569  |   g_string_assign(result_buff, "");
570  |   
571  |   for (i=0; filter_names[i] != NULL; i++) {
572  |     for (j=0; lines[j] != NULL; j++) {
573  |       if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
574  | 
575  |         g_string_sprintfa(result_buff, "%s\n", lines[j]);
576  | 	passed++;
577  | 	
578  | 	/* CONSTCOND */
579  |         filtering_an_attribute = TRUE;
580  |       }
581  |       /* CONSTCOND */
582  |       else if (filtering_an_attribute == TRUE) {
583  |         switch (lines[j][0]) {
584  |           case ' ':
585  |           case '\t':
586  |           case '+':
587  | 
588  |             g_string_sprintfa(result_buff, "%s\n", lines[j]);
589  |             
590  |           break;
591  | 
592  |           default:
593  |             filtering_an_attribute = FALSE;
594  |         }
595  |       }
596  |     }
597  |   }
598  | 
599  |   g_strfreev(lines);
600  | 
601  |   if(passed) {
602  |     g_string_append(result_buff, "\n");
603  |   }
604  |   result = strdup(result_buff->str);
605  |   g_string_free(result_buff,/* CONSTCOND */ TRUE);
606  | 
607  |   return result;
608  | } /* filter() */
609  | 
610  | /* write_results() */
611  | /*++++++++++++++++++++++++++++++++++++++
612  |   Write the results to the client socket.
613  | 
614  |   SQ_result_set_t *result The result set returned from the sql query.
615  |   unsigned filtered       if the objects should go through a filter (-K)
616  |   sk_conn_st *condat      Connection data for the client    
617  | 
618  |   More:
619  |   +html+ <PRE>
620  |   Authors:
621  |         ottrey - initial design
622  | 	marek - rewritten for accounting and cancellation.
623  |   +html+ </PRE>
624  | 
625  |   ++++++++++++++++++++++++++++++++++++++*/
626  | static int write_results(SQ_result_set_t *result, 
627  | 			 unsigned filtered,
628  | 			 unsigned fast,
629  | 			 sk_conn_st *condat,
630  | 			 acc_st    *acc_credit,
631  | 			 acl_st    *acl
632  | 			 ) {
633  |   SQ_row_t *row;
634  |   char *str;
635  |   char *filtrate;
636  |   char *fasted;
637  |   int retrieved_objects=0;
638  |   char *objt;
639  |   int type;
640  | 
641  |   /* Get all the results - one at a time */
642  |   if (result != NULL) {
643  |     /* here we are making use of the mysql_store_result capability
644  |        of interrupting the cycle of reading rows. mysql_use_result
645  |        would not allow that, would have to be read until end */
646  |     
647  |     while ( condat->rtc == 0 
648  | 	    && AC_credit_isdenied( acc_credit ) == 0
649  | 	    && (row = SQ_row_next(result)) != NULL ) {
650  |       
651  |       if (  (str = SQ_get_column_string(result, row, 0)) == NULL
652  | 	    || (objt = SQ_get_column_string(result, row, 3)) == NULL )  { 
653  | 	/* handle it somehow ? */
654  | 	die; 
655  |       }
656  |       else  { 
657  | 	/* get + add object type */
658  | 	type = atoi(objt);
659  | 	
660  | 	/* ASP_QI_LAST_DET */
661  | 	ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
662  | 		  "Retrieved serial id = %d , type = %s", atoi(str), objt);
663  | 	
664  | 	wr_free(str);
665  | 	wr_free(objt);
666  |       }
667  |       
668  |       /* decrement credit for accounting purposes */
669  |       AC_count_object( acc_credit, acl, 
670  | 		       type == C_PN || type == C_RO ); /* is private? */
671  | 
672  |       /* break the loop if the credit has just been exceeded and 
673  | 	 further results denied */
674  |       if( AC_credit_isdenied( acc_credit ) ) {
675  | 	continue; 
676  |       }
677  |       
678  |       if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 
679  |       else {
680  | 	
681  |         /* The fast output stage */
682  |         if (fast == 1) {
683  |           fasted = QI_fast_output(str);
684  |           wr_free(str);
685  |           str = fasted;
686  |         }
687  | 	
688  |         /* The filtering stage */
689  |         if (filtered == 0) {
690  |           SK_cd_puts(condat, str);
691  | 	  SK_cd_puts(condat, "\n");
692  |         }
693  |         else { 
694  | 	  
695  | 	  /* XXX accounting should be done AFTER filtering, not to count
696  | 	     objects filtered out */
697  | 
698  |           filtrate = filter(str);
699  |           SK_cd_puts(condat, filtrate);
700  |           wr_free(filtrate);
701  |         }
702  |         retrieved_objects++;
703  |       }
704  |       wr_free(str);
705  |     }
706  |   }
707  |   
708  |   return retrieved_objects;
709  | } /* write_results() */
710  | 
711  | /* generic SQL error message - it could be a configurable parameter, but
712  |    it also shouldn't happen! */
713  | static const char *sql_error_text = 
714  |     "% An internal database error has occurred.\n"  
715  |     "% It has been logged, and an adminstrator should look at it shorly.\n"
716  |     "% Please try your query again.\n"
717  |     "\n"
718  |     "\n";
719  | 
720  | /* use a macro so we can get our file and line number */
721  | #define report_sql_error(condat,sql_connection,sql_command) \
722  |   __report_sql_error((condat), (sql_connection), (sql_command), \
723  |                      __FILE__,__LINE__,pthread_self())
724  | 
725  | /* report_sql_error() */
726  | /*++++++++++++++++++++++++++++++++++++++
727  | 
728  |   sk_conn_st *condat                 connection with user
729  |   SQ_connection_t *sql_connection    connection to MySQL
730  |   const char *sql_command            SQL command sent to MySQL
731  | 
732  |   ++++++++++++++++++++++++++++++++++++++*/
733  | 
734  | void
735  | __report_sql_error(sk_conn_st *condat, 
736  |                  SQ_connection_t *sql_connection, 
737  | 		 const char *sql_command,
738  | 		 const char *file,
739  | 		 int line,
740  | 		 pthread_t tid)
741  | {
742  |     /* first, let user know what has happened */
743  |     SK_cd_puts(condat, sql_error_text);
744  | 
745  |     /* next, log this error */
746  |     ER_perror(FAC_QI, QI_SQLERR,"sql='%s' at %s:%d, tid:%d [%d] %s", 
747  | 		      sql_command,
748  | 		      file, line, tid,
749  | 		      SQ_errno(sql_connection), 
750  | 		      SQ_error(sql_connection));
751  | }
752  | 
753  | /* write_objects() */
754  | /*++++++++++++++++++++++++++++++++++++++
755  |   
756  |   SQ_connection_t *sql_connection The connection to the database.
757  | 
758  |   char *id_table The id of the temporary table (This is a result of the hacky
759  |                   way we've tried to get MySQL to do sub-selects.)
760  | 
761  |   sk_conn_st *condat  Connection data for the client
762  | 
763  |   More:
764  |   +html+ <PRE>
765  |   Authors:
766  |         ottrey,
767  | 	marek.
768  |   +html+ </PRE>
769  |   ++++++++++++++++++++++++++++++++++++++*/
770  | static int 
771  | write_objects(SQ_connection_t **sql_connection, 
772  | 			  char *id_table, 
773  | 			  unsigned int filtered, 
774  | 			  unsigned int fast, 
775  | 			  sk_conn_st *condat,
776  | 			  acc_st    *acc_credit,
777  | 			  acl_st    *acl
778  | 			  ) 
779  | {
780  |   SQ_result_set_t *result = NULL;
781  |   int retrieved_objects=0;
782  |   char sql_command[STR_XL];  
783  | 
784  |   sprintf(sql_command, Q_OBJECTS, id_table);
785  | 
786  |   if (sql_execute_watched(condat, sql_connection, sql_command, &result) == -1) {
787  |       report_sql_error(condat, *sql_connection, sql_command);
788  |       return SQ_errno(*sql_connection);
789  |   }
790  |   
791  |   /* Problem: if the query was aborted, the result structure does not
792  |      refer to any existing connection anymore. So we check rtc here.
793  |   */
794  |   
795  |   if( condat->rtc == 0) {
796  |     retrieved_objects = write_results(result, filtered, fast, condat, 
797  | 				      acc_credit, acl);
798  |     SQ_free_result(result); 
799  |   }
800  |   return 0;
801  | } /* write_objects() */
802  | 
803  | /* insert_radix_serials() */
804  | /*++++++++++++++++++++++++++++++++++++++
805  |   Insert the radix serial numbers into a temporary table in the database.
806  | 
807  |   mask_t bitmap The bitmap of attribute to be converted.
808  |    
809  |   SQ_connection_t *sql_connection The connection to the database.
810  | 
811  |   char *id_table The id of the temporary table (This is a result of the hacky
812  |                   way we've tried to get MySQL to do sub-selects.)
813  |   
814  |   GList *datlist The list of data from the radix tree.
815  | 
816  |   More:
817  |   +html+ <PRE>
818  |   Authors:
819  |         ottrey,
820  | 	marek
821  |   +html+ </PRE>
822  | 
823  |   ++++++++++++++++++++++++++++++++++++++*/
824  | static int insert_radix_serials(sk_conn_st *condat,
825  | 				 SQ_connection_t *sql_connection, 
826  | 				 char *id_table, GList *datlist) {
827  |   GList    *qitem;
828  |   char sql_command[STR_XL];
829  |   int serial;
830  |   int sql_error;
831  | 
832  |   sql_error = 0;
833  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
834  |     rx_datcpy_t *datcpy = qitem->data;
835  | 
836  |     serial = datcpy->leafcpy.data_key;
837  | 
838  |     /* don't bother to insert values into our temporary table */
839  |     /* if we've lost the client connection */
840  |     if ((condat->rtc == 0) && !sql_error) {
841  |         sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
842  |         if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
843  | 	    sql_error = SQ_errno(sql_connection);
844  | 	    report_sql_error(condat, sql_connection, sql_command);
845  | 	}
846  |     }
847  | 
848  |     wr_free(datcpy->leafcpy.data_ptr);
849  |   }
850  | 
851  |   wr_clear_list( &datlist );
852  | 
853  |   /* return error, if any */
854  |   return sql_error;
855  | 
856  | } /* insert_radix_serials() */
857  | 
858  | 
859  | /* write_radix_immediate() */
860  | /*++++++++++++++++++++++++++++++++++++++
861  |   Display the immediate data carried with the objects returned by the
862  |   radix tree.
863  | 
864  |   GList *datlist      The linked list of dataleaf copies
865  | 
866  |   sk_conn_st *condat  Connection data for the client
867  | 
868  |   acc_st  *acc_credit Accounting struct
869  | 
870  | More:
871  |   +html+ <PRE>
872  |   Authors:
873  |         marek
874  |   +html+ </PRE>
875  | 
876  |   Also free the list of answers.
877  | ++++++++++++++++++++++++++++++++++++++*/
878  | static void write_radix_immediate(GList *datlist, 
879  | 				  sk_conn_st *condat,
880  | 				  acc_st    *acc_credit,
881  | 				  acl_st    *acl) 
882  | {
883  |   GList    *qitem;
884  |   
885  |   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
886  |     rx_datcpy_t *datcpy = qitem->data;
887  | 
888  |     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
889  |     SK_cd_puts(condat, "\n");
890  |     
891  |     wr_free(datcpy->leafcpy.data_ptr);
892  |     
893  |     AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
894  | 
895  |     if(condat->rtc != 0) {
896  |       break;
897  |     }
898  |   }
899  |   
900  |   wr_clear_list( &datlist );
901  | } /* write_radix_immediate() */
902  | 
903  | 
904  | /* map_qc2rx() */
905  | /*++++++++++++++++++++++++++++++++++++++
906  |   The mapping between a query_command and a radix query.
907  | 
908  |   Query_instruction *qi The Query Instruction to be created from the mapping
909  |                         of the query command.
910  | 
911  |   const Query_command *qc The query command to be mapped.
912  | 
913  |   More:
914  |   +html+ <PRE>
915  |   Authors:
916  |         ottrey,
917  | 	marek - simplified the logic, added stealth -S option
918  |   +html+ </PRE>
919  | 
920  |   ++++++++++++++++++++++++++++++++++++++*/
921  | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
922  |   int result=1;
923  |   int allflags = (qc->L == 1) + (qc->M == 1) + (qc->l == 1) 
924  |                + (qc->m == 1) + (qc->x == 1);
925  | 
926  |   qi->rx_keys = qc->keys;
927  | 
928  |   /* only one option can be active at a time */
929  | 
930  |   if( allflags > 1 ) {
931  |       /* user error  (this should have been checked before) */
932  |       
933  |       ER_dbg_va(FAC_QI, ASP_QI_SKIP, 
934  | 		"ERROR in qc2rx mapping: bad combination of flags");
935  |       result = 0;
936  |   }
937  |   if( allflags == 0 ) { 
938  |       /* no options active - default search */
939  |       qi->rx_srch_mode = RX_SRCH_EXLESS;
940  |       qi->rx_par_a = 0;
941  |   }
942  |   else if ( qc->L == 1 ) {
943  |       qi->rx_srch_mode = RX_SRCH_LESS;
944  |       qi->rx_par_a = RX_ALL_DEPTHS;
945  |   }
946  |   else if (qc->M == 1) {
947  |       qi->rx_srch_mode = RX_SRCH_MORE;
948  |       qi->rx_par_a = RX_ALL_DEPTHS;
949  |   }
950  |   else if (qc->l == 1) {
951  |       qi->rx_srch_mode = RX_SRCH_LESS;
952  |       qi->rx_par_a = 1;
953  |   }
954  |   else if (qc->m == 1) {
955  |       qi->rx_srch_mode = RX_SRCH_MORE;
956  |       qi->rx_par_a = 1;
957  |   }
958  |   else if (qc->x == 1) {
959  |       qi->rx_srch_mode = RX_SRCH_EXACT;
960  |       qi->rx_par_a = 0;
961  |   }
962  |   
963  |   if( qi->rx_srch_mode == RX_SRCH_MORE && (qc->S == 1) ) {
964  |       qi->rx_srch_mode = RX_SRCH_DBLS;
965  |   }
966  |   
967  |   return result;
968  |   
969  | } /* map_qc2rx() */
970  | 
971  | 
972  | /* run_referral() */
973  | /*++++++++++++++++++++++++++++++++++++++
974  |   
975  |    invoked when no such domain found. Goes through the domain table
976  |    and searches for shorter domains, then if it finds one with referral 
977  |    it performs it, otherwise it just returns nothing.
978  | 
979  |    to perform referral, it actually composes the referral query 
980  |    for a given host/port/type and calls the whois query function.
981  | 
982  |    Well, it returns nothing anyway (void). It just prints to the socket.
983  | 
984  |   char *ref_host           referral server host name
985  | 
986  |   unsigned ref_port_int    referral server port number
987  | 
988  |   char *qry                query to be run
989  | 
990  |   Author:
991  |     marek
992  |   ++++++++++++++++++++++++++++++++++++++*/
993  | void run_referral(Query_environ *qe, 
994  | 		  char *ref_host,
995  | 		  unsigned ref_port_int,
996  | 		  char *qry)
997  | {
998  |   
999  | #if 1 /* switch off for testing */
1000 |   er_ret_t err;
1001 |   char *rep;
1002 | 
1003 |         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
1004 |   err= WH_cd_sock(&(qe->condat), ref_host, ref_port_int, qry, 
1005 | 		  ca_get_referralmaxlines, ca_get_referraltimeout
1006 | 		  );
1007 | 
1008 |   switch( err ) {
1009 |   case SK_OK:
1010 |     /* OK */
1011 |     break;
1012 |   case SK_TIMEOUT:
1013 |     /* Referral timeout */
1014 |     rep = ca_get_qi_ref_tmout ;
1015 |     SK_cd_puts(&(qe->condat), rep);
1016 |     wr_free(rep);
1017 |     break;
1018 | 	    
1019 |   case SK_BADHOST:
1020 |     /* Referral host not found */
1021 |     rep = ca_get_qi_ref_badhost ;
1022 |     SK_cd_puts(&(qe->condat), rep);
1023 |     wr_free(rep);
1024 |     break;
1025 | 
1026 |   case SK_CONNECT:
1027 |     /* Referral host not responding */
1028 |     rep = ca_get_qi_ref_hostnottresp ;
1029 |     SK_cd_puts(&(qe->condat), rep);
1030 |     wr_free(rep);
1031 |     break;
1032 | 
1033 |   case SK_BIND:
1034 |   case SK_SOCKET:
1035 |     /* XXX internal server problem...  */
1036 |     die; 
1037 | 
1038 |   case WH_MAXLINES:
1039 |     /* Referral reply line limit exceeded */
1040 |     rep = ca_get_qi_ref_overmaxlin ;
1041 |     SK_cd_puts(&(qe->condat), rep);
1042 |     wr_free(rep);
1043 |     break;
1044 |     
1045 |   default: /* any other errors ? */
1046 |     die;
1047 |     ;
1048 |   } /*switch WH_sock */
1049 | #endif
1050 |   
1051 | }/*run_referral*/
1052 | 
1053 | 
1054 | 
1055 | 
1056 | 
1057 | /*++++++++++++++++++++++++++++++++++++++
1058 |    
1059 |    prepare and run the referral, displaying the results directly to the
1060 |    client's connection. 
1061 |    
1062 |    XXX still missing protection against a referral loop
1063 |    XXX handling inverse flag not needed, to be removed
1064 | 
1065 |   char *domain               domain being looked up
1066 |  
1067 |   Query_instructions *qis    original query instructions structure
1068 | 
1069 |   Query_environ *qe          original query environment structure
1070 | 
1071 |   Query_instruction *qi      specific query instruction triggered
1072 | 
1073 |   SQ_result_set_t *result    result of the lookup containing referral details
1074 | 
1075 |   SQ_row_t *row              first row (should be only 1) of the result
1076 |                              this should contain columns: type, port, host
1077 | 
1078 |   char *sourcename           name of the database "source"
1079 | 
1080 |   Author: 
1081 |      marek
1082 |   ++++++++++++++++++++++++++++++++++++++*/
1083 | static
1084 | void qi_prep_run_refer(char *domain, 
1085 | 		       Query_instructions *qis,   
1086 | 		       Query_environ *qe, 
1087 | 		       Query_instruction *qi,
1088 | 		       SQ_result_set_t *result, SQ_row_t *row, 
1089 | 		       char *sourcename )
1090 | {
1091 |     int err;
1092 |     long ref_type;
1093 |     long ref_port;
1094 |     char *ref_host; 
1095 |     char querystr[STR_L];
1096 | 
1097 |     /* get values from SQL query */
1098 |     err = SQ_get_column_int(result, row, 0, &ref_type);
1099 |     dieif(err);
1100 |     err = SQ_get_column_int(result, row, 1, &ref_port);
1101 |     dieif(err);
1102 |     ref_host = SQ_get_column_string(result, row, 2);
1103 |     dieif(ref_host == NULL);
1104 |       
1105 |     strcpy(querystr,"");
1106 |       
1107 |     /* put -r if the reftype is RIPE and -r or -i were used */
1108 |     if( (ref_type == RF_RIPE) 
1109 | 	&& ( Query[qi->queryindex].querytype == Q_INVERSE       
1110 | 	     || qis->recursive > 0  )   ) 
1111 |     {
1112 | 	strcpy(querystr,"-r ");
1113 |     }
1114 |       
1115 |     /* prepend with -Vversion,IP for type CLIENTADDRESS */
1116 |     if( ref_type == RF_CLIENTADDRESS ) {
1117 | 	snprintf(querystr,sizeof(querystr),"-V%s,%s ",VERSION, qe->condat.ip);
1118 |     }
1119 | 
1120 |       
1121 |     /* now set the search term - set to the stripped down version 
1122 |        for inverse query, full-length otherwise */
1123 |     if( Query[qi->queryindex].querytype == Q_INVERSE ) {
1124 | 	strcat(querystr, domain);
1125 |     }
1126 |     else {
1127 | 	strcat(querystr, qis->qc->keys);
1128 |     }
1129 | 
1130 |     {
1131 | 	/* the object is not from %s, 
1132 | 	   it comes from %s %d, use -R to see %s */
1133 | 	char *rep = ca_get_qi_fmt_refheader ;
1134 | 	SK_cd_printf(&(qe->condat), rep, 
1135 | 		     sourcename, 
1136 | 		     ref_host, ref_port,
1137 | 		     sourcename );
1138 | 	wr_free(rep);
1139 |     }
1140 | 	    
1141 |     /* do the referral */
1142 |     ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host); 
1143 |       
1144 |     run_referral( qe, ref_host, ref_port, querystr);
1145 |       
1146 |     { /* End of referred query result */
1147 | 	char *rep = ca_get_qi_reftrailer ; 
1148 | 	SK_cd_puts(&(qe->condat), rep);
1149 | 	wr_free(rep);
1150 |     }
1151 |     SK_cd_puts(&(qe->condat), "\n");
1152 |     
1153 |     wr_free(ref_host);
1154 | }
1155 | 
1156 | 
1157 | /*++++++++++++++++++++++++++++++++++++++
1158 |   
1159 |   specific case of the object ID collection: the domains.
1160 |   Checks to see if the domain exists, and runs the referral if it is defined
1161 |   and the domain is missing.
1162 | 
1163 |   Arguments:
1164 | 
1165 |   char *sourcename                     name of the database "source"
1166 | 
1167 |   SQ_connection_t *sql_connection      sql connection dedicated to this thread
1168 |   
1169 |   char *id_table                       name of the temporary table to be used
1170 |   
1171 |   char *sub_table                      name of the temporary subtable
1172 |   
1173 |   Query_instructions *qis    original query instructions structure
1174 | 
1175 |   Query_environ *qe          original query environment structure
1176 | 
1177 |   Query_instruction *qi      specific query instruction triggered
1178 | 
1179 |   acc_st *acc_credit         credit for this client 
1180 |  
1181 |   Author:
1182 |      marek.
1183 |   ++++++++++++++++++++++++++++++++++++++*/
1184 | 
1185 | static int
1186 | qi_collect_domain(char *sourcename,
1187 | 		  SQ_connection_t *sql_connection, 
1188 | 		  char *id_table,
1189 | 		  char *sub_table,
1190 | 		  Query_instructions *qis,   
1191 | 		  Query_environ *qe, 
1192 | 		  Query_instruction *qi,
1193 | 		  acc_st *acc_credit,
1194 | 		  int *sql_error)
1195 | {
1196 |   char *domain = qis->qc->keys;
1197 |   char *dot = domain;
1198 |   int subcount = 0;
1199 |   int foundcount = 0;
1200 | 
1201 |   /* we MUST NOT have a diconnection from the server here */
1202 |   dieif(qe->condat.rtc != 0);
1203 | 
1204 |   /* while nothing found and still some pieces of the name left */
1205 |   while( dot != NULL && subcount == 0 ) { 
1206 |     int refcount = 0;
1207 |     SQ_row_t *row;
1208 |     SQ_result_set_t *result_referrals = NULL;
1209 |     char sql_command[STR_XL];
1210 | 
1211 |     ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
1212 | 
1213 |     /* domain lookup -- query into the _S table */
1214 |     sprintf(sql_command, "INSERT INTO %s SELECT object_id FROM domain WHERE domain = '%s'", sub_table, dot);
1215 |     
1216 |     if( SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1217 |         *sql_error = SQ_errno(sql_connection);
1218 | 	report_sql_error(&qe->condat, sql_connection, sql_command);
1219 | 	return 0;
1220 |     }
1221 |     subcount = SQ_get_affected_rows(sql_connection); 
1222 | 
1223 |     if( subcount != 0 ) { /* domain exists in the database */
1224 | 
1225 | 	/* referral check. Always done except for -R and INVERSE queries */  
1226 | 	if( qis->qc->R == 0 && 
1227 | 	    Query[qi->queryindex].querytype != Q_INVERSE ) {
1228 | 	    sprintf(sql_command, "SELECT type, port, host FROM %s ID, refer WHERE ID.id = refer.object_id", sub_table);
1229 | 	    if( SQ_execute_query(sql_connection, sql_command, 
1230 | 				    &result_referrals) == -1) 
1231 |             {
1232 | 	        *sql_error = SQ_errno(sql_connection);
1233 | 	        report_sql_error(&qe->condat, sql_connection, sql_command);
1234 | 		return 0;
1235 |             }
1236 | 	    refcount = SQ_num_rows(result_referrals);
1237 | 	}
1238 | 
1239 | 	/* if referral allowed and defined, even if domain was found but 
1240 | 	   contained referral - refer the query */
1241 | 	if( refcount != 0 ) { 
1242 | 	    /* get the referral parameters from the first row
1243 | 	       and perform it 
1244 | 	    */
1245 | 
1246 | 	    row = SQ_row_next(result_referrals);
1247 | 	    /* now: query for the original domain */
1248 | 	    qi_prep_run_refer(domain,
1249 | 			      qis, qe, qi, result_referrals, row, sourcename);
1250 | 	    
1251 | 	    acc_credit->referrals -= 1;
1252 | 	}
1253 | 	else {
1254 | 	    /* domain found 
1255 | 	       and (referral undefined  or  disabled by -R or inverse)
1256 | 	       two possible outcomes depending on whether 'dot' is:
1257 | 	       * the original search term -> pass what's in _S and quit 
1258 | 	       * a 'stripped' domain name -> return no result and quit
1259 | 	    */
1260 | 	    if( dot == domain ) {
1261 | 		sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s", 
1262 | 			id_table, sub_table);
1263 | 		if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1264 | 		    *sql_error = SQ_errno(sql_connection);
1265 | 	            report_sql_error(&qe->condat, sql_connection, sql_command);
1266 | 		    return 0;
1267 | 		}
1268 | 		foundcount = SQ_get_affected_rows(sql_connection); 
1269 | 	    }
1270 | 	} 
1271 | 	dot = NULL; /* don't make another round */
1272 |     } /* a domain was found */
1273 | 
1274 |     if( result_referrals != NULL ) {
1275 | 	SQ_free_result(result_referrals);
1276 | 	result_referrals = NULL;
1277 |     }
1278 |     
1279 |     if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1280 |       dot++;
1281 |     }
1282 |   }
1283 |     
1284 |   return foundcount;
1285 | } /* check_domain */
1286 | 
1287 | 
1288 | /* add_ref_name */
1289 | /*++++++++++++++++++++++++++++++++++++++
1290 | 
1291 |   Creates a SQL query for a reference-by-name lookup. Uses standard name
1292 |   lookup query generator (create_name_query), so the order of the names
1293 |   doesn't matter.
1294 | 
1295 |   SQ_connection_t *sql_connection   sql connection dedicated to this thread
1296 | 
1297 |   char *rectable       table in which to look up
1298 |   
1299 |   char *allnames       all name words to be looked up, space delimited.
1300 | 
1301 | ++++++++++++++++++++++++++++++++++++++*/
1302 | static
1303 | int 
1304 | add_ref_name(SQ_connection_t *sql_connection, 
1305 | 	     char *rectable,
1306 | 	     char *allnames,
1307 | 	     sk_conn_st *condat
1308 | 	     )
1309 | {
1310 |   /* construct the query, allow zero-length list */
1311 |   if( strlen(allnames) > 0 ) {
1312 |     char final_query[STR_XL];
1313 |     char select_query[STR_XL];
1314 | 
1315 |     create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1316 | 		      "AND N00.object_type != 100 AND N00.thread_id = 0", 
1317 | 		      allnames);
1318 |     
1319 |     sprintf(final_query, "INSERT INTO %s %s",
1320 | 	    rectable,
1321 | 	    select_query);
1322 | 
1323 |     allnames[0]=0;
1324 |     
1325 |     if (SQ_execute_query(sql_connection, final_query, NULL) == -1 ) {
1326 |         report_sql_error(condat, sql_connection, final_query);
1327 |         return SQ_errno(sql_connection);
1328 |     }
1329 | 
1330 |   }
1331 | 
1332 |   return 0;
1333 | }/* add_ref_name */
1334 | 
1335 | 
1336 | 
1337 | /* qi_collect_ids */
1338 | /*++++++++++++++++++++++++++++++++++++++ 
1339 |   
1340 |   collects object ID's from all queries defined in the Query_instructions 
1341 |   array. The results from RADIX trees are maintained in a linked list, the 
1342 |   results from SQL lookups are kept in a temporary table. For domains,
1343 |   a specific function is invoked that may run the referral. 
1344 |   Any sql lookup will be limited to the maximum number of objects allowed
1345 |   for the client (acl and credit are checked for this).
1346 |   The routine uses its own temporary _S table, destroyed at exit.
1347 |   
1348 |   ca_dbSource_t *dbhdl              source-specific identifier (defined in CA)
1349 |   
1350 |   char *sourcename                  name of the database "source"
1351 |   
1352 |   SQ_connection_t **sql_connection  sql connection dedicated to this thread
1353 |                                     (replaced on cancel)
1354 |  
1355 |   Query_instructions *qis           original query instructions structure
1356 | 
1357 |   Query_environ *qe                 original query environment structure
1358 |  
1359 |   char *id_table                    the table to store the ID's found
1360 |   
1361 |   GList **datlist                   the list  to store the Radix leaves found
1362 |   
1363 |   acc_st *acc_credit                credit for this client 
1364 |   
1365 |   acl_st *acl                       acl for this client 
1366 |   
1367 |   ++++++++++++++++++++++++++++++++++++++*/
1368 | static
1369 | int
1370 | qi_collect_ids(ca_dbSource_t *dbhdl,
1371 | 	       char *sourcename,
1372 | 	       SQ_connection_t **sql_connection,
1373 | 	       Query_instructions *qis,
1374 | 	       Query_environ *qe,	
1375 | 	       char *id_table,
1376 | 	       GList **datlist,
1377 | 	       acc_st *acc_credit,
1378 | 	       acl_st *acl
1379 | 	       )
1380 | {
1381 |   Query_instruction **ins=NULL;
1382 |   int i;
1383 |   int  count, errors=0;
1384 |   char sql_command[STR_XL];
1385 |   er_ret_t err;
1386 |   char sub_table[32];
1387 |   int limit ;
1388 |              /* a limit on the max number of objects to be returned
1389 | 		from a single search. For some queries the object types
1390 | 		are not known at this stage, so the limit must be
1391 | 		the higher number of the two: private / public,
1392 | 		or unlimited if any of them is 'unlimited'.
1393 | 	     */
1394 |   char limit_str[32];
1395 |   int sql_error;
1396 | 
1397 |   if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1398 |     strcpy(limit_str,"");
1399 |   } else {
1400 |     sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1401 | 						so that the client hits
1402 | 						the limit */
1403 |   }
1404 | 
1405 |   sprintf(sub_table, "%s_S ", id_table);
1406 |   
1407 |   /* see if there was a leftover table from a crashed session 
1408 |    * (assume the ID cannot be currently in use)
1409 |    *
1410 |    * update: this can't ever happen with TEMPORARY tables, but we're going to
1411 |    *         check for it anyway  - shane
1412 |    */
1413 |   sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1414 |   if( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1415 |       report_sql_error(&qe->condat, *sql_connection, sql_command);
1416 |       return SQ_errno(*sql_connection);
1417 |   }
1418 | 
1419 |   /* create a table for special subqueries (domain only for now) */
1420 |   sprintf(sql_command, "CREATE " TEMPORARY " TABLE %s ( id int ) TYPE=HEAP", 
1421 |     sub_table);
1422 |   if( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1423 |       report_sql_error(&qe->condat, *sql_connection, sql_command);
1424 |       return SQ_errno(*sql_connection);
1425 |   }
1426 |   
1427 |   /* Iterate through query instructions */
1428 |   ins = qis->instruction;
1429 |   sql_error = 0;
1430 |   for (i=0; ins[i] != NULL && errors == 0; i++) {
1431 |     Query_instruction *qi = ins[i];
1432 |     
1433 |     /* check if the client is still there */
1434 |     if( qe->condat.rtc ) {
1435 |       break;
1436 |     }
1437 | 
1438 |     switch ( qi->search_type ) {
1439 |     case R_SQL:
1440 |       if ( qi->query_str != NULL ) {
1441 | 
1442 | 	/* handle special cases first */
1443 | 	if( Query[qi->queryindex].class == C_DN 
1444 | 	    && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1445 | 	  
1446 | 	  /* if any more cases than just domain appear, we will be
1447 | 	     cleaning the _S table from the previous query here 
1448 | 	     
1449 | 	     "DELETE FROM %s_S"
1450 | 	  */
1451 | 	  
1452 | 	  count = qi_collect_domain(sourcename, *sql_connection, id_table, 
1453 | 				    sub_table, qis, qe, qi, acc_credit, 
1454 | 				    &sql_error);
1455 | 	} /* if class DN and Straight lookup */
1456 | 	else {
1457 | 	  /* any other class of query */
1458 | 
1459 | 	  sprintf(sql_command, "INSERT INTO %s %s %s", 
1460 | 		  id_table, qi->query_str, limit_str);
1461 | 
1462 | 	  if(sql_execute_watched( &(qe->condat), sql_connection, 
1463 | 				  sql_command, NULL) == -1 ) {
1464 | 	    errors++;
1465 | 	    sql_error = SQ_errno(*sql_connection);
1466 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1467 | 	  }
1468 | 	  count = SQ_get_affected_rows(*sql_connection);
1469 | 	} /* not DN */
1470 |       } /* if SQL query not NULL */
1471 |       
1472 |       ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1473 | 		"%d entries added in %s query for %s",
1474 | 		count, Query[qi->queryindex].descr, qis->qc->keys
1475 | 		);
1476 |       break;
1477 |       
1478 |     case R_RADIX:
1479 | 
1480 |      
1481 |       err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 
1482 | 			  qi->rx_keys, dbhdl, 
1483 | 			  Query[qi->queryindex].attribute, 
1484 | 			  datlist, limit);
1485 |      
1486 | 
1487 |       if( NOERR(err)) {
1488 | 	if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1489 | 	  /* prevent unnecessary g_list_length call */
1490 | 	  
1491 | 	  ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1492 | 		    "%d entries after %s (mode %d par %d reg %d) query for %s",
1493 | 		    g_list_length(*datlist),
1494 | 		    Query[qi->queryindex].descr,
1495 | 		    qi->rx_srch_mode, qi->rx_par_a, 
1496 | 		    dbhdl,
1497 | 		    qi->rx_keys);
1498 | 	}
1499 |       }
1500 |       else {
1501 | 	ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1502 | 		  "RP_asc_search returned %x ", err);
1503 |       }
1504 |       break;
1505 |       
1506 |     default: die;
1507 |     } /* switch */
1508 |     
1509 |   } /* for <every instruction> */
1510 | 
1511 |   /* Now drop the _S table */
1512 |   sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1513 |   if(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1514 |       report_sql_error(&qe->condat, *sql_connection, sql_command);
1515 |       return SQ_errno(*sql_connection);
1516 |   }
1517 | 
1518 |   /* return success */
1519 |   return sql_error;
1520 | }
1521 | 
1522 | /* qi_fetch_references */ 
1523 | /*++++++++++++++++++++++++++++++++++++++
1524 |   
1525 |   given the list of object ID's collects the references from these objects 
1526 |   to person and role objects. Uses its own temporary SQL table (_R)
1527 |   and upon completion transfers the results from it to the main
1528 |   temporary table. Runs queries in watched mode, to be able to cancel them.
1529 |   
1530 |   SQ_connection_t **sql_connection  sql connection dedicated to this thread
1531 |                                     (replaced on cancel)
1532 |   
1533 |   Query_environ *qe                 original query environment structure
1534 |   
1535 |   char *id_table                    the table with the ID's found
1536 |   
1537 |   acc_st *acc_credit                credit for this client 
1538 |   
1539 |   acl_st *acl                       acl for this client 
1540 | 
1541 | ++++++++++++++++++++++++++++++++++++++*/
1542 | static
1543 | int
1544 | qi_fetch_references(SQ_connection_t **sql_connection,
1545 | 		    Query_environ *qe,
1546 | 		    char *id_table,
1547 | 		    acc_st *acc_credit,
1548 | 		    acl_st *acl
1549 | 		    )
1550 | {
1551 |     char rec_table[32];
1552 |     SQ_result_set_t *result = NULL;
1553 |     SQ_row_t *row;
1554 |     int thisid = 0;
1555 |     int oldid = 0;
1556 |     char allnames[STR_L];
1557 |     char sql_command[STR_XL];
1558 |     int sql_error;
1559 | 
1560 |     /* use sql_error to flag errors */
1561 |     sql_error = 0;
1562 |  
1563 |     sprintf(rec_table, "%s_R", id_table);
1564 | 
1565 |     /* see if there was a leftover table from a crashed session 
1566 |      * (assume the ID cannot be currently in use)
1567 |      */
1568 |     sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1569 |     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1570 |         report_sql_error(&qe->condat, *sql_connection, sql_command);
1571 |         return SQ_errno(*sql_connection);
1572 |     }
1573 | 
1574 |     /* a temporary table for recursive data must be created, because
1575 |        a query using the same table as a source and target is illegal
1576 |        ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1577 |     */
1578 |     sprintf(sql_command, 
1579 |      "CREATE " TEMPORARY " TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", 
1580 |      rec_table);
1581 |     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1582 |         report_sql_error(&qe->condat, *sql_connection, sql_command);
1583 |         return SQ_errno(*sql_connection);
1584 |     }
1585 | 
1586 |     /* from this point on, we can't just return on error, because 
1587 |        we need to insure the table we just created gets dropped */
1588 |     
1589 |     /* find the contacts */      
1590 |     sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1591 |     if (sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) 
1592 |         == -1)
1593 |     {
1594 |         sql_error = SQ_errno(*sql_connection);
1595 | 	report_sql_error(&qe->condat, *sql_connection, sql_command);
1596 |     }
1597 |     
1598 |     if (!sql_error) {
1599 |         sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1600 |         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1601 | 	    NULL) == -1) 
1602 |         {
1603 | 	    sql_error = SQ_errno(*sql_connection);
1604 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1605 | 	}
1606 |     }
1607 |     
1608 |     if (!sql_error) {
1609 |         sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c");
1610 |         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1611 | 	    NULL) == -1) 
1612 |         {
1613 | 	    sql_error = SQ_errno(*sql_connection);
1614 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1615 | 	}
1616 |     }
1617 | 
1618 |     if (!sql_error) {
1619 |         sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c");
1620 |         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1621 | 	    NULL) == -1) 
1622 |         {
1623 | 	    sql_error = SQ_errno(*sql_connection);
1624 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1625 | 	}
1626 |     }
1627 |     
1628 |     /* replace references to dummies by references by name */
1629 |     if (!sql_error) {
1630 |         sprintf(sql_command, 
1631 | 	    " SELECT id, name    FROM %s IDS STRAIGHT_JOIN names "
1632 | 	    " WHERE IDS.id = names.object_id "
1633 | 	    "      AND names.object_type = 100"
1634 | 	    " ORDER BY id",
1635 | 	    rec_table);
1636 |         if (sql_execute_watched(&(qe->condat), sql_connection, sql_command, 
1637 | 	    &result) == -1) 
1638 |         {
1639 | 	    sql_error = SQ_errno(*sql_connection);
1640 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1641 | 	}
1642 |     }
1643 |     
1644 |     /* well, it might not be -1, but if the watchdog worked then the
1645 |        result is NULL */
1646 |     if (!sql_error && (result != NULL)) {
1647 |       
1648 |       allnames[0]=0;
1649 |       /* now go through the results and collect names */
1650 |       while ( !sql_error &&
1651 |               (qe->condat.rtc == 0) && 
1652 | 	      (row = SQ_row_next(result)) != NULL ) 
1653 |       {
1654 | 	char *id   = SQ_get_column_string(result, row, 0);
1655 | 	char *name = SQ_get_column_string(result, row, 1);
1656 | 	
1657 | 	thisid = atoi(id);
1658 | 	
1659 | 	/* when the id changes, the name is complete */
1660 | 	if( thisid != oldid && oldid != 0 ) {
1661 | 	    sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1662 |  	                              &qe->condat);
1663 | 	}
1664 | 	
1665 | 	strcat(allnames, name);
1666 | 	strcat(allnames, " ");
1667 | 	oldid = thisid;
1668 | 	wr_free(id);
1669 | 	wr_free(name);
1670 |       }
1671 |       /* also do the last name */
1672 |       if (!sql_error) {
1673 |           sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1674 | 	                            &qe->condat);
1675 |       }
1676 |       
1677 |       SQ_free_result(result); /* we can do it only because the watchdog */
1678 |       /* has not started between the check for non-NULL result and here */
1679 |     }
1680 |     
1681 |     /* if we've lost connection, don't bother with this extra work */
1682 |     if (!sql_error && (qe->condat.rtc == 0)) {
1683 |         /* now copy things back to the main temporary table   */
1684 |         sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s", 
1685 | 	       id_table, rec_table);
1686 |         if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1687 | 	    sql_error = SQ_errno(*sql_connection);
1688 | 	    report_sql_error(&qe->condat, *sql_connection, sql_command);
1689 | 	}
1690 |     }
1691 | 
1692 |     /* Now drop the IDS recursive table (try to do this even if
1693 |        we had an SQL error, to avoid leaving extra tables lying around) */
1694 |     sprintf(sql_command, "DROP TABLE IF EXISTS %s", rec_table);
1695 |     if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1696 |         sql_error = SQ_errno(*sql_connection);
1697 | 	report_sql_error(&qe->condat, *sql_connection, sql_command);
1698 |     }
1699 | 
1700 |     /* return error, if any */
1701 |     return sql_error;
1702 | }
1703 | /* qi_fetch_references */ 
1704 | 
1705 | 
1706 | /* QI_execute() */
1707 | /*++++++++++++++++++++++++++++++++++++++
1708 |   Execute the query instructions.  This is called for each source.
1709 |   This is linked into MySQL by the fact that MySQL doesn't have sub selects
1710 |  (yet).  The queries are done in two stages.  Make some temporary tables and
1711 |   insert into them.  Then use them in the next select.
1712 | 
1713 |    
1714 |   ca_dbSource_t *dbhdl            source-specific identifier (defined in CA)
1715 | 
1716 |   Query_instructions *qis         query instructions.
1717 | 				 
1718 |   Query_environ *qe               query environment.
1719 | 				 
1720 |   acc_st *acc_credit              object display credit 
1721 | 				 
1722 |   acl_st *acl                     copy of the original acl for this client 
1723 | 
1724 |   More:
1725 |   +html+ <PRE>
1726 |   Authors:
1727 |         ottrey - original version,
1728 | 	marek - the rest.
1729 |   +html+ </PRE>
1730 | ++++++++++++++++++++++++++++++++++++++*/
1731 | er_ret_t QI_execute(ca_dbSource_t *dbhdl,
1732 | 		    Query_instructions *qis, 
1733 | 		    Query_environ *qe,	
1734 | 		    acc_st *acc_credit,
1735 | 		    acl_st *acl
1736 | 		    ) 
1737 | {
1738 |   /* those things must be freed after use! */
1739 |   char *dbhost = ca_get_srcdbmachine(dbhdl);
1740 |   char *dbname = ca_get_srcdbname(dbhdl);
1741 |   char *dbuser = ca_get_srcdbuser(dbhdl);
1742 |   char *dbpass = ca_get_srcdbpassword(dbhdl);
1743 |   char *srcnam;
1744 |   unsigned dbport = ca_get_srcdbport(dbhdl);
1745 |   char id_table[STR_S];
1746 |   char sql_command[STR_XL];
1747 |   GList *datlist=NULL;
1748 |   SQ_connection_t *sql_connection=NULL;
1749 |   int sql_error;
1750 | 
1751 |   sql_connection = SQ_get_connection( dbhost, dbport,
1752 | 				      dbname, dbuser, dbpass );
1753 |   /* free parameters when done */
1754 |   wr_free(dbhost);
1755 |   wr_free(dbuser);
1756 |   wr_free(dbpass);
1757 | 
1758 |   /* return error if occurred */
1759 |   if (sql_connection == NULL) {
1760 |     ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s", 
1761 | 	      dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1762 |     wr_free(dbname);
1763 |     return QI_CANTDB;
1764 |   }
1765 |   wr_free(dbname);
1766 | 
1767 |   /* from here on out, we use the sql_error flag to verify our 
1768 |      connection to the SQL database is still good */
1769 |   sql_error = 0;
1770 | 
1771 |   sprintf(id_table, "ID_%ld_%d",   mysql_thread_id(sql_connection),
1772 | 	  pthread_self());
1773 | 
1774 |   /* see if there was a leftover table from a crashed session 
1775 |    * (assume the ID cannot be currently in use)
1776 |    */
1777 |   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1778 |   if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1779 |       sql_error = SQ_errno(sql_connection);
1780 |       report_sql_error(&qe->condat, sql_connection, sql_command);
1781 |   }
1782 |   
1783 |   /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1784 |   if (!sql_error) {
1785 |       sprintf(sql_command, 
1786 |               "CREATE " TEMPORARY 
1787 | 	      " TABLE %s ( id INT PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1788 |       if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1789 |           sql_error = SQ_errno(sql_connection);
1790 |           report_sql_error(&qe->condat, sql_connection, sql_command);
1791 |       }
1792 |   }
1793 | 
1794 |   if (!sql_error) { 
1795 |       srcnam = ca_get_srcname(dbhdl);
1796 |       sql_error = qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, 
1797 |                                  id_table, &datlist, acc_credit, acl);
1798 |       wr_free(srcnam);
1799 |   }
1800 | 
1801 |   /* post-processing */
1802 |   if (!sql_error && (qis->filtered == 0)) {
1803 |       /* start the watchdog just to set the rtc flag */
1804 |       SK_watch_setclear(&(qe->condat));
1805 |       SK_watchstart(&(qe->condat));
1806 | 
1807 |       /* add radix results (only if -K is not active and still connected) */
1808 |       if (qe->condat.rtc == 0) {
1809 |           sql_error = insert_radix_serials(&(qe->condat), 
1810 | 	                                   sql_connection, 
1811 | 	                                   id_table, 
1812 | 					   datlist);
1813 |       }
1814 | 
1815 |       SK_watchstop(&(qe->condat));
1816 |   }
1817 | 
1818 |   /* fetch recursive objects (ac,tc,zc,ah) */
1819 |   if (!sql_error && qis->recursive && (qe->condat.rtc == 0)) {
1820 |       sql_error = qi_fetch_references(&sql_connection, 
1821 |                                       qe, 
1822 | 				      id_table, 
1823 | 				      acc_credit, 
1824 | 				      acl);
1825 |   } /* if recursive */
1826 |   
1827 |   /* display */
1828 |   /* -K filtering: 
1829 |    * right now only filtering, no expanding sets like write_set_objects() 
1830 |    */
1831 |   
1832 |   /* display the immediate data from the radix tree */
1833 |   if (!sql_error && (qis->filtered == 1)) {
1834 |     write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
1835 |   }
1836 | 
1837 |   /* display objects from the IDs table */
1838 |   if (!sql_error) {
1839 |       sql_error = write_objects( &sql_connection, id_table, qis->filtered,
1840 | 		                qis->fast, &(qe->condat), acc_credit, acl);
1841 |   }
1842 | 
1843 |   /* drop the table */
1844 |   /* try to do this, even if there is an SQL error */
1845 |   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1846 |   if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
1847 |       sql_error = SQ_errno(sql_connection);
1848 |       report_sql_error(&qe->condat, sql_connection, sql_command);
1849 |   }
1850 | 
1851 |   /* Now disconnect (temporary tables get dropped automatically) */
1852 |   SQ_close_connection(sql_connection);  
1853 | 
1854 |   /* return appropriate value */
1855 |   if (sql_error) {
1856 |      return QI_SQLERR;
1857 |   } else {
1858 |      return QI_OK;
1859 |   }
1860 | } /* QI_execute() */
1861 | 
1862 | 
1863 | /* instruction_free() */
1864 | /*++++++++++++++++++++++++++++++++++++++
1865 |   Free the instruction.
1866 | 
1867 |   Query_instruction *qi query_instruction to be freed.
1868 |    
1869 |   More:
1870 |   +html+ <PRE>
1871 |   Authors:
1872 |         ottrey
1873 |   +html+ </PRE>
1874 |   ++++++++++++++++++++++++++++++++++++++*/
1875 | static void instruction_free(Query_instruction *qi) {
1876 |   if (qi != NULL) {
1877 |     if (qi->query_str != NULL) {
1878 |       wr_free(qi->query_str);
1879 |     }
1880 |     wr_free(qi);
1881 |   }
1882 | } /* instruction_free() */
1883 | 
1884 | /* QI_free() */
1885 | /*++++++++++++++++++++++++++++++++++++++
1886 |   Free the query_instructions.
1887 | 
1888 |   Query_instructions *qis Query_instructions to be freed.
1889 |    
1890 |   More:
1891 |   +html+ <PRE>
1892 |   Authors:
1893 |         ottrey, marek
1894 |   +html+ </PRE>
1895 |   ++++++++++++++++++++++++++++++++++++++*/
1896 | void QI_free(Query_instructions *qis) {
1897 |   int i;
1898 | 
1899 |   for (i=0; qis->instruction[i] != NULL; i++) {
1900 |     instruction_free(qis->instruction[i]);
1901 |   } 
1902 | 
1903 |   if (qis != NULL) {
1904 |     wr_free(qis);
1905 |   }
1906 | 
1907 | } /* QI_free() */
1908 | 
1909 | /*++++++++++++++++++++++++++++++++++++++
1910 |   Determine if this query should be conducted or not.
1911 | 
1912 |   If it was an inverse query - if the attribute appears in the query command's bitmap.
1913 |   If it was a lookup query - if the attribute appears in the object type bitmap or
1914 |                              disregard if there is no object_type bitmap (Ie object filter).
1915 | 
1916 |   mask_t bitmap The bitmap of attribute to be converted.
1917 |    
1918 |   const Query_command *qc  The query_command that the instructions are created
1919 |                            from.
1920 |   
1921 |   const Query_t q          The query being considered.
1922 |   +html+ <PRE>
1923 |   Authors:
1924 |         ottrey,
1925 | 	marek.
1926 |   +html+ </PRE>
1927 |   ++++++++++++++++++++++++++++++++++++++*/
1928 | static int valid_query(const Query_command *qc, const Query_t q) {
1929 |   int result=0;
1930 | 
1931 |   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
1932 |     if (q.query != NULL) {
1933 |       switch (q.querytype) {
1934 |         case Q_INVERSE:
1935 |           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
1936 |             result = 1;
1937 |           }
1938 |         break;
1939 | 
1940 |         case Q_LOOKUP:
1941 | 	  if (q.class == C_ANY 
1942 | 	      || MA_isset(qc->object_type_bitmap, (unsigned) q.class)) {
1943 |             result=1;
1944 |           }
1945 |         break;
1946 | 
1947 |         default:
1948 |           /* XXX */fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
1949 |       }
1950 |     }
1951 |   }
1952 | 
1953 |   return result;
1954 | } /* valid_query() */
1955 | 
1956 | /* QI_new() */
1957 | /*++++++++++++++++++++++++++++++++++++++
1958 |   Create a new set of query_instructions. Returns an allocated structure which
1959 |   must be freed after use with QI_free().
1960 | 
1961 |   const Query_command *qc The query_command that the instructions are created
1962 |                           from.
1963 | 
1964 |   const Query_environ *qe The environmental variables that they query is being
1965 |                           performed under.
1966 |   
1967 |   +html+ <PRE>
1968 |   Authors:
1969 |         ottrey,
1970 | 	marek.
1971 |   +html+ </PRE>
1972 |   ++++++++++++++++++++++++++++++++++++++*/
1973 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
1974 |   Query_instructions *qis=NULL;
1975 |   Query_instruction *qi=NULL;
1976 |   int i_no=0;
1977 |   int i;
1978 |   char *query_str;
1979 | 
1980 |   dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
1981 | 
1982 |   qis->filtered = qc->filtered;
1983 |   qis->fast = qc->fast;
1984 |   qis->recursive = qc->recursive;
1985 |   qis->qc = (qc);
1986 | 
1987 |   
1988 |   for (i=0; Query[i].query != NULL; i++) {
1989 | 
1990 |     /* If a valid query. */
1991 |     if ( valid_query(qc, Query[i]) == 1) {
1992 | 
1993 |       dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
1994 | 
1995 |       qi->queryindex = i;
1996 | 
1997 |       /* SQL Query */
1998 |       if ( Query[i].refer == R_SQL) {
1999 |         qi->search_type = R_SQL;
2000 |         query_str = create_query(Query[i], qc);
2001 | 
2002 |         if (query_str!= NULL) {
2003 |           qi->query_str = query_str;
2004 |           qis->instruction[i_no++] = qi;
2005 |         }
2006 |       }
2007 |       /* Radix Query */
2008 |       else if (Query[i].refer == R_RADIX) {
2009 |         qi->search_type = R_RADIX;
2010 | 	
2011 |         if (map_qc2rx(qi, qc) == 1) {
2012 | 	  int j;
2013 | 	  int found=0;
2014 | 	  
2015 |           /* check that there is no such query yet, for example if
2016 | 	     more than one keytype (wk) matched */
2017 | 	  for (j=0; j<i_no; j++) {
2018 | 	    Query_instruction *qij = qis->instruction[j];
2019 | 	    
2020 | 	    if(    qij->search_type == R_RADIX
2021 | 		   && Query[qij->queryindex].attribute 
2022 | 		   == Query[qi ->queryindex].attribute) {
2023 | 	      
2024 |               found=1;
2025 |               break;
2026 |             }
2027 |           }
2028 | 	  
2029 |           if ( found ) {
2030 |             /* Discard the Query Instruction */
2031 |             wr_free(qi);
2032 |           } 
2033 |           else {
2034 |             /* Add the query_instruction to the array */
2035 |             qis->instruction[i_no++] = qi;
2036 |           }
2037 |         }
2038 |       }
2039 |       else {
2040 | 	  /* ERROR: bad search_type */
2041 | 	  die;
2042 |       }
2043 |     }
2044 |   }
2045 |   qis->instruction[i_no++] = NULL;
2046 | 
2047 | 
2048 |   {  /* tracing */
2049 |       char *descrstr = QI_queries_to_string(qis);
2050 | 
2051 |       ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
2052 |       wr_free( descrstr );
2053 |   }
2054 | 
2055 |   return qis;
2056 | 
2057 | } /* QI_new() */
2058 | 
2059 | 
2060 |  
2061 | 
2062 | 
2063 | /*++++++++++++++++++++++++++++++++++++++
2064 |   
2065 |   char *QI_queries_to_string    returns a list of descriptions for queries 
2066 |                                 that will be performed (debugging only).
2067 | 				Allocated text, must be freed after use.
2068 | 
2069 |   Query_instructions *qis       query instructions structure
2070 | 
2071 |   Author:
2072 |      marek.
2073 |   ++++++++++++++++++++++++++++++++++++++*/
2074 | 
2075 | char *QI_queries_to_string(Query_instructions *qis)
2076 | {
2077 |    Query_instruction *qi;
2078 |    int i;
2079 |    char *resstr = NULL;
2080 | 
2081 |    dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK);
2082 |    strcpy(resstr, "{");
2083 | 
2084 |    for( i = 0; ( qi=qis->instruction[i] ) != NULL;  i++ ) {
2085 |        char *descr = Query[qi->queryindex].descr;
2086 |        int oldres = strlen( resstr );
2087 |        
2088 |        dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK);
2089 |        strcat(resstr, descr);
2090 |        strcat(resstr, ",");
2091 |    }
2092 |    if( i>0 ) {
2093 |        /* cancel the last comma */
2094 |        resstr[strlen(resstr)-1] = 0;
2095 |    }
2096 | 
2097 |    dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 ) 
2098 | 	  != UT_OK);
2099 |    strcat(resstr, "}");
2100 |    
2101 |    return resstr;
2102 | }