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)
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,
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) {
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,
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) {
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) {
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)
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) {
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,
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,
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,
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,
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) {
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,
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,
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,
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,
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,
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) {
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) {
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) {
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) {
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)
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 | }