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