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