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

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