## modules/qi/query_instructions.c

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. qi_kill_body
2. sql_execute_watched
3. create_name_query
4. create_asblock_query
5. add_filter
6. create_query
7. QI_fast_output
8. filter
9. write_results
10. report_sql_error
11. __report_sql_error
12. write_objects
13. insert_radix_serials
14. write_radix_immediate
15. map_qc2rx
16. run_referral
17. qi_prep_run_refer
18. qi_collect_domain
19. add_ref_name
20. qi_collect_ids
21. qi_fetch_references
22. QI_execute
23. instruction_free
24. QI_free
25. valid_query
26. QI_new
27. QI_queries_to_string

       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)
         /* [<][>][^][v][top][bottom][index][help] */
      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,
         /* [<][>][^][v][top][bottom][index][help] */
     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) {
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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) {
         /* [<][>][^][v][top][bottom][index][help] */
     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) {
         /* [<][>][^][v][top][bottom][index][help] */
     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)
         /* [<][>][^][v][top][bottom][index][help] */
     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) {
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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) \
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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) {
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
     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,
         /* [<][>][^][v][top][bottom][index][help] */
    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,
         /* [<][>][^][v][top][bottom][index][help] */
    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,
         /* [<][>][^][v][top][bottom][index][help] */
    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,
         /* [<][>][^][v][top][bottom][index][help] */
    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,
         /* [<][>][^][v][top][bottom][index][help] */
    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,
         /* [<][>][^][v][top][bottom][index][help] */
    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) {
         /* [<][>][^][v][top][bottom][index][help] */
    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) {
         /* [<][>][^][v][top][bottom][index][help] */
    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) {
         /* [<][>][^][v][top][bottom][index][help] */
    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) {
         /* [<][>][^][v][top][bottom][index][help] */
    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)
         /* [<][>][^][v][top][bottom][index][help] */
    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 }

/* [<][>][^][v][top][bottom][index][help] */