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