## 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. fast_output
8. filter
9. write_results
10. write_objects
11. insert_radix_serials
12. write_radix_immediate
13. map_qc2rx
14. run_referral
15. add_ref_name
16. qi_collect_ids
17. qi_fetch_references
18. QI_execute
19. instruction_free
20. QI_free
21. valid_query
22. QI_new
23. QI_queries_to_string

       1 /***************************************
       2   $Revision: 1.46 $
       3
       4
       5   Sql module (sq).  This is a mysql implementation of an sql module.
       6
       7   Status: NOT REVUED, NOT TESTED
       8
       9   Note: this code has been heavily coupled to MySQL, and may need to be changed
      10   (to improve performance) if a new RDBMS is used.
      11
      12   ******************/ /******************
      13   Filename            : query_instructions.c
      14   Author              : ottrey@ripe.net
      15   OSs Tested          : Solaris
      16   Problems            : Moderately linked to MySQL.  Not sure which inverse
      17                         attributes each option has.  Would like to modify this
      18                         after re-designing the objects module.
      19   Comments            : Not sure about the different keytypes.
      20   ******************/ /******************
      21   Copyright (c) 1999                              RIPE NCC
      22
      23   All Rights Reserved
      24
      25   Permission to use, copy, modify, and distribute this software and its
      26   documentation for any purpose and without fee is hereby granted,
      27   provided that the above copyright notice appear in all copies and that
      28   both that copyright notice and this permission notice appear in
      29   supporting documentation, and that the name of the author not be
      30   used in advertising or publicity pertaining to distribution of the
      31   software without specific, written prior permission.
      32
      33   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
      34   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
      35   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
      36   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
      37   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      38   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      39   ***************************************/
      40 #include <stdio.h>
      41 #include <string.h>
      42 #include <glib.h>
      43
      44 #include "which_keytypes.h"
      45 #include "query_instructions.h"
      46 #include "mysql_driver.h"
      47 #include "rp.h"
      48 #include "stubs.h"
      49 #include "constants.h"
      50 #include "memwrap.h"
      51 #include "wh_queries.h"
      52
      53
      54
      55 /*+ String sizes +*/
      56 #define STR_S   63
      57 #define STR_M   255
      58 #define STR_L   1023
      59 #define STR_XL  4095
      60 #define STR_XXL 16383
      61
      62 /* XXX this must be removed from here!!! a .h file must be
      63    generated from xml */
      64
      65 #include "defs.h"
      66
      67 /* body of the query thread.
      68
      69    takes a ptr to structure with all arguments.
      70    returns an int (result of sq_execute_query) cast to (void*)
      71
      72    by marek
      73 */
      74 static
      75 void *qi_kill_body(void *arg)
         /* [<][>][^][v][top][bottom][index][help] */
      76 {
      77   SQ_connection_t *sql_connection = arg;
      78   ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
      79               "rtc: killing SQL connection %d", (sql_connection)->thread_id);
      80   /* abort the running query */
      81   SQ_abort_query(sql_connection);
      82 }
      83
      84 /*
      85    wrapper around sq_execute_query: starts a query
      86    in a separate thread and starts the socket watcher to cancel the query
      87    if the socket is closed.
      88
      89    by marek
      90 */
      91 int sql_execute_watched(sk_conn_st *condat, SQ_connection_t **sql_connection,
         /* [<][>][^][v][top][bottom][index][help] */
      92                         const char *query, SQ_result_set_t **result_ptr)
      93 {
      94   int retval; /* return value of sq_execute_query */
      95   SQ_connection_t *tempcon;
      96
      97   /* make clean */
      98   SK_watchclear(condat);
      99
     100   /* set watchdog to execute the abort function */
     101   SK_watchexec(condat, qi_kill_body, *sql_connection);
     102
     103   /* start the watchdog */
     104   SK_watchstart(condat);
     105
     106   /* start query. An error may be returned if the query is aborted */
     107   retval = SQ_execute_query(*sql_connection, query, result_ptr);
     108
     109   /* but short queries will complete before the watchdog kills the
     110      connection */
     111
     112   SK_watchstop(condat);
     113
     114   /* if the watchdog triggered, then it is guaranteed that
     115      the kill_body function was invoked and therefore the sql-connection
     116      is now unusable...
     117      Close and reopen it for cleanup, use temporary connection
     118      to keep the login details */
     119   if( condat->rtc != 0 ) {
     120     /* can't rely on the error code from mysql!
     121      */
     122
     123     /* one thing: this code must be entered ONLY if the kill_body
     124        thing was invoked by the watchdog.
     125     */
     126
     127     /* if result is defined, free it here before destroying the
     128        asssociated connection */
     129     if( retval == 0 && result_ptr && *result_ptr ) {
     130       SQ_free_result( *result_ptr );
     131       *result_ptr = NULL;
     132     }
     133
     134     tempcon = SQ_duplicate_connection(*sql_connection);
     135
     136     ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
     137               "rtc: closing SQL thread %d", (*sql_connection)->thread_id);
     138     SQ_close_connection(*sql_connection);
     139
     140     *sql_connection = SQ_duplicate_connection(tempcon);
     141     ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
     142               "rtc: reopened as thread %d", (*sql_connection)->thread_id);
     143
     144     ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
     145               "rtc: closing SQL temporary thread %d", tempcon->thread_id);
     146     SQ_close_connection(tempcon);
     147
     148     /* make it look as if there was no error and
     149        the result is empty */
     150     retval = 0;
     151   }
     152
     153
     154   return retval;
     155 }
     156
     157 /* create_name_query() */
     158 /*++++++++++++++++++++++++++++++++++++++
     159   Create an sql query for the names table.
     160
     161   char *query_str
     162
     163   const char *sql_query
     164
     165   const char *keys
     166
     167   More:
     168   +html+ <PRE>
     169   Authors:
     170   ottrey
     171   +html+ </PRE><DL COMPACT>
     172   +html+ <DT>Online References:
     173   +html+ <DD><UL>
     174   +html+ </UL></DL>
     175
     176   ++++++++++++++++++++++++++++++++++++++*/
     177 static void create_name_query(char *query_str, const char *sql_query, const char *keys) {
         /* [<][>][^][v][top][bottom][index][help] */
     178   int i;
     179   /* Allocate stuff */
     180   GString *from_clause = g_string_sized_new(STR_L);
     181   GString *where_clause = g_string_sized_new(STR_L);
     182   gchar **words = g_strsplit(keys, " ", 0);
     183
     184   /* double quotes " are used in queries to allow querying for
     185      names like O'Hara */
     186
     187   g_string_sprintfa(from_clause, "names N%.2d", 0);
     188   g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
     189
     190   for (i=1; words[i] != NULL; i++) {
     191     g_string_sprintfa(from_clause, ", names N%.2d", i);
     192     g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i);
     193   }
     194
     195   sprintf(query_str, sql_query, from_clause->str, where_clause->str);
     196
     197   /* Free up stuff */
     198   g_strfreev(words);
     199   g_string_free(where_clause,/* CONSTCOND */ TRUE);
     200   g_string_free(from_clause, /* CONSTCOND */ TRUE);
     201
     202 } /* create_name_query() */
     203
     204 /*+ create_asblock_query:
     205
     206   given a string like: AS1
     207                        AS1 - AS10
     208                        AS1-AS10
     209   construct a range query for the as_block table
     210 */
     211 static int create_asblock_query(char *query_str,
         /* [<][>][^][v][top][bottom][index][help] */
     212                                 const char *sql_query,
     213                                 const char *keys) {
     214   char *keycopy = wr_string(keys);
     215   char *token, *cursor = keycopy;
     216   int  asnums[2] = {0,0};
     217   int index = 0; /* index into the asnums array */
     218
     219
     220   while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {
     221     /* discard the letters (or leading whitespace), take the number */
     222     if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
     223       return -1; /* error */
     224     }
     225   }
     226   /* if only beginning was supplied, copy it as end */
     227   if( index == 1 ) {
     228     asnums[1] = asnums[0];
     229   }
     230
     231   /* now construct the query */
     232   sprintf(query_str, sql_query, asnums[0], asnums[1]);
     233
     234   wr_free(keycopy);
     235   return 0;
     236 }
     237
     238 static void add_filter(char *query_str, const Query_command *qc) {
         /* [<][>][^][v][top][bottom][index][help] */
     239   int i;
     240   int qlen;
     241   char filter_atom[STR_M];
     242
     243 /*
     244   if (MA_bitcount(qc->object_type_bitmap) > 0) {
     245     g_string_sprintfa(query_str, " AND (");
     246     for (i=0; i < C_END; i++) {
     247       if (MA_isset(qc->object_type_bitmap, i)) {
     248         g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
     249       }
     250     }
     251     g_string_truncate(query_str, query_str->len-3);
     252     g_string_append_c(query_str, ')');
     253   }
     254 */
     255   if (MA_bitcount(qc->object_type_bitmap) > 0) {
     256     strcat(query_str, " AND (");
     257     for (i=0; i < C_END; i++) {
     258       if (MA_isset(qc->object_type_bitmap, i)) {
     259         strcpy(filter_atom, "");
     260         sprintf(filter_atom, "i.object_type = %d OR ", i);
     261                         /* XXX class codes should be used instead:
     262                            DF_get_class_dbase_code(i))
     263                            but currently the tables contain values of enums
     264                            (C_IN, etc) and not codes
     265                         */
     266         strcat(query_str, filter_atom);
     267       }
     268     }
     269     qlen = strlen(query_str);
     270     query_str[qlen-3] = ')';
     271     query_str[qlen-2] = '\0';
     272     query_str[qlen-1] = '\0';
     273   }
     274
     275 } /* add_filter() */
     276
     277 /* create_query() */
     278 /*++++++++++++++++++++++++++++++++++++++
     279   Create an sql query from the query_command and the matching keytype and the
     280   selected inverse attributes.
     281   Note this clears the first inv_attribute it sees, so is called sequentially
     282   until there are no inv_attributes left.
     283
     284   WK_Type keytype The matching keytype.
     285
     286   const Query_command *qc The query command.
     287
     288   mask_t *inv_attrs_bitmap The selected inverse attributes.
     289
     290   More:
     291   +html+ <PRE>
     292   Authors:
     293         ottrey
     294   +html+ </PRE><DL COMPACT>
     295   +html+ <DT>Online References:
     296   +html+ <DD><UL>
     297   +html+ </UL></DL>
     298
     299   ++++++++++++++++++++++++++++++++++++++*/
     300 static char *create_query(const Query_t q, const Query_command *qc) {
         /* [<][>][^][v][top][bottom][index][help] */
     301   char *result=NULL;
     302   char result_buff[STR_XL];
     303   Q_Type_t querytype;
     304   int addquery = 0; /* controls if the query should be added to the list */
     305
     306   if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
     307     querytype = Q_INVERSE;
     308   }
     309   else {
     310     querytype = Q_LOOKUP;
     311   }
     312
     313   if ( (q.query != NULL)
     314     && (q.querytype == querytype) ) {
     315
     316     addquery = 1; /* if it got here, it should be added, unless.(see asblock)*/
     317
     318     if (q.keytype == WK_NAME) {
     319       /* Name queries require special treatment. */
     320        create_name_query(result_buff, q.query, qc->keys);
     321     }
     322     else if( q.keytype == WK_IPADDRESS ) {  /* ifaddr sql lookups */
     323         ip_range_t myrang;
     324         unsigned   begin, end;
     325         ip_keytype_t key_type;
     326
     327         if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
     328             if(IP_rang_b2_space(&myrang) == IP_V4 ) {
     329                 IP_rang_b2v4(&myrang, &begin, &end);
     330                 sprintf(result_buff, q.query, begin, end);
     331             }
     332             else {
     333                 die;
     334             }
     335         }
     336     }
     337     else if( q.keytype == WK_ASRANGE ) {   /* as_block range composition */
     338       if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
     339         addquery = 0; /* ... unless it's not correct */
     340       }
     341     }
     342     else {
     343       sprintf(result_buff, q.query, qc->keys);
     344     }
     345
     346     if (q.class == -1 && addquery == 1 ) {
     347       /* It is class type ANY so add the object filtering */
     348       add_filter(result_buff, qc);
     349     }
     350   }
     351
     352   if( addquery == 1 ) {
     353     dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
     354     strcpy(result, result_buff);
     355     return result;
     356   }
     357   else {
     358     return NULL;
     359   }
     360 } /* create_query() */
     361
     362 /* fast_output() */
     363 /*++++++++++++++++++++++++++++++++++++++
     364   This is for the '-F' flag.
     365   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
     366
     367   Fast isn't fast anymore - it's just there for compatibility reasons.
     368   This could be speed up if there were breaks out of the loops, once it matched something.
     369   (Wanna add a goto Marek?  :-) ).
     370
     371   const char *string The string to be "fast outputed".
     372
     373   More:
     374   +html+ <PRE>
     375   Authors:
     376         ottrey
     377   +html+ </PRE><DL COMPACT>
     378   +html+ <DT>Online References:
     379   +html+ <DD><UL>
     380   +html+ </UL></DL>
     381
     382   ++++++++++++++++++++++++++++++++++++++*/
     383
     384 char *fast_output(const char *str)
         /* [<][>][^][v][top][bottom][index][help] */
     385 {
     386 int i,j;
     387 char *result;
     388 char result_bit[STR_L];
     389 char result_buff[STR_XL];
     390 gchar **lines = g_strsplit(str, "\n", 0);
     391 char * const *attribute_names;
     392 gboolean filtering_an_attribute = FALSE;
     393 char *value;
     394
     395 attribute_names = DF_get_attribute_names();
     396
     397 strcpy(result_buff, "");
     398  for (j=0; lines[j] != NULL; j++) {
     399    for(i=0; attribute_names[i] != NULL; i++) {
     400      if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) {
     401        strcpy(result_bit, "");
     402        /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
     403        value = strchr(lines[j], ':');
     404        value++;
     405        /* Now get rid of whitespace. */
     406        while (*value == ' ' || *value == '\t') {
     407          value++;
     408        }
     409        sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value);
     410        strcat(result_buff, result_bit);
     411      }
     412      /* CONSTCOND */
     413      else if (filtering_an_attribute == TRUE) {
     414        switch (lines[j][0]) {
     415        case ' ':
     416        case '\t':
     417        case '+':
     418          strcpy(result_bit, "");
     419          sprintf(result_bit, "%s\n", lines[j]);
     420          strcat(result_buff, result_bit);
     421          break;
     422
     423        default:
     424          filtering_an_attribute = FALSE;
     425        }
     426      }
     427    }
     428  }
     429
     430
     431  dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
     432
     433  strcpy(result, result_buff);
     434
     435  return result;
     436 } /* fast_output() */
     437
     438 /* filter() */
     439 /*++++++++++++++++++++++++++++++++++++++
     440   Basically it's for the '-K' flag for non-set (and non-radix) objects.
     441   It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
     442
     443   This could be speed up if there were breaks out of the loops, once it matched something.
     444   (Wanna add a goto Marek?  :-) ).
     445
     446   const char *string The string to be filtered.
     447
     448   More:
     449   +html+ <PRE>
     450   Authors:
     451         ottrey
     452   +html+ </PRE><DL COMPACT>
     453   +html+ <DT>Online References:
     454   +html+ <DD><UL>
     455   +html+ </UL></DL>
     456
     457   ++++++++++++++++++++++++++++++++++++++*/
     458 char *filter(const char *str) {
         /* [<][>][^][v][top][bottom][index][help] */
     459   int i,j, passed=0;
     460   char *result;
     461   char result_bit[STR_L];
     462   char result_buff[STR_XL];
     463   gchar **lines = g_strsplit(str, "\n", 0);
     464   char * const *filter_names;
     465   gboolean filtering_an_attribute = FALSE;
     466
     467   filter_names = DF_get_filter_names();
     468
     469   strcpy(result_buff, "");
     470   for (i=0; filter_names[i] != NULL; i++) {
     471     for (j=0; lines[j] != NULL; j++) {
     472       if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
     473         strcpy(result_bit, "");
     474         sprintf(result_bit, "%s\n", lines[j]);
     475         strcat(result_buff, result_bit);
     476         passed++;
     477
     478         /* can someone explain where %^&()! lint sees the condition here ? */
     479         /* CONSTCOND */
     480         filtering_an_attribute = TRUE;
     481       }
     482       /* CONSTCOND */
     483       else if (filtering_an_attribute == TRUE) {
     484         switch (lines[j][0]) {
     485           case ' ':
     486           case '\t':
     487           case '+':
     488             strcpy(result_bit, "");
     489             sprintf(result_bit, "%s\n", lines[j]);
     490             strcat(result_buff, result_bit);
     491           break;
     492
     493           default:
     494             filtering_an_attribute = FALSE;
     495         }
     496       }
     497     }
     498   }
     499
     500   if(passed) {
     501     strcat(result_buff, "\n");
     502   }
     503
     504   dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
     505   strcpy(result, result_buff);
     506
     507   return result;
     508 } /* filter() */
     509
     510 /* write_results() */
     511 /*++++++++++++++++++++++++++++++++++++++
     512   Write the results to the client socket.
     513
     514   SQ_result_set_t *result The result set returned from the sql query.
     515   unsigned filtered       if the objects should go through a filter (-K)
     516   sk_conn_st *condat      Connection data for the client
     517
     518   XXX NB. this is very dependendant on what rows are returned in the result!!!
     519
     520   More:
     521   +html+ <PRE>
     522   Authors:
     523         ottrey
     524   +html+ </PRE><DL COMPACT>
     525   +html+ <DT>Online References:
     526   +html+ <DD><UL>
     527   +html+ </UL></DL>
     528
     529   ++++++++++++++++++++++++++++++++++++++*/
     530 static int write_results(SQ_result_set_t *result,
         /* [<][>][^][v][top][bottom][index][help] */
     531                          unsigned filtered,
     532                          unsigned fast,
     533                          sk_conn_st *condat,
     534                          acc_st    *acc_credit,
     535                          acl_st    *acl
     536                          ) {
     537   SQ_row_t *row;
     538   char *str;
     539   char *filtrate;
     540   char *fasted;
     541   int retrieved_objects=0;
     542   char *objt;
     543   int type;
     544
     545   /* Get all the results - one at a time */
     546   if (result != NULL) {
     547     /* here we are making use of the mysql_store_result capability
     548        of interrupting the cycle of reading rows. mysql_use_result
     549        would not allow that, would have to be read until end */
     550
     551     while ( condat->rtc == 0
     552             && AC_credit_isdenied( acc_credit ) == 0
     553             && (row = SQ_row_next(result)) != NULL ) {
     554
     555       if (  (str = SQ_get_column_string(result, row, 0)) == NULL
     556             || (objt = SQ_get_column_string(result, row, 3)) == NULL )  {
     557         /* handle it somehow ? */
     558         die;
     559       }
     560       else  {
     561         /* get + add object type */
     562         type = atoi(objt);
     563
     564         /* ASP_QI_LAST_DET */
     565         ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
     566                   "Retrieved serial id = %d , type = %s", atoi(str), objt);
     567
     568         wr_free(str);
     569         wr_free(objt);
     570       }
     571
     572       /* decrement credit for accounting purposes */
     573       AC_count_object( acc_credit, acl,
     574                        type == C_PN || type == C_RO ); /* is private? */
     575
     576       /* break the loop if the credit has just been exceeded and
     577          further results denied */
     578       if( AC_credit_isdenied( acc_credit ) ) {
     579         continue;
     580       }
     581
     582       if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; }
     583       else {
     584
     585         /* The fast output stage */
     586         if (fast == 1) {
     587           fasted = fast_output(str);
     588           wr_free(str);
     589           str = fasted;
     590         }
     591
     592         /* The filtering stage */
     593         if (filtered == 0) {
     594           SK_cd_puts(condat, str);
     595           SK_cd_puts(condat, "\n");
     596         }
     597         else {
     598
     599           /* XXX accounting should be done AFTER that, and not for objects
     600              filtered out */
     601
     602           filtrate = filter(str);
     603           SK_cd_puts(condat, filtrate);
     604           wr_free(filtrate);
     605         }
     606         retrieved_objects++;
     607       }
     608       wr_free(str);
     609     }
     610   }
     611
     612   return retrieved_objects;
     613 } /* write_results() */
     614
     615 /* write_objects() */
     616 /*++++++++++++++++++++++++++++++++++++++
     617   This is linked into MySQL by the fact that MySQL doesn't have sub selects
     618   (yet).  The queries are done in two stages.  Make some temporary tables and
     619   insert into them.  Then use them in the next select.
     620
     621   SQ_connection_t *sql_connection The connection to the database.
     622
     623   char *id_table The id of the temporary table (This is a result of the hacky
     624                   way we've tried to get MySQL to do sub-selects.)
     625
     626   sk_conn_st *condat  Connection data for the client
     627
     628   More:
     629   +html+ <PRE>
     630   Authors:
     631         ottrey
     632   +html+ </PRE><DL COMPACT>
     633   ++++++++++++++++++++++++++++++++++++++*/
     634 static void write_objects(SQ_connection_t **sql_connection,
         /* [<][>][^][v][top][bottom][index][help] */
     635                           char *id_table,
     636                           unsigned int filtered,
     637                           unsigned int fast,
     638                           sk_conn_st *condat,
     639                           acc_st    *acc_credit,
     640                           acl_st    *acl
     641                           )
     642 {
     643   /* XXX This should really return a linked list of the objects */
     644
     645   SQ_result_set_t *result, *order_res;
     646   SQ_row_t *order_row;
     647   int retrieved_objects=0;
     648   char sql_command[STR_XL];
     649
     650 #if 0
     651   SQ_execute_query( *sql_connection, "SELECT object_type FROM object_order ORDER BY order_code", &order_res );
     652   while( (order_row = SQ_row_next(order_res)) != NULL ) {
     653     char *object_type = SQ_get_column_string(order_res, order_row, 0);
     654     sprintf(sql_command, Q_OBJECTS, id_table, object_type);
     655
     656     exec/write
     657   }
     658   SQ_free_result(order_res);
     659 #endif
     660
     661   sprintf(sql_command, Q_OBJECTS, id_table);
     662
     663   dieif(sql_execute_watched(condat, sql_connection, sql_command, &result) == -1 );
     664
     665   /* Problem: if the query was aborted, the result structure does not
     666      refer to any existing connection anymore. So we check rtc here.
     667   */
     668
     669   if( condat->rtc == 0) {
     670     retrieved_objects = write_results(result, filtered, fast, condat,
     671                                       acc_credit, acl);
     672     SQ_free_result(result);
     673   }
     674 } /* write_objects() */
     675
     676 /* insert_radix_serials() */
     677 /*++++++++++++++++++++++++++++++++++++++
     678   Insert the radix serial numbers into a temporary table in the database.
     679
     680   mask_t bitmap The bitmap of attribute to be converted.
     681
     682   SQ_connection_t *sql_connection The connection to the database.
     683
     684   char *id_table The id of the temporary table (This is a result of the hacky
     685                   way we've tried to get MySQL to do sub-selects.)
     686
     687   GList *datlist The list of data from the radix tree.
     688
     689   XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty.  :-(
     690
     691   More:
     692   +html+ <PRE>
     693   Authors:
     694         ottrey
     695   +html+ </PRE><DL COMPACT>
     696   +html+ <DT>Online References:
     697   +html+ <DD><UL>
     698              <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A>
     699   +html+ </UL></DL>
     700
     701   ++++++++++++++++++++++++++++++++++++++*/
     702 static void insert_radix_serials(sk_conn_st *condat,
         /* [<][>][^][v][top][bottom][index][help] */
     703                                  SQ_connection_t *sql_connection,
     704                                  char *id_table, GList *datlist) {
     705   GList    *qitem;
     706   char sql_command[STR_XL];
     707   int serial;
     708
     709   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
     710     rx_datcpy_t *datcpy = qitem->data;
     711
     712     serial = datcpy->leafcpy.data_key;
     713
     714     sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
     715     dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1);
     716
     717     wr_free(datcpy->leafcpy.data_ptr);
     718
     719     if(condat->rtc != 0) {
     720       break;
     721     }
     722   }
     723
     724   wr_clear_list( &datlist );
     725
     726 } /* insert_radix_serials() */
     727
     728
     729 /* write_radix_immediate() */
     730 /*++++++++++++++++++++++++++++++++++++++
     731   Display the immediate data carried with the objects returned by the
     732   radix tree.
     733
     734   GList *datlist      The linked list of dataleaf copies
     735   sk_conn_st *condat  Connection data for the client
     736   acc_st  *acc_credit Accounting struct
     737
     738 More:
     739   +html+ <PRE>
     740   Authors:
     741         marek
     742   +html+ </PRE><DL COMPACT>
     743   +html+ <DT>Online References:
     744   +html+ <DD><UL>
     745   +html+ </UL></DL>
     746
     747
     748   Also free the list of answers.
     749 */
     750 static void write_radix_immediate(GList *datlist,
         /* [<][>][^][v][top][bottom][index][help] */
     751                                   sk_conn_st *condat,
     752                                   acc_st    *acc_credit,
     753                                   acl_st    *acl)
     754 {
     755   GList    *qitem;
     756
     757   for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
     758     rx_datcpy_t *datcpy = qitem->data;
     759
     760     SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
     761     SK_cd_puts(condat, "\n");
     762
     763     wr_free(datcpy->leafcpy.data_ptr);
     764
     765     AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
     766
     767     if(condat->rtc != 0) {
     768       break;
     769     }
     770   }
     771
     772   wr_clear_list( &datlist );
     773 } /* write_radix_immediate() */
     774
     775
     776 /* map_qc2rx() */
     777 /*++++++++++++++++++++++++++++++++++++++
     778   The mapping between a query_command and a radix query.
     779
     780   Query_instruction *qi The Query Instruction to be created from the mapping
     781                         of the query command.
     782
     783   const Query_command *qc The query command to be mapped.
     784
     785   More:
     786   +html+ <PRE>
     787   Authors:
     788         ottrey
     789   +html+ </PRE><DL COMPACT>
     790   +html+ <DT>Online References:
     791   +html+ <DD><UL>
     792   +html+ </UL></DL>
     793
     794   ++++++++++++++++++++++++++++++++++++++*/
     795 static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
         /* [<][>][^][v][top][bottom][index][help] */
     796   int result=1;
     797
     798   qi->rx_keys = qc->keys;
     799
     800   if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
     801     qi->rx_srch_mode = RX_SRCH_EXLESS;
     802       qi->rx_par_a = 0;
     803   }
     804   else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
     805     qi->rx_srch_mode = RX_SRCH_LESS;
     806     qi->rx_par_a = RX_ALL_DEPTHS;
     807   }
     808   else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) {
     809     qi->rx_srch_mode = RX_SRCH_MORE;
     810       qi->rx_par_a = RX_ALL_DEPTHS;
     811   }
     812   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) {
     813     qi->rx_srch_mode = RX_SRCH_LESS;
     814     qi->rx_par_a = 1;
     815   }
     816   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) {
     817     qi->rx_srch_mode = RX_SRCH_MORE;
     818     qi->rx_par_a = 1;
     819   }
     820   else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) {
     821     qi->rx_srch_mode = RX_SRCH_EXACT;
     822     qi->rx_par_a = 0;
     823   }
     824   else {
     825       /* user error  ( XXX : this should have been checked before) */
     826
     827       ER_dbg_va(FAC_QI, ASP_QI_SKIP,
     828                 "ERROR in qc2rx mapping: bad combination of flags");
     829       result = 0;
     830   }
     831
     832   return result;
     833
     834 } /* map_qc2rx() */
     835
     836
     837 /* run_referral() */
     838 /*
     839    invoked when no such domain found. Goes through the domain table
     840    and searches for shorter domains, then if it finds one with referral
     841    it performs it, otherwise it just returns nothing.
     842
     843    to perform referral, it actually composes the referral query
     844    for a given host/port/type and calls the whois query function.
     845
     846    Well, it returns nothing anyway (void). It just prints to the socket.
     847
     848 */
     849 void run_referral(char *sourcename,
         /* [<][>][^][v][top][bottom][index][help] */
     850                   SQ_connection_t *sql_connection,
     851                   Query_instructions *qis,
     852                   Query_environ *qe,
     853                   int qi_index) {
     854   char *dot = qis->qc->keys;
     855   char querystr[STR_L];
     856   SQ_row_t *row;
     857   SQ_result_set_t *result;
     858   char sql_command[STR_XL];
     859   int stop_loop=0;
     860   char *ref_host;
     861   char *ref_type;
     862   char *ref_port;
     863   int  ref_port_int;
     864
     865   strcpy(querystr,"");
     866
     867   while( !stop_loop && (dot=index(dot,'.')) != NULL ) {
     868     dot++;
     869
     870     ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
     871
     872     sprintf(sql_command, "SELECT domain.object_id, domain, type, port, host FROM domain, refer WHERE domain.object_id = refer.object_id AND domain = '%s'", dot);
     873     dieif( SQ_execute_query(sql_connection, sql_command, &result) == -1);
     874
     875     switch( SQ_num_rows(result) ) {
     876       case 0: /* no such domain -> no action, will try next chunk */
     877       break;
     878
     879       case 1: /* check for referral host and perform query if present
     880                in any case end the loop */
     881       stop_loop=1;
     882       assert( (row = SQ_row_next(result)) != NULL);
     883
     884       ref_host = SQ_get_column_string(result, row, 4);
     885
     886       ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host);
     887
     888       if( ref_host != NULL && strlen(ref_host) > 0 ) {
     889         ref_type = SQ_get_column_string(result, row, 2);
     890         ref_port = SQ_get_column_string(result, row, 3);
     891
     892         /* get the integer value, it should be correct */
     893         if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
     894           die;
     895         }
     896
     897         /* compose the query: */
     898
     899         /* put -r if the reftype is RIPE and -r or -i were used */
     900         if( strcmp(ref_type,"RIPE") == 0
     901             && (   Query[qis->instruction[qi_index]->queryindex]
     902                    .querytype == Q_INVERSE
     903                    || qis->recursive > 0  )   ) {
     904           strcat(querystr," -r ");
     905         }
     906
     907         /* prepend with -Vversion,IP for type CLIENTADDRESS */
     908         if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
     909           char optv[STR_M];
     910
     911           snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip);
     912           strcat(querystr,optv);
     913         }
     914
     915         /* now set the search term - set to the stripped down version
     916            for inverse query, full-length otherwise */
     917         if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) {
     918           strcat(querystr,dot);
     919         }
     920         else {
     921           strcat(querystr,qis->qc->keys);
     922         }
     923
     924         SK_cd_printf(&(qe->condat),
     925 "% The object shown below is NOT in the %s database.\n"
     926 "% It has been obtained by querying a remote server:\n"
     927 "% (whois.denic.de) at port 43.\n"
     928 "% To see the object stored in the %s database\n"
     929 "% use the -R flag in your query.n"
     930 "%\n"
     931 "%%% Start of referred query result\n",
     932                    sourcename, sourcename );
     933
     934         SK_cd_puts(&(qe->condat), ref_host);
     935         SK_cd_puts(&(qe->condat), "\n\n");
     936
     937         /* WH_sock(sock, host, port, query, maxlines, timeout)) */
     938         switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr,  25, 5) ) {
     939         case WH_TIMEOUT:
     940             SK_cd_puts(&(qe->condat),"referral timeout\n");
     941             break;
     942
     943         case WH_MAXLINES:
     944             SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n");
     945             break;
     946
     947         case WH_BADHOST:
     948             SK_cd_puts(&(qe->condat),"referral host not found\n");
     949             break;
     950
     951         case WH_CONNECT:
     952             SK_cd_puts(&(qe->condat),"referral host not responding\n");
     953             break;
     954
     955         case WH_BIND:
     956         case WH_SOCKET:
     957             /* XXX internal server problem... what to do - wait ? */
     958         default:
     959           ;
     960         } /*switch WH_sock */
     961       }
     962       break;
     963
     964       default: /* more than one domain in this file: something broken */
     965       die;
     966     }
     967     SQ_free_result(result);
     968   }
     969 } /*run_referral*/
     970
     971 static
     972 void
     973 add_ref_name(SQ_connection_t *sql_connection,
         /* [<][>][^][v][top][bottom][index][help] */
     974              char *rectable,
     975              char *allnames
     976              )
     977 {
     978   /* construct the query, allow zero-length list */
     979   if( strlen(allnames) > 0 ) {
     980     char final_query[STR_XL];
     981     char select_query[STR_XL];
     982
     983     create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
     984                       "AND N00.object_type != 100 AND N00.thread_id = 0",
     985                       allnames);
     986
     987     sprintf(final_query, "INSERT INTO %s %s",
     988             rectable,
     989             select_query);
     990
     991     dieif(SQ_execute_query(sql_connection, final_query, NULL) == -1 );
     992
     993     allnames[0]=0;
     994   }
     995 }
     996
     997 static
     998 void
     999 qi_collect_ids(ca_dbSource_t *dbhdl,
         /* [<][>][^][v][top][bottom][index][help] */
    1000                char *sourcename,
    1001                SQ_connection_t **sql_connection,
    1002                Query_instructions *qis,
    1003                Query_environ *qe,
    1004                char *id_table,
    1005                GList **datlist,
    1006                acc_st *acc_credit,
    1007                acl_st *acl
    1008                )
    1009 {
    1010   Query_instruction **ins=NULL;
    1011   int i;
    1012   int   count, errors=0;
    1013   char sql_command[STR_XL];
    1014   er_ret_t err;
    1015   char sub_table[32];
    1016   int limit ;
    1017              /* a limit on the max number of objects to be returned
    1018                 from a single search. For some queries the object types
    1019                 are not known at this stage, so the limit must be
    1020                 the higher number of the two: private / public,
    1021                 or unlimited if any of them is 'unlimited'.
    1022              */
    1023   char limit_str[32];
    1024
    1025   if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
    1026     strcpy(limit_str,"");
    1027   } else {
    1028     sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
    1029                                                 so that the client hits
    1030                                                 the limit */
    1031   }
    1032
    1033   sprintf(sub_table, "%s_S ", id_table);
    1034
    1035   /* see if there was a leftover table from a crashed session
    1036    * (assume the ID cannot be currently in use)
    1037    */
    1038   sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
    1039   dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
    1040
    1041   /* create a table for special subqueries (domain only for now) */
    1042   sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", sub_table);
    1043   dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
    1044
    1045   /* Iterate through query instructions */
    1046   ins = qis->instruction;
    1047   for (i=0; ins[i] != NULL && errors == 0; i++) {
    1048     Query_instruction *qi = ins[i];
    1049
    1050     /* check if the client is still there */
    1051     if( qe->condat.rtc ) {
    1052       break;
    1053     }
    1054
    1055     switch ( qi->search_type ) {
    1056     case R_SQL:
    1057       if ( qi->query_str != NULL ) {
    1058
    1059         /* handle special cases first */
    1060         if( Query[qi->queryindex].class == C_DN ) {
    1061
    1062           /* XXX if any more cases than just domain appear, we will be
    1063              cleaning the _S table from the previous query here
    1064
    1065              "DELETE FROM %s_S"
    1066           */
    1067
    1068           /* now query into the _S table */
    1069           sprintf(sql_command, "INSERT INTO %s%s", sub_table, qi->query_str);
    1070           dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1);
    1071
    1072           /* if any results - copy to the id's table.
    1073              Otherwise, run referral */
    1074           count = SQ_get_affected_rows(*sql_connection);
    1075
    1076           ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
    1077                     "DN lookup for %s found %d entries", qis->qc->keys, count);
    1078
    1079           if( count ) {
    1080             sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s",
    1081                     id_table, sub_table);
    1082             dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1);
    1083           }
    1084
    1085           if( count == 0
    1086               || Query[qi->queryindex].querytype == Q_INVERSE ) {
    1087             /* now: if the domain was not found, we run referral.
    1088                unless prohibited by a flag
    1089
    1090                But for inverse queries we return the things that were
    1091                or were not found AND also do the referral unless prohibited.
    1092             */
    1093             if (qis->qc->R == 0) {
    1094               run_referral(sourcename, *sql_connection, qis, qe, i);
    1095             }
    1096           }
    1097
    1098         } /* if class DN */
    1099         else {
    1100           /* any other class of query */
    1101
    1102           sprintf(sql_command, "INSERT INTO %s %s %s",
    1103                   id_table, qi->query_str, limit_str);
    1104
    1105           if(sql_execute_watched( &(qe->condat), sql_connection,
    1106                                   sql_command, NULL) == -1 ) {
    1107
    1108             ER_perror(FAC_QI, QI_SQLERR," query='%s' [%d] %s",
    1109                       sql_command,
    1110                       SQ_errno(*sql_connection), SQ_error(*sql_connection));
    1111             errors++;
    1112           }
    1113           count = SQ_get_affected_rows(*sql_connection);
    1114         } /* not DN */
    1115       } /* if SQL query not NULL */
    1116
    1117       ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
    1118                 "%d entries added in %s query for %s",
    1119                 count, Query[qi->queryindex].descr, qis->qc->keys
    1120                 );
    1121       break;
    1122
    1123     case R_RADIX:
    1124
    1125       if( ! qis->qc->S ) /* XXX patch: use new search algorithm by default */ {
    1126         err = RP_new_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0,
    1127                             qi->rx_keys, dbhdl,
    1128                             Query[qi->queryindex].attribute,
    1129                             datlist, limit);
    1130
    1131       }
    1132       else {
    1133         err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0,
    1134                             qi->rx_keys, dbhdl,
    1135                             Query[qi->queryindex].attribute,
    1136                             datlist, limit);
    1137       }
    1138
    1139       if( NOERR(err)) {
    1140         if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
    1141           /* prevent unnecessary g_list_length call */
    1142
    1143           ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
    1144                     "%d entries after %s (mode %d par %d reg %d) query for %s",
    1145                     g_list_length(*datlist),
    1146                     Query[qi->queryindex].descr,
    1147                     qi->rx_srch_mode, qi->rx_par_a,
    1148                     dbhdl,
    1149                     qi->rx_keys);
    1150         }
    1151       }
    1152       else {
    1153         ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
    1154                   "RP_asc_search returned %x ", err);
    1155       }
    1156       break;
    1157
    1158     default: die;
    1159     } /* switch */
    1160
    1161   } /* for <every instruction> */
    1162
    1163   /* Now drop the _S table */
    1164   sprintf(sql_command, "DROP TABLE %s", sub_table);
    1165   dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
    1166
    1167 }
    1168
    1169 static
    1170 void
    1171 qi_fetch_references(SQ_connection_t **sql_connection,
         /* [<][>][^][v][top][bottom][index][help] */
    1172                     Query_environ *qe,
    1173                     char *id_table,
    1174                     acc_st *acc_credit,
    1175                     acl_st *acl
    1176                     )
    1177 {
    1178 char rec_table[32];
    1179     SQ_result_set_t *result;
    1180     SQ_row_t *row;
    1181     int thisid = 0;
    1182     int oldid = 0;
    1183     char allnames[STR_M];
    1184     char sql_command[STR_XL];
    1185
    1186     sprintf(rec_table, "%s_R", id_table);
    1187
    1188     /* see if there was a leftover table from a crashed session
    1189      * (assume the ID cannot be currently in use)
    1190      */
    1191     sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
    1192     dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
    1193
    1194     /* a temporary table for recursive data must be created, because
    1195        a query using the same table as a source and target is illegal
    1196        ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
    1197     */
    1198     sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", rec_table);
    1199     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
    1200
    1201     /* find the contacts */
    1202     sprintf(sql_command, Q_REC, rec_table, id_table, "author");
    1203     dieif(sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) == -1 );
    1204
    1205     sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
    1206     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
    1207
    1208     sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c" );
    1209     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
    1210
    1211     sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c" );
    1212     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
    1213
    1214
    1215     /* replace references to dummies by references by name */
    1216     sprintf(sql_command,
    1217             " SELECT id, name    FROM %s IDS STRAIGHT_JOIN names "
    1218             " WHERE IDS.id = names.object_id "
    1219             "      AND names.object_type = 100"
    1220             " ORDER BY id",
    1221             rec_table);
    1222
    1223     dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command,
    1224                               &result) == -1 );
    1225     /* well, it might not be -1, but if the watchdog worked then the
    1226        result is NULL */
    1227     if( result != NULL ) {
    1228
    1229       allnames[0]=0;
    1230       /* now go through the results and collect names */
    1231       while ( (qe->condat.rtc == 0)
    1232               && (row = SQ_row_next(result)) != NULL ) {
    1233         char *id   = SQ_get_column_string(result, row, 0);
    1234         char *name = SQ_get_column_string(result, row, 1);
    1235
    1236         thisid = atoi(id);
    1237
    1238         /* when the id changes, the name is complete */
    1239         if( thisid != oldid && oldid != 0 ) {
    1240           add_ref_name( *sql_connection, rec_table, allnames);
    1241         }
    1242
    1243         strcat(allnames, name);
    1244         strcat(allnames, " ");
    1245         oldid = thisid;
    1246         wr_free(id);
    1247         wr_free(name);
    1248       }
    1249       /* also do the last name */
    1250       add_ref_name( *sql_connection, rec_table, allnames);
    1251
    1252       SQ_free_result(result); /* we can do it only because the watchdog */
    1253       /* has not started between the check for non-NULL result and here */
    1254     }
    1255
    1256     /* now copy things back to the main temporary table   */
    1257     sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s",
    1258             id_table, rec_table);
    1259     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
    1260
    1261     /* Now drop the IDS recursive table */
    1262     sprintf(sql_command, "DROP TABLE %s", rec_table);
    1263     dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
    1264 }
    1265
    1266
    1267 /* QI_execute() */
    1268 /*++++++++++++++++++++++++++++++++++++++
    1269   Execute the query instructions.  This is called for each source.
    1270
    1271   void *database_voidptr Pointer to the database name
    1272
    1273   void *qis_voidptr Pointer to the query_instructions.
    1274
    1275   More:
    1276   +html+ <PRE>
    1277   Authors:
    1278         ottrey
    1279   +html+ </PRE>
    1280   ++++++++++++++++++++++++++++++++++++++*/
    1281 er_ret_t QI_execute(ca_dbSource_t *dbhdl,
         /* [<][>][^][v][top][bottom][index][help] */
    1282                     Query_instructions *qis,
    1283                     Query_environ *qe,
    1284                     acc_st *acc_credit,
    1285                     acl_st *acl
    1286                     )
    1287 {
    1288   /* those things must be freed after use! */
    1289   char *dbhost = ca_get_srcdbmachine(dbhdl);
    1290   char *dbname = ca_get_srcdbname(dbhdl);
    1291   char *dbuser = ca_get_srcdbuser(dbhdl);
    1292   char *dbpass = ca_get_srcdbpassword(dbhdl);
    1293   char *srcnam = ca_get_srcname(dbhdl);
    1294   Query_instruction **ins=NULL;
    1295   char id_table[STR_S];
    1296   char sql_command[STR_XL];
    1297   GList *datlist=NULL;
    1298   int i;
    1299   SQ_connection_t *sql_connection=NULL;
    1300   int   count, errors=0;
    1301   er_ret_t err;
    1302
    1303   sql_connection = SQ_get_connection( dbhost, ca_get_srcdbport(dbhdl),
    1304                                       dbname, dbuser, dbpass );
    1305   if (sql_connection == NULL) {
    1306     ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s",
    1307               dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
    1308     return QI_CANTDB;
    1309   }
    1310
    1311   sprintf(id_table, "ID_%ld_%d",   mysql_thread_id(sql_connection),
    1312           pthread_self());
    1313
    1314   /* see if there was a leftover table from a crashed session
    1315    * (assume the ID cannot be currently in use)
    1316    */
    1317   sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
    1318   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1319
    1320   /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
    1321   sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
    1322   dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1323
    1324   qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, id_table,
    1325                  &datlist, acc_credit, acl);
    1326
    1327   /* post-processing */
    1328   if( qis->filtered == 0 ) {
    1329     /* start the watchdog just to set the rtc flag */
    1330     SK_watchclear(&(qe->condat));
    1331     SK_watchstart(&(qe->condat));
    1332
    1333     /* add radix results (only if -K is not active) */
    1334     insert_radix_serials(&(qe->condat), sql_connection, id_table, datlist);
    1335
    1336     SK_watchstop(&(qe->condat));
    1337   }
    1338
    1339   /* fetch recursive objects (ac,tc,zc,ah) */
    1340   if ( qis->recursive ) {
    1341     qi_fetch_references( &sql_connection, qe, id_table, acc_credit, acl);
    1342   } /* if recursive */
    1343
    1344   /* display */
    1345   /* -K filtering:
    1346    * right now only filtering, no expanding sets like write_set_objects()
    1347    */
    1348
    1349   /* display the immediate data from the radix tree */
    1350   if( qis->filtered == 1 ) {
    1351     write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
    1352   }
    1353
    1354   /* display objects from the IDs table */
    1355   write_objects( &sql_connection, id_table, qis->filtered,
    1356                 qis->fast, &(qe->condat), acc_credit, acl);
    1357
    1358   /* Now drop the IDS table */
    1359   sprintf(sql_command, "DROP TABLE %s", id_table);
    1360   dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
    1361   SQ_close_connection(sql_connection);
    1362
    1363   /* free allocated parameters */
    1364   wr_free(dbhost);
    1365   wr_free(dbname);
    1366   wr_free(dbuser);
    1367   wr_free(dbpass);
    1368   wr_free(srcnam);
    1369
    1370   return QI_OK;
    1371 } /* QI_execute() */
    1372
    1373
    1374 /* instruction_free() */
    1375 /*++++++++++++++++++++++++++++++++++++++
    1376   Free the instruction.
    1377
    1378   Query_instruction *qi query_instruction to be freed.
    1379
    1380   More:
    1381   +html+ <PRE>
    1382   Authors:
    1383         ottrey
    1384   +html+ </PRE>
    1385   ++++++++++++++++++++++++++++++++++++++*/
    1386 static void instruction_free(Query_instruction *qi) {
         /* [<][>][^][v][top][bottom][index][help] */
    1387   if (qi != NULL) {
    1388     if (qi->query_str != NULL) {
    1389       wr_free(qi->query_str);
    1390     }
    1391     wr_free(qi);
    1392   }
    1393 } /* instruction_free() */
    1394
    1395 /* QI_free() */
    1396 /*++++++++++++++++++++++++++++++++++++++
    1397   Free the query_instructions.
    1398
    1399   Query_instructions *qis Query_instructions to be freed.
    1400
    1401   XXX This isn't working too well at the moment.
    1402
    1403   More:
    1404   +html+ <PRE>
    1405   Authors:
    1406         ottrey
    1407   +html+ </PRE>
    1408   ++++++++++++++++++++++++++++++++++++++*/
    1409 void QI_free(Query_instructions *qis) {
         /* [<][>][^][v][top][bottom][index][help] */
    1410   int i;
    1411
    1412   for (i=0; qis->instruction[i] != NULL; i++) {
    1413     instruction_free(qis->instruction[i]);
    1414   }
    1415
    1416   if (qis != NULL) {
    1417     wr_free(qis);
    1418   }
    1419
    1420 } /* QI_free() */
    1421
    1422 /*++++++++++++++++++++++++++++++++++++++
    1423   Determine if this query should be conducted or not.
    1424
    1425   If it was an inverse query - it the attribute appears in the query command's bitmap.
    1426   If it was a lookup query - if the attribute appears in the object type bitmap or
    1427                              disregard if there is no object_type bitmap (Ie object filter).
    1428
    1429   mask_t bitmap The bitmap of attribute to be converted.
    1430
    1431   const Query_command *qc The query_command that the instructions are created
    1432                           from.
    1433
    1434   const Query_t q The query being investigated.
    1435
    1436   ++++++++++++++++++++++++++++++++++++++*/
    1437 static int valid_query(const Query_command *qc, const Query_t q) {
         /* [<][>][^][v][top][bottom][index][help] */
    1438   int result=0;
    1439
    1440   if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
    1441     if (q.query != NULL) {
    1442       switch (q.querytype) {
    1443         case Q_INVERSE:
    1444           if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
    1445             result = 1;
    1446           }
    1447         break;
    1448
    1449         case Q_LOOKUP:
    1450           if (MA_bitcount(qc->object_type_bitmap) == 0) {
    1451             result=1;
    1452           }
    1453           else if (q.class<0 || MA_isset(qc->object_type_bitmap, q.class)) {
    1454             result=1;
    1455           }
    1456         break;
    1457
    1458         default:
    1459           fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
    1460       }
    1461     }
    1462   }
    1463
    1464   return result;
    1465 } /* valid_query() */
    1466
    1467 /* QI_new() */
    1468 /*++++++++++++++++++++++++++++++++++++++
    1469   Create a new set of query_instructions.
    1470
    1471   const Query_command *qc The query_command that the instructions are created
    1472                           from.
    1473
    1474   const Query_environ *qe The environmental variables that they query is being
    1475                           performed under.
    1476   More:
    1477   +html+ <PRE>
    1478   Authors:
    1479         ottrey
    1480   +html+ </PRE>
    1481   ++++++++++++++++++++++++++++++++++++++*/
    1482 Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
         /* [<][>][^][v][top][bottom][index][help] */
    1483   Query_instructions *qis=NULL;
    1484   Query_instruction *qi=NULL;
    1485   int i_no=0;
    1486   int i;
    1487   char *query_str;
    1488
    1489   dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
    1490
    1491   qis->filtered = qc->filtered;
    1492   qis->fast = qc->fast;
    1493   qis->recursive = qc->recursive;
    1494   qis->qc = (qc);
    1495
    1496
    1497   for (i=0; Query[i].query != NULL; i++) {
    1498
    1499     /* If a valid query. */
    1500     if ( valid_query(qc, Query[i]) == 1) {
    1501
    1502       dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
    1503
    1504       qi->queryindex = i;
    1505
    1506       /* SQL Query */
    1507       if ( Query[i].refer == R_SQL) {
    1508         qi->search_type = R_SQL;
    1509         query_str = create_query(Query[i], qc);
    1510
    1511         if (query_str!= NULL) {
    1512           qi->query_str = query_str;
    1513           qis->instruction[i_no++] = qi;
    1514         }
    1515       }
    1516       /* Radix Query */
    1517       else if (Query[i].refer == R_RADIX) {
    1518         qi->search_type = R_RADIX;
    1519
    1520         if (map_qc2rx(qi, qc) == 1) {
    1521           int j;
    1522           int found=0;
    1523
    1524           /* check that there is no such query yet, for example if
    1525              more than one keytype (wk) matched */
    1526           for (j=0; j<i_no; j++) {
    1527             Query_instruction *qij = qis->instruction[j];
    1528
    1529             if(    qij->search_type == R_RADIX
    1530                    && Query[qij->queryindex].attribute
    1531                    == Query[qi ->queryindex].attribute) {
    1532
    1533               found=1;
    1534               break;
    1535             }
    1536           }
    1537
    1538           if ( found ) {
    1539             /* Discard the Query Instruction */
    1540             wr_free(qi);
    1541           }
    1542           else {
    1543             /* Add the query_instruction to the array */
    1544             qis->instruction[i_no++] = qi;
    1545           }
    1546         }
    1547       }
    1548       else {
    1549           /* ERROR: bad search_type */
    1550           die;
    1551       }
    1552     }
    1553   }
    1554   qis->instruction[i_no++] = NULL;
    1555
    1556
    1557   {  /* tracing */
    1558       char *descrstr = QI_queries_to_string(qis);
    1559
    1560       ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
    1561       wr_free( descrstr );
    1562   }
    1563
    1564   return qis;
    1565
    1566 } /* QI_new() */
    1567
    1568 /* QI_queries_to_string()
    1569
    1570    returns a list of descriptions for queries that will be performed.
    1571 */
    1572
    1573 char *QI_queries_to_string(Query_instructions *qis)
         /* [<][>][^][v][top][bottom][index][help] */
    1574 {
    1575    Query_instruction *qi;
    1576    int i;
    1577    char *resstr = NULL;
    1578
    1579    dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK);
    1580    strcpy(resstr, "{");
    1581
    1582    for( i = 0; ( qi=qis->instruction[i] ) != NULL;  i++ ) {
    1583        char *descr = Query[qi->queryindex].descr;
    1584        int oldres = strlen( resstr );
    1585
    1586        dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK);
    1587        strcat(resstr, descr);
    1588        strcat(resstr, ",");
    1589    }
    1590    if( i>0 ) {
    1591        /* cancel the last comma */
    1592        resstr[strlen(resstr)-1] = 0;
    1593    }
    1594
    1595    dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 )
    1596           != UT_OK);
    1597    strcat(resstr, "}");
    1598
    1599    return resstr;
    1600 }

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