## modules/qi/query_instructions.c

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

## FUNCTIONS

This source file includes following functions.

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

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

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