1 | /***************************************
2 | $Revision: 1.96 $
3 |
4 | Query instructions (qi). This is where the queries are executed.
5 |
6 | Status: NOT REVUED, TESTED
7 |
8 | ******************/ /******************
9 | Filename : query_instructions.c
10 | Authors : ottrey@ripe.net - framework and draft implementation
11 | marek@ripe.net - cleaned and extended, added referral,
12 | accounting support and watchdog cancellation.
13 | OSs Tested : Solaris
14 | ******************/ /******************
15 | Copyright (c) 1999,2000,2001,2002 RIPE NCC
16 |
17 | All Rights Reserved
18 |
19 | Permission to use, copy, modify, and distribute this software and its
20 | documentation for any purpose and without fee is hereby granted,
21 | provided that the above copyright notice appear in all copies and that
22 | both that copyright notice and this permission notice appear in
23 | supporting documentation, and that the name of the author not be
24 | used in advertising or publicity pertaining to distribution of the
25 | software without specific, written prior permission.
26 |
27 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
28 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
29 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
30 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
31 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 | ***************************************/
34 | #include "rip.h"
35 |
36 | #include <stdio.h>
37 | #include <string.h>
38 | #include <glib.h>
39 |
40 | /* maximum number of names to query for */
41 | /* MySQL only allows 31 tables in a single query, so this limits us */
42 | /* As of 2002-03-04 the most names in a person object are 14 */
43 | #define MAX_NAMES 30
44 |
45 | /* the TEMPORARY table is only available in 3.23 or later MySQL */
46 | #if MYSQL_VERSION_ID >= 32300
47 | #define USE_TEMPORARY_TABLE
48 | #endif
49 |
50 | /* string to stick in CREATE TABLE statments */
51 | #ifdef USE_TEMPORARY_TABLE
52 | #define TEMPORARY "TEMPORARY"
53 | #else
54 | #define TEMPORARY ""
55 | #endif
56 |
57 | /*++++++++++++++++++++++++++++++++++++++
58 | Function invoked on query cancellation by the watchdog,
59 | used from the sql_execute_watched() function.
60 |
61 | It aborts the running query (the abort function in sq kills and
62 | reestablished the connection).
63 |
64 | void *qi_kill_body result of sq_execute_query, int cast to (void*)
65 |
66 | void *arg pointer to sql connection
67 |
68 | Author:
69 | marek.
70 |
71 | ++++++++++++++++++++++++++++++++++++++*/
72 | static
73 | void *qi_kill_body(void *arg)
74 | {
75 | SQ_connection_t *sql_connection = arg;
76 | ER_dbg_va(FAC_QI, ASP_QI_WATCH,
77 | "rtc: killing SQL connection %d", (sql_connection)->thread_id);
78 | /* abort the running query */
79 | SQ_abort_query(sql_connection);
80 |
81 | return NULL;
82 | }
83 |
84 |
85 |
86 | /*++++++++++++++++++++++++++++++++++++++
87 | wrapper around sq_execute_query: starts a query
88 | in a separate thread and starts the socket watchdog to cancel the query
89 | if the socket is closed. If the socket has problems already (its
90 | reason-to-close flag is set) no query is attempted.
91 |
92 | The execution of the query or watchdog is not guaranteed at all!
93 |
94 | int sql_execute_watched Returns the return code of SQ_execute_query,
95 | Returns 0 for cancelled queries.
96 |
97 | sk_conn_st *condat connection to watch
98 |
99 | SQ_connection_t **sql_connection sql connection
100 |
101 | const char *query sql query to execute
102 |
103 | SQ_result_set_t **result_ptr storage for the query result structure
104 | (passed to SQ_execute_query). Must either
105 | be NULL, or the pointer it points to must
106 | be NULL - in that case the result struct.
107 | will be allocated in SQ.
108 |
109 | Author:
110 | marek.
111 | ++++++++++++++++++++++++++++++++++++++*/
112 | int sql_execute_watched(sk_conn_st *condat, SQ_connection_t **sql_connection,
113 | const char *query, SQ_result_set_t **result_ptr)
114 | {
115 | int retval = 0; /* return value of sq_execute_query */
116 | SQ_connection_t *tempcon;
117 |
118 | /* assert that, if defined, result_ptr is initialised to NULL
119 | prior to calling this function */
120 | if( result_ptr != NULL ) {
121 | dieif( *result_ptr != NULL );
122 | }
123 |
124 | /* don't even try to perform the query/fire up watchdog
125 | if rtc is already set. Do this only if not set yet. */
126 | if( condat->rtc == 0 ) {
127 |
128 | /* make clean */
129 | SK_watch_setclear(condat);
130 |
131 | /* set watchdog to execute the abort function */
132 | SK_watch_setexec(condat, qi_kill_body, *sql_connection);
133 |
134 | /* start the watchdog */
135 | SK_watchstart(condat);
136 |
137 | /* start query. An error may be returned if the query is aborted */
138 | retval = SQ_execute_query(*sql_connection, query, result_ptr);
139 |
140 | /* but short queries will complete before the watchdog kills the
141 | connection */
142 |
143 | SK_watchstop(condat);
144 |
145 |
146 | /* if the watchdog triggered, then it is guaranteed that
147 | the kill_body function was invoked and therefore the sql-connection
148 | is now unusable...
149 | Close and reopen it for cleanup, use temporary connection
150 | to keep the login details */
151 | if( condat->rtc != 0 ) {
152 | /* can't rely on the error code from mysql!
153 | */
154 |
155 | /* one thing: this code must be entered ONLY if the kill_body
156 | thing was invoked by the watchdog.
157 | */
158 |
159 | /* if result is defined, free it here before destroying the
160 | associated connection */
161 | if( retval == 0 && result_ptr && *result_ptr ) {
162 | SQ_free_result( *result_ptr );
163 | *result_ptr = NULL;
164 | }
165 |
166 | tempcon = SQ_duplicate_connection(*sql_connection);
167 |
168 | ER_dbg_va(FAC_QI, ASP_QI_WATCH,
169 | "rtc: closing SQL thread %d", (*sql_connection)->thread_id);
170 | SQ_close_connection(*sql_connection);
171 |
172 | *sql_connection = tempcon;
173 | ER_dbg_va(FAC_QI, ASP_QI_WATCH,
174 | "rtc: reopened as thread %d", (*sql_connection)->thread_id);
175 |
176 | /* make it look as if there was no error and
177 | the result is empty */
178 | retval = 0;
179 | } /* if watchdog set rtc */
180 |
181 | } /* if rtc not set before */
182 |
183 | return retval;
184 | }
185 |
186 | /* create_name_query() */
187 | /*++++++++++++++++++++++++++++++++++++++
188 | Create an sql query for the names table.
189 |
190 | char *query_str
191 |
192 | const char *sql_query
193 |
194 | const char *keys
195 |
196 | More:
197 | +html+ <PRE>
198 | Authors:
199 | ottrey
200 | +html+ </PRE>
201 | ++++++++++++++++++++++++++++++++++++++*/
202 | static void create_name_query(GString *query_str, const char *sql_query, const char *keys) {
203 | int i;
204 | /* Allocate stuff - use dynamic strings (initialised to some length) */
205 | GString *from_clause = g_string_sized_new(STR_L);
206 | GString *where_clause = g_string_sized_new(STR_L);
207 | gchar **words = g_strsplit(keys, " ", 0);
208 |
209 | /* double quotes " are used in queries to allow querying for
210 | names like O'Hara */
211 |
212 | if (words[0] != NULL) {
213 | g_string_sprintfa(from_clause, "names N%.2d", 0);
214 | g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
215 |
216 | for (i=1; (i < MAX_NAMES) && (words[i] != NULL); i++) {
217 | g_string_sprintfa(from_clause, ", names N%.2d", i);
218 | g_string_sprintfa(where_clause,
219 | " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id",
220 | i, words[i], i);
221 | }
222 | }
223 |
224 | g_string_sprintf(query_str, sql_query, from_clause->str, where_clause->str);
225 |
226 | /* Free up stuff */
227 | g_strfreev(words);
228 | g_string_free(where_clause,/* CONSTCOND */ TRUE);
229 | g_string_free(from_clause, /* CONSTCOND */ TRUE);
230 |
231 | } /* create_name_query() */
232 |
233 |
234 | /*++++++++++++++++++++++++++++++++++++++
235 | construct a range query for the as_block table
236 | (a query for an AS block object) given a string like:
237 | AS1
238 | AS1 - AS10
239 | AS1-AS10
240 |
241 | int create_asblock_query Returns 0 on success, -1 on failure
242 | (search term not an AS# nor range)
243 |
244 | char *query_str buffer for the final query (must be big enough)
245 |
246 | const char *sql_query rest of the sql query (with %d %d formats for
247 | AS numbers)
248 |
249 | const char *keys user-supplied search term.
250 |
251 | Author:
252 | marek
253 | ++++++++++++++++++++++++++++++++++++++*/
254 | static int create_asblock_query(GString *query_str,
255 | const char *sql_query,
256 | const char *keys) {
257 | char *keycopy = wr_string(keys);
258 | char *token, *cursor = keycopy;
259 | int asnums[2] = {0,0};
260 | int index = 0; /* index into the asnums array */
261 |
262 |
263 | while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {
264 | /* discard the letters (or leading whitespace), take the number */
265 | if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
266 | return -1; /* error */
267 | }
268 | }
269 | /* if only beginning was supplied, copy it as end */
270 | if( index == 1 ) {
271 | asnums[1] = asnums[0];
272 | }
273 |
274 | /* now construct the query */
275 | g_string_sprintf(query_str, sql_query, asnums[0], asnums[1]);
276 |
277 | UT_free(keycopy);
278 | return 0;
279 | }
280 |
281 |
282 | /*++++++++++++++++++++++++++++++++++++++
283 | add_filter(): construct a query to limit the objects returned from the last
284 | table to predefined types.
285 |
286 | char *query_str buffer for the final query, containing the initial
287 | part of the query (must be big enough)
288 |
289 | const Query_command *qc query command structure with the bitmap of
290 | object types to be included.
291 |
292 | Author:
293 | ottrey.
294 | ++++++++++++++++++++++++++++++++++++++*/
295 | static void add_filter(GString *query_str, const Query_command *qc)
296 | {
297 | unsigned i;
298 | /* int qlen;*/
299 | char filter_atom[STR_M];
300 |
301 | #if 0
302 | /* glib string manipulation - untested yet */
303 |
304 | if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) {
305 | g_string_sprintfa(query_str, " AND (");
306 | for (i=0; i < C_END; i++) {
307 | if (MA_isset(qc->object_type_bitmap, i)) {
308 | g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i));
309 | }
310 | }
311 | g_string_truncate(query_str, query_str->len-3);
312 | g_string_append_c(query_str, ')');
313 | }
314 |
315 | #else /* classic string operations */
316 |
317 | /* add filters only if any bits are 0 (the number of 1's is < MAX_MAX */
318 | if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) {
319 | g_string_append(query_str, " AND (");
320 | for (i=0; i < C_END; i++) {
321 | if (MA_isset(qc->object_type_bitmap, i)) {
322 | strcpy(filter_atom, "");
323 | sprintf(filter_atom, "i.object_type = %d OR ", i);
324 | /* XXX class codes should be used instead:
325 | DF_get_class_dbase_code(i))
326 | but currently the tables contain values of enums
327 | (C_IN, etc) and not codes
328 | */
329 | g_string_append(query_str, filter_atom);
330 | }
331 | }
332 | dieif(query_str->len < 3); /* this code can only be reached if there is
333 | at least one object here, meaning this
334 | must end with the string "OR ", which we
335 | then remove */
336 | g_string_truncate(query_str, query_str->len - 3);
337 | g_string_append_c(query_str, ')');
338 | /* qlen = strlen(query_str);
339 | query_str[qlen-3] = ')';
340 | query_str[qlen-2] = '\0';
341 | query_str[qlen-1] = '\0';*/
342 | }
343 |
344 | #endif
345 |
346 | } /* add_filter() */
347 |
348 | /* create_query() */
349 | /*++++++++++++++++++++++++++++++++++++++
350 | Create an sql query from the query_command and the matching keytype and the
351 | selected inverse attributes.
352 | Note this clears the first inv_attribute it sees, so is called sequentially
353 | until there are no inv_attributes left.
354 |
355 | WK_Type keytype The matching keytype.
356 |
357 | const Query_command *qc The query command.
358 |
359 | mask_t *inv_attrs_bitmap The selected inverse attributes.
360 |
361 | More:
362 | +html+ <PRE>
363 | Authors:
364 | ottrey
365 | +html+ </PRE>
366 |
367 | ++++++++++++++++++++++++++++++++++++++*/
368 | static char *create_query(const Query_t q, const Query_command *qc)
369 | {
370 | GString *result_buff;
371 | char *result;
372 | Q_Type_t querytype;
373 | int addquery = 0; /* controls if the query should be added to the list */
374 |
375 | result_buff = g_string_sized_new(STR_XL);
376 |
377 | if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
378 | querytype = Q_INVERSE;
379 | }
380 | else {
381 | querytype = Q_LOOKUP;
382 | }
383 |
384 | if ( (q.query != NULL)
385 | && (q.querytype == querytype) ) {
386 |
387 | /* addquery = 1; */
388 | /* if it got here, it should be added, unless.(see asblock)*/
389 |
390 | if (q.keytype == WK_NAME) {
391 | /* Name queries require special treatment. */
392 | create_name_query(result_buff, q.query, qc->keys);
393 | addquery = 1;
394 | }
395 | else if( q.keytype == WK_IPADDRESS ) { /* ifaddr sql lookups */
396 | ip_range_t myrang;
397 | unsigned begin, end;
398 | ip_keytype_t key_type;
399 |
400 | /* The only inverse query for IPADDRESS is nserver. */
401 | /* We need to insure that we don't try to use the numeric values for this
402 | * query, because the address of the server is stored as a string, and
403 | * the SQL query is formatted appropriately. */
404 | if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
405 | if(IP_rang_b2_space(&myrang) == IP_V4 ) {
406 | IP_rang_b2v4(&myrang, &begin, &end);
407 | if (querytype == Q_INVERSE) {
408 | /* for inverse queries, convert number to dotted-quad */
409 | char buf[64];
410 | const char *inet_ntop_ret;
411 | inet_ntop_ret = inet_ntop(AF_INET, &begin, buf, sizeof(buf));
412 | dieif(inet_ntop_ret == NULL);
413 | g_string_sprintf(result_buff, q.query, buf);
414 | } else {
415 | /* otherwise, execute appropriate query on numeric values */
416 | g_string_sprintf(result_buff, q.query, begin, end);
417 | }
418 | addquery = 1;
419 | }
420 | else {
421 | die;
422 | }
423 | }
424 | }
425 | else if( q.keytype == WK_ASRANGE ) { /* as_block range composition */
426 | if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
427 | addquery = 0; /* ... unless it's not correct */
428 | }
429 | else {
430 | addquery = 1;
431 | }
432 | }
433 | else {
434 | g_string_sprintf(result_buff, q.query, qc->keys);
435 | addquery = 1;
436 | }
437 |
438 | if (q.class == C_ANY && addquery == 1 ) {
439 | /* It is class type ANY so add the object filtering */
440 | add_filter(result_buff, qc);
441 | }
442 | }
443 |
444 | if( addquery == 1 ) {
445 | result = UT_strdup(result_buff->str);
446 | } else {
447 | result = NULL;
448 | }
449 | g_string_free(result_buff, TRUE);
450 |
451 | return result;
452 | } /* create_query() */
453 |
454 | /* QI_fast_output() */
455 | /*++++++++++++++++++++++++++++++++++++++
456 | This is for the '-F' flag.
457 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
458 | Fast isn't fast anymore - it's just there for compatibility reasons.
459 |
460 | const char *str The object to be "fast output'ed".
461 |
462 | More:
463 | +html+ <PRE>
464 | Authors:
465 | ottrey,
466 | marek - glib strings + small changes
467 | +html+ </PRE>
468 | ++++++++++++++++++++++++++++++++++++++*/
469 | char *QI_fast_output(const char *str)
470 | {
471 | int i,j;
472 | char *result;
473 | GString *result_buff = g_string_sized_new(STR_XL);
474 | gchar **lines = g_strsplit(str, "\n", 0);
475 | unsigned char *value, *colon;
476 | char *attr;
477 |
478 | g_string_assign(result_buff, "");
479 |
480 | for (j=0; lines[j] != NULL; j++) {
481 |
482 | switch (lines[j][0]) {
483 | /* line continuation */
484 | case ' ':
485 | case '\t':
486 | case '+':
487 | value = (unsigned char *) lines[j]+1;
488 | while(*value != '\0' && isspace(*value)) {
489 | value++;
490 | }
491 | g_string_append(result_buff, "\n+ ");
492 | g_string_append(result_buff, (char *)value);
493 | break;
494 |
495 | default:
496 | /* a line of the form "attribute: value" */
497 | /* first: close the last line (if there was any, i.e. j>0) */
498 | if( j > 0 ) {
499 | g_string_append_c(result_buff, '\n');
500 | }
501 |
502 | /* get attribute name */
503 | attr = lines[j];
504 | colon = (unsigned char *) strchr(lines[j], ':');
505 | /* if there's no colon for whatever reason, dump the object
506 | and report the condition */
507 | if( colon == NULL ) {
508 | ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
509 | goto fast_output_cleanup;
510 | }
511 | *colon = '\0';
512 | for(value = colon+1; *value != '\0' && isspace(*value) ; value++) {
513 | ;
514 | }
515 |
516 | if( (i = DF_attribute_name2type(attr)) == -1 ) {
517 | /* warning! error in the object format */
518 | ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
519 | goto fast_output_cleanup;
520 |
521 | }
522 | else {
523 | /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
524 | g_string_append_c(result_buff, '*');
525 | g_string_append(result_buff, DF_get_attribute_code(i));
526 | g_string_append(result_buff, ": ");
527 | g_string_append(result_buff, (char *)value);
528 | }
529 | } /* switch */
530 | } /* for every line */
531 |
532 | fast_output_cleanup:
533 |
534 | g_strfreev(lines);
535 |
536 | g_string_append_c(result_buff, '\n');
537 | result = UT_strdup(result_buff->str);
538 | dieif(result == NULL);
539 |
540 | g_string_free(result_buff,/* CONSTCOND */ TRUE);
541 |
542 | return result;
543 | } /* fast_output() */
544 |
545 | /* filter() */
546 | /*++++++++++++++++++++++++++++++++++++++
547 | Basically it's for the '-K' flag for non-set (and non-radix) objects.
548 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
549 |
550 | This could be speed up if there were breaks out of the loops, once it matched something.
551 |
552 | const char *string The string to be filtered.
553 |
554 | More:
555 | +html+ <PRE>
556 | Authors:
557 | ottrey
558 | +html+ </PRE>
559 |
560 | ++++++++++++++++++++++++++++++++++++++*/
561 | char *filter(const char *str) {
562 | int i,j, passed=0;
563 | char *result;
564 | GString *result_buff = g_string_sized_new(STR_XL);
565 | gchar **lines = g_strsplit(str, "\n", 0);
566 | char * const *filter_names;
567 | gboolean filtering_an_attribute = FALSE;
568 |
569 | filter_names = DF_get_filter_names();
570 |
571 | g_string_assign(result_buff, "");
572 |
573 | for (i=0; filter_names[i] != NULL; i++) {
574 | for (j=0; lines[j] != NULL; j++) {
575 | /* match lines that start with "key-field:" */
576 | int filter_name_len = strlen(filter_names[i]);
577 | if ((strncmp(filter_names[i], lines[j], filter_name_len) == 0) &&
578 | (lines[j][filter_name_len] == ':'))
579 | {
580 |
581 | g_string_sprintfa(result_buff, "%s\n", lines[j]);
582 | passed++;
583 |
584 | /* CONSTCOND */
585 | filtering_an_attribute = TRUE;
586 | }
587 | /* CONSTCOND */
588 | else if (filtering_an_attribute == TRUE) {
589 | switch (lines[j][0]) {
590 | case ' ':
591 | case '\t':
592 | case '+':
593 |
594 | g_string_sprintfa(result_buff, "%s\n", lines[j]);
595 |
596 | break;
597 |
598 | default:
599 | filtering_an_attribute = FALSE;
600 | }
601 | }
602 | }
603 | }
604 |
605 | g_strfreev(lines);
606 |
607 | if(passed) {
608 | g_string_append(result_buff, "\n");
609 | }
610 | result = UT_strdup(result_buff->str);
611 | g_string_free(result_buff,/* CONSTCOND */ TRUE);
612 |
613 | return result;
614 | } /* filter() */
615 |
616 | /* write_results() */
617 | /*++++++++++++++++++++++++++++++++++++++
618 | Write the results to the client socket.
619 |
620 | SQ_result_set_t *result The result set returned from the sql query.
621 | unsigned filtered if the objects should go through a filter (-K)
622 | sk_conn_st *condat Connection data for the client
623 |
624 | More:
625 | +html+ <PRE>
626 | Authors:
627 | ottrey - initial design
628 | marek - rewritten for accounting and cancellation.
629 | +html+ </PRE>
630 |
631 | ++++++++++++++++++++++++++++++++++++++*/
632 | static int write_results(SQ_result_set_t *result,
633 | unsigned filtered,
634 | unsigned fast,
635 | sk_conn_st *condat,
636 | acc_st *acc_credit,
637 | acl_st *acl
638 | ) {
639 | SQ_row_t *row;
640 | char *str;
641 | char *filtrate;
642 | char *fasted;
643 | int retrieved_objects=0;
644 | char *objt;
645 | int type;
646 |
647 | /* Get all the results - one at a time */
648 | if (result != NULL) {
649 | /* here we are making use of the mysql_store_result capability
650 | of interrupting the cycle of reading rows. mysql_use_result
651 | would not allow that, would have to be read until end */
652 |
653 | while ( condat->rtc == 0
654 | && AC_credit_isdenied( acc_credit ) == 0
655 | && (row = SQ_row_next(result)) != NULL ) {
656 |
657 | dieif ( (str = SQ_get_column_string(result, row, 0)) == NULL )
658 | dieif ( (objt = SQ_get_column_string(result, row, 3)) == NULL );
659 |
660 | /* get + add object type */
661 | type = atoi(objt);
662 |
663 | /* ASP_QI_LAST_DET */
664 | ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
665 | "Retrieved serial id = %d , type = %s", atoi(str), objt);
666 |
667 | UT_free(str);
668 | UT_free(objt);
669 |
670 | /* decrement credit for accounting purposes */
671 | AC_count_object( acc_credit, acl,
672 | type == C_PN || type == C_RO ); /* is private? */
673 |
674 | /* break the loop if the credit has just been exceeded and
675 | further results denied */
676 | if( AC_credit_isdenied( acc_credit ) ) {
677 | continue;
678 | }
679 |
680 | if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; }
681 | else {
682 |
683 | /* The fast output stage */
684 | if (fast == 1) {
685 | fasted = QI_fast_output(str);
686 | UT_free(str);
687 | str = fasted;
688 | }
689 |
690 | /* The filtering stage */
691 | if (filtered == 0) {
692 | SK_cd_puts(condat, str);
693 | SK_cd_puts(condat, "\n");
694 | }
695 | else {
696 |
697 | /* XXX accounting should be done AFTER filtering, not to count
698 | objects filtered out */
699 |
700 | filtrate = filter(str);
701 | SK_cd_puts(condat, filtrate);
702 | UT_free(filtrate);
703 | }
704 | retrieved_objects++;
705 | }
706 | UT_free(str);
707 | }
708 | }
709 |
710 | return retrieved_objects;
711 | } /* write_results() */
712 |
713 | /* generic SQL error message - it could be a configurable parameter, but
714 | it also shouldn't happen! */
715 | static const char *sql_error_text =
716 | "% An internal database error has occurred.\n"
717 | "% It has been logged, and an adminstrator should look at it shorly.\n"
718 | "% Please try your query again.\n"
719 | "\n"
720 | "\n";
721 |
722 | /* use a macro so we can get our file and line number */
723 | #define report_sql_error(condat,sql_connection,sql_command) \
724 | __report_sql_error((condat), (sql_connection), (sql_command), \
725 | __FILE__,__LINE__,pthread_self())
726 |
727 | /* report_sql_error() */
728 | /*++++++++++++++++++++++++++++++++++++++
729 |
730 | sk_conn_st *condat connection with user
731 | SQ_connection_t *sql_connection connection to MySQL
732 | const char *sql_command SQL command sent to MySQL
733 |
734 | ++++++++++++++++++++++++++++++++++++++*/
735 |
736 | void
737 | __report_sql_error(sk_conn_st *condat,
738 | SQ_connection_t *sql_connection,
739 | const char *sql_command,
740 | const char *file,
741 | int line,
742 | pthread_t tid)
743 | {
744 | /* first, let user know what has happened */
745 | SK_cd_puts(condat, sql_error_text);
746 |
747 | /* next, log this error */
748 | ER_perror(FAC_QI, QI_SQLERR, "at %s:%d, tid:%lu [%d] %s sql='%s'",
749 | file, line, (unsigned long)tid,
750 | SQ_errno(sql_connection),
751 | SQ_error(sql_connection),
752 | sql_command);
753 | }
754 |
755 | /* write_objects() */
756 | /*++++++++++++++++++++++++++++++++++++++
757 |
758 | SQ_connection_t *sql_connection The connection to the database.
759 |
760 | char *id_table The id of the temporary table (This is a result of the hacky
761 | way we've tried to get MySQL to do sub-selects.)
762 |
763 | sk_conn_st *condat Connection data for the client
764 |
765 | More:
766 | +html+ <PRE>
767 | Authors:
768 | ottrey,
769 | marek.
770 | +html+ </PRE>
771 | ++++++++++++++++++++++++++++++++++++++*/
772 | static int
773 | write_objects(SQ_connection_t **sql_connection,
774 | char *id_table,
775 | unsigned int filtered,
776 | unsigned int fast,
777 | sk_conn_st *condat,
778 | acc_st *acc_credit,
779 | acl_st *acl
780 | )
781 | {
782 | SQ_result_set_t *result = NULL;
783 | int retrieved_objects=0;
784 | char sql_command[STR_XL];
785 |
786 | sprintf(sql_command, Q_OBJECTS, id_table);
787 |
788 | if (sql_execute_watched(condat, sql_connection, sql_command, &result) == -1) {
789 | report_sql_error(condat, *sql_connection, sql_command);
790 | return SQ_errno(*sql_connection);
791 | }
792 |
793 | /* Problem: if the query was aborted, the result structure does not
794 | refer to any existing connection anymore. So we check rtc here.
795 | */
796 |
797 | if( condat->rtc == 0) {
798 | retrieved_objects = write_results(result, filtered, fast, condat,
799 | acc_credit, acl);
800 | SQ_free_result(result);
801 | }
802 | return 0;
803 | } /* write_objects() */
804 |
805 | /* rx_node_has_mnt_irt() */
806 | /*++++++++++++++++++++++++++++++++++++++
807 | See if the node has an "mnt-irt:" attribute.
808 |
809 | condat connection data for the client
810 | sql_connection connection to the database
811 | rx_data data node returned from the RX module
812 | ++++++++++++++++++++++++++++++++++++++*/
813 | gboolean
814 | rx_node_has_mnt_irt (sk_conn_st *condat,
815 | SQ_connection_t *sql_connection,
816 | int object_id)
817 | {
818 | GString *sql_command;
819 | SQ_result_set_t *result_ptr;
820 | int sql_error;
821 | SQ_row_t *row;
822 | long num_irt_ref;
823 |
824 | /* query database for references from this object to an IRT */
825 | sql_command = g_string_new("");
826 | g_string_sprintf(sql_command,
827 | "SELECT COUNT(*) FROM mnt_irt WHERE object_id=%d",
828 | object_id);
829 | if (SQ_execute_query(sql_connection, sql_command->str, &result_ptr) == -1) {
830 | sql_error = SQ_errno(sql_connection);
831 | report_sql_error(condat, sql_connection, sql_command->str);
832 | g_string_free(sql_command, TRUE);
833 | return FALSE;
834 | }
835 | g_string_free(sql_command, TRUE);
836 |
837 | /* get number of references */
838 | row = SQ_row_next(result_ptr);
839 | if (row == NULL) {
840 | /* if the object is deleted after we have gotten a reference to it,
841 | the lookup will fail - in such a case treat it the same as
842 | an object without any mnt_irt attributes */
843 | num_irt_ref = 0;
844 | } else {
845 | if (SQ_get_column_int(result_ptr, row, 0, &num_irt_ref) != 0) {
846 | ER_perror(FAC_QI, QI_SQLERR, "bad column at %s:%d",
847 | __FILE__, __LINE__);
848 | num_irt_ref = 0;
849 | }
850 | }
851 | SQ_free_result(result_ptr);
852 |
853 | /* return appropriate result */
854 | if (num_irt_ref > 0) {
855 | return TRUE;
856 | } else {
857 | return FALSE;
858 | }
859 | }
860 |
861 |
862 | /* mnt_irt_filter() */
863 | /*++++++++++++++++++++++++++++++++++++++
864 | Find the most specific entry in the list of network nodes passed
865 | and remove all entries but that one from the list.
866 |
867 | condat connection data for the client
868 | sql_connection connection to the database
869 | datlist list of data from the radix tree (nodes of *rx_datcpy_t)
870 |
871 | Note: The "-c" query flag specifies that the most specific inetnum or
872 | inet6num with an "mnt-irt:" attribute should be returned.
873 |
874 | To do this, we get a list of encompassing networks by sending the
875 | same options to the RP/RX module that the '-L' query does. Then
876 | we call this function, mnt_irt_filter(), and search the list for
877 | the first entry that has an "mnt-irt:" attribute, by looking in
878 | the "mnt_irt" table in MySQL for a reference (see the
879 | rx_node_has_mnt_irt() for details).
880 |
881 | If a reference is found, the list is replaced with a list
882 | containing the single entry. If no reference is found, the list
883 | is deleted.
884 | ++++++++++++++++++++++++++++++++++++++*/
885 | void
886 | mnt_irt_filter (sk_conn_st *condat,
887 | SQ_connection_t *sql_connection,
888 | GList **datlist)
889 | {
890 | GList *p;
891 | GList *new_datlist;
892 | rx_datcpy_t *rx_data;
893 | int object_id;
894 |
895 | /* empty datlist */
896 | new_datlist = NULL;
897 |
898 | /* search for node with "mnt-irt:" attribute */
899 | for (p=g_list_last(*datlist); p != NULL; p=g_list_previous(p)) {
900 | /* grab the data for this node */
901 | rx_data = (rx_datcpy_t *)p->data;
902 | object_id = rx_data->leafcpy.data_key;
903 |
904 | /* see if this is the node we are looking for */
905 | if (rx_node_has_mnt_irt(condat, sql_connection, object_id)) {
906 | /* use this entry, and remove from old list */
907 | new_datlist = g_list_append(NULL, rx_data);
908 | *datlist = g_list_remove_link(*datlist, p);
909 | g_list_free_1(p);
910 | break;
911 | }
912 | }
913 |
914 | /* free our old datlist */
915 | for (p=*datlist; p != NULL; p = g_list_next(p)) {
916 | rx_data = (rx_datcpy_t *)p->data;
917 | UT_free(rx_data->leafcpy.data_ptr);
918 | p = g_list_next(p);
919 | }
920 | wr_clear_list(datlist);
921 |
922 | /* use our new datlist */
923 | *datlist = new_datlist;
924 | }
925 |
926 |
927 | /* insert_radix_serials() */
928 | /*++++++++++++++++++++++++++++++++++++++
929 | Insert the radix serial numbers into a temporary table in the database.
930 |
931 | mask_t bitmap The bitmap of attribute to be converted.
932 |
933 | SQ_connection_t *sql_connection The connection to the database.
934 |
935 | char *id_table The id of the temporary table (This is a result of the hacky
936 | way we've tried to get MySQL to do sub-selects.)
937 |
938 | GList *datlist The list of data from the radix tree.
939 |
940 | More:
941 | +html+ <PRE>
942 | Authors:
943 | ottrey,
944 | marek
945 | +html+ </PRE>
946 |
947 | ++++++++++++++++++++++++++++++++++++++*/
948 | static int insert_radix_serials(sk_conn_st *condat,
949 | SQ_connection_t *sql_connection,
950 | char *id_table, GList *datlist) {
951 | GList *qitem;
952 | GString *sql_command;
953 | int serial;
954 | int sql_error;
955 |
956 | sql_error = 0;
957 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
958 | rx_datcpy_t *datcpy = qitem->data;
959 |
960 | serial = datcpy->leafcpy.data_key;
961 |
962 | /* don't bother to insert values into our temporary table */
963 | /* if we've lost the client connection */
964 | if ((condat->rtc == 0) && !sql_error) {
965 | sql_command = g_string_sized_new(STR_S);
966 | g_string_sprintf(sql_command,
967 | "INSERT INTO %s values (%d)", id_table, serial);
968 | if (SQ_execute_query(sql_connection, sql_command->str, NULL) == -1) {
969 | sql_error = SQ_errno(sql_connection);
970 | report_sql_error(condat, sql_connection, sql_command->str);
971 | }
972 | g_string_free(sql_command, TRUE);
973 | }
974 |
975 | UT_free(datcpy->leafcpy.data_ptr);
976 | }
977 |
978 | wr_clear_list( &datlist );
979 |
980 | /* return error, if any */
981 | return sql_error;
982 |
983 | } /* insert_radix_serials() */
984 |
985 |
986 | /* write_radix_immediate() */
987 | /*++++++++++++++++++++++++++++++++++++++
988 | Display the immediate data carried with the objects returned by the
989 | radix tree.
990 |
991 | GList *datlist The linked list of dataleaf copies
992 |
993 | sk_conn_st *condat Connection data for the client
994 |
995 | acc_st *acc_credit Accounting struct
996 |
997 | More:
998 | +html+ <PRE>
999 | Authors:
1000 | marek
1001 | +html+ </PRE>
1002 |
1003 | Also free the list of answers.
1004 | ++++++++++++++++++++++++++++++++++++++*/
1005 | static void write_radix_immediate(GList *datlist,
1006 | sk_conn_st *condat,
1007 | acc_st *acc_credit,
1008 | acl_st *acl)
1009 | {
1010 | GList *qitem;
1011 |
1012 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
1013 | rx_datcpy_t *datcpy = qitem->data;
1014 |
1015 | SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
1016 | SK_cd_puts(condat, "\n");
1017 |
1018 | UT_free(datcpy->leafcpy.data_ptr);
1019 |
1020 | AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
1021 |
1022 | if(condat->rtc != 0) {
1023 | break;
1024 | }
1025 | }
1026 |
1027 | wr_clear_list( &datlist );
1028 | } /* write_radix_immediate() */
1029 |
1030 |
1031 | /* map_qc2rx() */
1032 | /*++++++++++++++++++++++++++++++++++++++
1033 | The mapping between a query_command and a radix query.
1034 |
1035 | Query_instruction *qi The Query Instruction to be created from the mapping
1036 | of the query command.
1037 |
1038 | const Query_command *qc The query command to be mapped.
1039 |
1040 | More:
1041 | +html+ <PRE>
1042 | Authors:
1043 | ottrey,
1044 | marek - simplified the logic, added stealth -S option
1045 | +html+ </PRE>
1046 |
1047 | ++++++++++++++++++++++++++++++++++++++*/
1048 | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
1049 | int result=1;
1050 | int allflags = (qc->L == 1) + (qc->M == 1) + (qc->l == 1)
1051 | + (qc->m == 1) + (qc->x == 1);
1052 |
1053 | allflags += qc->c_irt_search;
1054 |
1055 | qi->rx_keys = qc->keys;
1056 |
1057 | /* only one option can be active at a time */
1058 |
1059 | if( allflags > 1 ) {
1060 | /* user error (this should have been checked before) */
1061 |
1062 | ER_dbg_va(FAC_QI, ASP_QI_SKIP,
1063 | "ERROR in qc2rx mapping: bad combination of flags");
1064 | result = 0;
1065 | }
1066 | if( allflags == 0 ) {
1067 | /* no options active - default search */
1068 | qi->rx_srch_mode = RX_SRCH_EXLESS;
1069 | qi->rx_par_a = 0;
1070 | }
1071 | else if (qc->c_irt_search) {
1072 | qi->rx_srch_mode = RX_SRCH_LESS;
1073 | qi->rx_par_a = RX_ALL_DEPTHS;
1074 | }
1075 | else if ( qc->L == 1 ) {
1076 | qi->rx_srch_mode = RX_SRCH_LESS;
1077 | qi->rx_par_a = RX_ALL_DEPTHS;
1078 | }
1079 | else if (qc->M == 1) {
1080 | qi->rx_srch_mode = RX_SRCH_MORE;
1081 | qi->rx_par_a = RX_ALL_DEPTHS;
1082 | }
1083 | else if (qc->l == 1) {
1084 | qi->rx_srch_mode = RX_SRCH_LESS;
1085 | qi->rx_par_a = 1;
1086 | }
1087 | else if (qc->m == 1) {
1088 | qi->rx_srch_mode = RX_SRCH_MORE;
1089 | qi->rx_par_a = 1;
1090 | }
1091 | else if (qc->x == 1) {
1092 | qi->rx_srch_mode = RX_SRCH_EXACT;
1093 | qi->rx_par_a = 0;
1094 | }
1095 |
1096 | if( qi->rx_srch_mode == RX_SRCH_MORE && (qc->S == 1) ) {
1097 | qi->rx_srch_mode = RX_SRCH_DBLS;
1098 | }
1099 |
1100 | return result;
1101 |
1102 | } /* map_qc2rx() */
1103 |
1104 |
1105 | /* run_referral() */
1106 | /*++++++++++++++++++++++++++++++++++++++
1107 |
1108 | invoked when no such domain found. Goes through the domain table
1109 | and searches for shorter domains, then if it finds one with referral
1110 | it performs it, otherwise it just returns nothing.
1111 |
1112 | to perform referral, it actually composes the referral query
1113 | for a given host/port/type and calls the whois query function.
1114 |
1115 | Well, it returns nothing anyway (void). It just prints to the socket.
1116 |
1117 | char *ref_host referral server host name
1118 |
1119 | unsigned ref_port_int referral server port number
1120 |
1121 | char *qry query to be run
1122 |
1123 | Author:
1124 | marek
1125 | ++++++++++++++++++++++++++++++++++++++*/
1126 | void run_referral(Query_environ *qe,
1127 | char *ref_host,
1128 | unsigned ref_port_int,
1129 | char *qry)
1130 | {
1131 |
1132 | #if 1 /* switch off for testing */
1133 | er_ret_t err;
1134 | char *rep;
1135 |
1136 | /* WH_sock(sock, host, port, query, maxlines, timeout)) */
1137 | err= WH_cd_sock(&(qe->condat), ref_host, ref_port_int, qry,
1138 | ca_get_referralmaxlines, ca_get_referraltimeout
1139 | );
1140 |
1141 | switch( err ) {
1142 | case SK_OK:
1143 | /* OK */
1144 | break;
1145 | case SK_TIMEOUT:
1146 | /* Referral timeout */
1147 | rep = ca_get_qi_ref_tmout ;
1148 | SK_cd_puts(&(qe->condat), rep);
1149 | UT_free(rep);
1150 | break;
1151 |
1152 | case SK_BADHOST:
1153 | /* Referral host not found */
1154 | rep = ca_get_qi_ref_badhost ;
1155 | SK_cd_puts(&(qe->condat), rep);
1156 | UT_free(rep);
1157 | break;
1158 |
1159 | case SK_CONNECT:
1160 | /* Referral host not responding */
1161 | rep = ca_get_qi_ref_hostnottresp ;
1162 | SK_cd_puts(&(qe->condat), rep);
1163 | UT_free(rep);
1164 | break;
1165 |
1166 | case SK_BIND:
1167 | case SK_SOCKET:
1168 | /* XXX internal server problem... */
1169 | die;
1170 |
1171 | case WH_MAXLINES:
1172 | /* Referral reply line limit exceeded */
1173 | rep = ca_get_qi_ref_overmaxlin ;
1174 | SK_cd_puts(&(qe->condat), rep);
1175 | UT_free(rep);
1176 | break;
1177 |
1178 | default: /* any other errors ? */
1179 | die;
1180 | ;
1181 | } /*switch WH_sock */
1182 | #endif
1183 |
1184 | }/*run_referral*/
1185 |
1186 |
1187 |
1188 |
1189 |
1190 | /*++++++++++++++++++++++++++++++++++++++
1191 |
1192 | prepare and run the referral, displaying the results directly to the
1193 | client's connection.
1194 |
1195 | XXX still missing protection against a referral loop
1196 | XXX handling inverse flag not needed, to be removed
1197 |
1198 | char *domain domain being looked up
1199 |
1200 | Query_instructions *qis original query instructions structure
1201 |
1202 | Query_environ *qe original query environment structure
1203 |
1204 | Query_instruction *qi specific query instruction triggered
1205 |
1206 | SQ_result_set_t *result result of the lookup containing referral details
1207 |
1208 | SQ_row_t *row first row (should be only 1) of the result
1209 | this should contain columns: type, port, host
1210 |
1211 | char *sourcename name of the database "source"
1212 |
1213 | Author:
1214 | marek
1215 | ++++++++++++++++++++++++++++++++++++++*/
1216 | static
1217 | void qi_prep_run_refer(char *domain,
1218 | Query_instructions *qis,
1219 | Query_environ *qe,
1220 | Query_instruction *qi,
1221 | SQ_result_set_t *result, SQ_row_t *row,
1222 | char *sourcename )
1223 | {
1224 | int err;
1225 | long ref_type;
1226 | long ref_port;
1227 | char *ref_host;
1228 | GString *querystr;
1229 |
1230 | /* get values from SQL query */
1231 | err = SQ_get_column_int(result, row, 0, &ref_type);
1232 | dieif(err);
1233 | err = SQ_get_column_int(result, row, 1, &ref_port);
1234 | dieif(err);
1235 | ref_host = SQ_get_column_string(result, row, 2);
1236 | dieif(ref_host == NULL);
1237 |
1238 | querystr = g_string_sized_new(STR_L);
1239 |
1240 | /* put -r if the reftype is RIPE and -r or -i were used */
1241 | if( (ref_type == RF_RIPE)
1242 | && ( Query[qi->queryindex].querytype == Q_INVERSE
1243 | || qis->recursive > 0 ) )
1244 | {
1245 | g_string_append(querystr, "-r ");
1246 | }
1247 |
1248 | /* prepend with -Vversion,IP for type CLIENTADDRESS */
1249 | if( ref_type == RF_CLIENTADDRESS ) {
1250 | g_string_sprintf(querystr, "-V%s,%s ", VERSION, qe->condat.ip);
1251 | }
1252 |
1253 |
1254 | /* now set the search term - set to the stripped down version
1255 | for inverse query, full-length otherwise */
1256 | if( Query[qi->queryindex].querytype == Q_INVERSE ) {
1257 | g_string_append(querystr, domain);
1258 | }
1259 | else {
1260 | g_string_append(querystr, qis->qc->keys);
1261 | }
1262 |
1263 | {
1264 | /* the object is not from %s,
1265 | it comes from %s %d, use -R to see %s */
1266 | char *rep = ca_get_qi_fmt_refheader ;
1267 | SK_cd_printf(&(qe->condat), rep,
1268 | sourcename,
1269 | ref_host, ref_port,
1270 | sourcename );
1271 | UT_free(rep);
1272 | }
1273 |
1274 | /* do the referral */
1275 | ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host);
1276 |
1277 | run_referral( qe, ref_host, ref_port, querystr->str);
1278 |
1279 | { /* End of referred query result */
1280 | char *rep = ca_get_qi_reftrailer ;
1281 | SK_cd_puts(&(qe->condat), rep);
1282 | UT_free(rep);
1283 | }
1284 | SK_cd_puts(&(qe->condat), "\n");
1285 |
1286 | g_string_free(querystr, TRUE);
1287 | UT_free(ref_host);
1288 | }
1289 |
1290 |
1291 | /*++++++++++++++++++++++++++++++++++++++
1292 |
1293 | specific case of the object ID collection: the domains.
1294 | Checks to see if the domain exists, and runs the referral if it is defined
1295 | and the domain is missing.
1296 |
1297 | Arguments:
1298 |
1299 | char *sourcename name of the database "source"
1300 |
1301 | SQ_connection_t *sql_connection sql connection dedicated to this thread
1302 |
1303 | char *id_table name of the temporary table to be used
1304 |
1305 | char *sub_table name of the temporary subtable
1306 |
1307 | Query_instructions *qis original query instructions structure
1308 |
1309 | Query_environ *qe original query environment structure
1310 |
1311 | Query_instruction *qi specific query instruction triggered
1312 |
1313 | acc_st *acc_credit credit for this client
1314 |
1315 | Author:
1316 | marek.
1317 | ++++++++++++++++++++++++++++++++++++++*/
1318 |
1319 | static int
1320 | qi_collect_domain(char *sourcename,
1321 | SQ_connection_t *sql_connection,
1322 | char *id_table,
1323 | char *sub_table,
1324 | Query_instructions *qis,
1325 | Query_environ *qe,
1326 | Query_instruction *qi,
1327 | acc_st *acc_credit,
1328 | int *sql_error)
1329 | {
1330 | char *domain = qis->qc->keys;
1331 | char *dot = domain;
1332 | int subcount = 0;
1333 | int foundcount = 0;
1334 | GString *sql_command;
1335 |
1336 | /* we MUST NOT have a diconnection from the server here */
1337 | dieif(qe->condat.rtc != 0);
1338 |
1339 | /* create a string for our queries */
1340 | sql_command = g_string_sized_new(STR_XL);
1341 |
1342 | /* while nothing found and still some pieces of the name left */
1343 | while( dot != NULL && subcount == 0 ) {
1344 | int refcount = 0;
1345 | SQ_row_t *row;
1346 | SQ_result_set_t *result_referrals = NULL;
1347 |
1348 | ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
1349 |
1350 | /* domain lookup -- query into the _S table */
1351 | g_string_sprintf(sql_command,
1352 | "INSERT INTO %s SELECT object_id "
1353 | "FROM domain "
1354 | "WHERE domain = '%s'",
1355 | sub_table, dot);
1356 | if (SQ_execute_query(sql_connection, sql_command->str, NULL) != 0) {
1357 | *sql_error = SQ_errno(sql_connection);
1358 | report_sql_error(&qe->condat, sql_connection, sql_command->str);
1359 | foundcount = 0;
1360 | goto exit_qi_collect_domain;
1361 | }
1362 | subcount = SQ_get_affected_rows(sql_connection);
1363 |
1364 | if( subcount != 0 ) { /* domain exists in the database */
1365 |
1366 | /* referral check. Always done except for -R and INVERSE queries */
1367 | if( qis->qc->R == 0 &&
1368 | Query[qi->queryindex].querytype != Q_INVERSE ) {
1369 | g_string_sprintf(sql_command,
1370 | "SELECT type, port, host "
1371 | "FROM %s ID, refer "
1372 | "WHERE ID.id = refer.object_id",
1373 | sub_table);
1374 |
1375 | if( SQ_execute_query(sql_connection, sql_command->str,
1376 | &result_referrals) == -1)
1377 | {
1378 | *sql_error = SQ_errno(sql_connection);
1379 | report_sql_error(&qe->condat, sql_connection, sql_command->str);
1380 | foundcount = 0;
1381 | goto exit_qi_collect_domain;
1382 | }
1383 | refcount = SQ_num_rows(result_referrals);
1384 | }
1385 |
1386 | /* if referral allowed and defined, even if domain was found but
1387 | contained referral - refer the query */
1388 | if( refcount != 0 ) {
1389 | /* get the referral parameters from the first row
1390 | and perform it
1391 | */
1392 |
1393 | row = SQ_row_next(result_referrals);
1394 | /* now: query for the original domain */
1395 | qi_prep_run_refer(domain,
1396 | qis, qe, qi, result_referrals, row, sourcename);
1397 |
1398 | acc_credit->referrals -= 1;
1399 | }
1400 | else {
1401 | /* domain found
1402 | and (referral undefined or disabled by -R or inverse)
1403 | two possible outcomes depending on whether 'dot' is:
1404 | * the original search term -> pass what's in _S and quit
1405 | * a 'stripped' domain name -> return no result and quit
1406 | */
1407 | if( dot == domain ) {
1408 | g_string_sprintf(sql_command,
1409 | "INSERT INTO %s SELECT id FROM %s",
1410 | id_table, sub_table);
1411 | if (SQ_execute_query(sql_connection,
1412 | sql_command->str, NULL) == -1)
1413 | {
1414 | *sql_error = SQ_errno(sql_connection);
1415 | report_sql_error(&qe->condat, sql_connection,
1416 | sql_command->str);
1417 | foundcount = 0;
1418 | goto exit_qi_collect_domain;
1419 | }
1420 | foundcount = SQ_get_affected_rows(sql_connection);
1421 | }
1422 | }
1423 | dot = NULL; /* don't make another round */
1424 | } /* a domain was found */
1425 |
1426 | if( result_referrals != NULL ) {
1427 | SQ_free_result(result_referrals);
1428 | result_referrals = NULL;
1429 | }
1430 |
1431 | if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1432 | dot++;
1433 | }
1434 | }
1435 |
1436 | /* unified success/failure exit point to perform cleanup */
1437 | exit_qi_collect_domain:
1438 |
1439 | /* free the string we used for queries */
1440 | g_string_free(sql_command, TRUE);
1441 |
1442 | return foundcount;
1443 | } /* qi_collect_domain */
1444 |
1445 |
1446 | /* add_ref_name */
1447 | /*++++++++++++++++++++++++++++++++++++++
1448 |
1449 | Creates a SQL query for a reference-by-name lookup. Uses standard name
1450 | lookup query generator (create_name_query), so the order of the names
1451 | doesn't matter.
1452 |
1453 | SQ_connection_t *sql_connection sql connection dedicated to this thread
1454 |
1455 | char *rectable table in which to look up
1456 |
1457 | char *allnames all name words to be looked up, space delimited.
1458 |
1459 | ++++++++++++++++++++++++++++++++++++++*/
1460 | static
1461 | int
1462 | add_ref_name(SQ_connection_t *sql_connection,
1463 | char *rectable,
1464 | char *allnames,
1465 | sk_conn_st *condat
1466 | )
1467 | {
1468 | int error;
1469 |
1470 | error = 0;
1471 |
1472 | /* construct the query, allow zero-length list */
1473 | if( strlen(allnames) > 0 ) {
1474 | GString *final_query;
1475 | GString *select_query;
1476 |
1477 | final_query = g_string_sized_new(STR_XL);
1478 | select_query = g_string_sized_new(STR_XL);
1479 |
1480 | create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1481 | "AND N00.object_type != 100 AND N00.thread_id = 0",
1482 | allnames);
1483 |
1484 | g_string_sprintf(final_query, "INSERT INTO %s %s",
1485 | rectable, select_query->str);
1486 |
1487 | allnames[0]=0;
1488 |
1489 | if (SQ_execute_query(sql_connection, final_query->str, NULL) == -1 ) {
1490 | report_sql_error(condat, sql_connection, final_query->str);
1491 | error = SQ_errno(sql_connection);
1492 | }
1493 |
1494 | g_string_free(select_query, TRUE);
1495 | g_string_free(final_query, TRUE);
1496 | }
1497 |
1498 | return error;
1499 | }/* add_ref_name */
1500 |
1501 |
1502 |
1503 | /* qi_collect_ids */
1504 | /*++++++++++++++++++++++++++++++++++++++
1505 |
1506 | collects object ID's from all queries defined in the Query_instructions
1507 | array. The results from RADIX trees are maintained in a linked list, the
1508 | results from SQL lookups are kept in a temporary table. For domains,
1509 | a specific function is invoked that may run the referral.
1510 | Any sql lookup will be limited to the maximum number of objects allowed
1511 | for the client (acl and credit are checked for this).
1512 | The routine uses its own temporary _S table, destroyed at exit.
1513 |
1514 | ca_dbSource_t *dbhdl source-specific identifier (defined in CA)
1515 |
1516 | char *sourcename name of the database "source"
1517 |
1518 | SQ_connection_t **sql_connection sql connection dedicated to this thread
1519 | (replaced on cancel)
1520 |
1521 | Query_instructions *qis original query instructions structure
1522 |
1523 | Query_environ *qe original query environment structure
1524 |
1525 | char *id_table the table to store the ID's found
1526 |
1527 | GList **datlist the list to store the Radix leaves found
1528 |
1529 | acc_st *acc_credit credit for this client
1530 |
1531 | acl_st *acl acl for this client
1532 |
1533 | ++++++++++++++++++++++++++++++++++++++*/
1534 | static
1535 | int
1536 | qi_collect_ids(ca_dbSource_t *dbhdl,
1537 | char *sourcename,
1538 | SQ_connection_t **sql_connection,
1539 | Query_instructions *qis,
1540 | Query_environ *qe,
1541 | char *id_table,
1542 | GList **datlist,
1543 | acc_st *acc_credit,
1544 | acl_st *acl
1545 | )
1546 | {
1547 | Query_instruction **ins=NULL;
1548 | int i;
1549 | int count, errors=0;
1550 | GString *sql_command;
1551 | er_ret_t err;
1552 | char sub_table[64];
1553 | int limit ;
1554 | /* a limit on the max number of objects to be returned
1555 | from a single search. For some queries the object types
1556 | are not known at this stage, so the limit must be
1557 | the higher number of the two: private / public,
1558 | or unlimited if any of them is 'unlimited'.
1559 | */
1560 | char limit_str[32];
1561 | int sql_error;
1562 |
1563 | /* use a nice resizing GString for our command */
1564 | sql_command = g_string_sized_new(STR_XL);
1565 |
1566 | if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1567 | strcpy(limit_str,"");
1568 | } else {
1569 | sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1570 | so that the client hits
1571 | the limit */
1572 | }
1573 |
1574 | sprintf(sub_table, "%s_S ", id_table);
1575 |
1576 | /* see if there was a leftover table from a crashed session
1577 | * (assume the ID cannot be currently in use)
1578 | *
1579 | * update: this can't ever happen with TEMPORARY tables, but we're going to
1580 | * check for it anyway - shane
1581 | */
1582 | g_string_sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1583 | if( SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1584 | report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1585 | return SQ_errno(*sql_connection);
1586 | }
1587 |
1588 | /* create a table for special subqueries (domain only for now) */
1589 | g_string_sprintf(sql_command,
1590 | "CREATE " TEMPORARY " TABLE %s ( id int ) TYPE=HEAP",
1591 | sub_table);
1592 | if( SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1593 | report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1594 | return SQ_errno(*sql_connection);
1595 | }
1596 |
1597 | /* Iterate through query instructions */
1598 | ins = qis->instruction;
1599 | sql_error = 0;
1600 | for (i=0; ins[i] != NULL && errors == 0; i++) {
1601 | Query_instruction *qi = ins[i];
1602 |
1603 | /* check if the client is still there */
1604 | if( qe->condat.rtc ) {
1605 | break;
1606 | }
1607 |
1608 | switch ( qi->search_type ) {
1609 | case R_SQL:
1610 | count = 0;
1611 | if ( qi->query_str != NULL ) {
1612 |
1613 | /* handle special cases first */
1614 | if( Query[qi->queryindex].class == C_DN
1615 | && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1616 |
1617 | /* if any more cases than just domain appear, we will be
1618 | cleaning the _S table from the previous query here
1619 |
1620 | "DELETE FROM %s_S"
1621 | */
1622 |
1623 | count = qi_collect_domain(sourcename, *sql_connection, id_table,
1624 | sub_table, qis, qe, qi, acc_credit,
1625 | &sql_error);
1626 | } /* if class DN and Straight lookup */
1627 | else {
1628 | /* any other class of query */
1629 |
1630 | g_string_sprintf(sql_command, "INSERT INTO %s %s %s",
1631 | id_table, qi->query_str, limit_str);
1632 |
1633 | if(sql_execute_watched( &(qe->condat), sql_connection,
1634 | sql_command->str, NULL) == -1 ) {
1635 | errors++;
1636 | sql_error = SQ_errno(*sql_connection);
1637 | report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1638 | }
1639 | count = SQ_get_affected_rows(*sql_connection);
1640 | } /* not DN */
1641 | } /* if SQL query not NULL */
1642 |
1643 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1644 | "%d entries added in %s query for %s",
1645 | count, Query[qi->queryindex].descr, qis->qc->keys
1646 | );
1647 | break;
1648 |
1649 | case R_RADIX:
1650 |
1651 |
1652 | err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0,
1653 | qi->rx_keys, dbhdl,
1654 | Query[qi->queryindex].attribute,
1655 | datlist, limit);
1656 |
1657 |
1658 | if( NOERR(err)) {
1659 | if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1660 | /* prevent unnecessary g_list_length call */
1661 |
1662 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1663 | "%d entries after %s (mode %d par %d reg %d) query for %s",
1664 | g_list_length(*datlist),
1665 | Query[qi->queryindex].descr,
1666 | qi->rx_srch_mode, qi->rx_par_a,
1667 | dbhdl,
1668 | qi->rx_keys);
1669 | }
1670 | }
1671 | else {
1672 | ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1673 | "RP_asc_search returned %x ", err);
1674 | }
1675 | break;
1676 |
1677 | default: die;
1678 | } /* switch */
1679 |
1680 | } /* for <every instruction> */
1681 |
1682 | /* Now drop the _S table */
1683 | g_string_sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1684 | if(SQ_execute_query(*sql_connection, sql_command->str, NULL) == -1 ) {
1685 | report_sql_error(&qe->condat, *sql_connection, sql_command->str);
1686 | sql_error = SQ_errno(*sql_connection);
1687 | }
1688 |
1689 | /* free our command buffer */
1690 | g_string_free(sql_command, TRUE);
1691 |
1692 | /* return success */
1693 | return sql_error;
1694 | }
1695 |
1696 | /* qi_fetch_references */
1697 | /*++++++++++++++++++++++++++++++++++++++
1698 |
1699 | given the list of object ID's collects the references from these objects
1700 | to person and role objects. Uses its own temporary SQL table (_R)
1701 | and upon completion transfers the results from it to the main
1702 | temporary table. Runs queries in watched mode, to be able to cancel them.
1703 |
1704 | SQ_connection_t **sql_connection sql connection dedicated to this thread
1705 | (replaced on cancel)
1706 |
1707 | Query_environ *qe original query environment structure
1708 |
1709 | char *id_table the table with the ID's found
1710 |
1711 | acc_st *acc_credit credit for this client
1712 |
1713 | acl_st *acl acl for this client
1714 |
1715 | ++++++++++++++++++++++++++++++++++++++*/
1716 | static
1717 | int
1718 | qi_fetch_references(SQ_connection_t **sql_connection,
1719 | Query_environ *qe,
1720 | char *id_table,
1721 | acc_st *acc_credit,
1722 | acl_st *acl
1723 | )
1724 | {
1725 | char rec_table[64];
1726 | SQ_result_set_t *result = NULL;
1727 | SQ_row_t *row;
1728 | int thisid = 0;
1729 | int oldid = 0;
1730 | char allnames[STR_L];
1731 | char sql_command[STR_XL];
1732 | int sql_error;
1733 |
1734 | /* use sql_error to flag errors */
1735 | sql_error = 0;
1736 |
1737 | sprintf(rec_table, "%s_R", id_table);
1738 |
1739 | /* see if there was a leftover table from a crashed session
1740 | * (assume the ID cannot be currently in use)
1741 | */
1742 | sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1743 | if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1744 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1745 | return SQ_errno(*sql_connection);
1746 | }
1747 |
1748 | /* a temporary table for recursive data must be created, because
1749 | a query using the same table as a source and target is illegal
1750 | ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1751 | */
1752 | sprintf(sql_command,
1753 | "CREATE " TEMPORARY " TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP",
1754 | rec_table);
1755 | if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1) {
1756 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1757 | return SQ_errno(*sql_connection);
1758 | }
1759 |
1760 | /* from this point on, we can't just return on error, because
1761 | we need to insure the table we just created gets dropped */
1762 |
1763 | /* find the contacts */
1764 | sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1765 | if (sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL)
1766 | == -1)
1767 | {
1768 | sql_error = SQ_errno(*sql_connection);
1769 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1770 | }
1771 |
1772 | if (!sql_error) {
1773 | sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1774 | if (sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1775 | NULL) == -1)
1776 | {
1777 | sql_error = SQ_errno(*sql_connection);
1778 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1779 | }
1780 | }
1781 |
1782 | if (!sql_error) {
1783 | sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c");
1784 | if (sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1785 | NULL) == -1)
1786 | {
1787 | sql_error = SQ_errno(*sql_connection);
1788 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1789 | }
1790 | }
1791 |
1792 | if (!sql_error) {
1793 | sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c");
1794 | if (sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1795 | NULL) == -1)
1796 | {
1797 | sql_error = SQ_errno(*sql_connection);
1798 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1799 | }
1800 | }
1801 |
1802 | /* replace references to dummies by references by name */
1803 | if (!sql_error) {
1804 | sprintf(sql_command,
1805 | " SELECT id, name FROM %s IDS STRAIGHT_JOIN names "
1806 | " WHERE IDS.id = names.object_id "
1807 | " AND names.object_type = 100"
1808 | " ORDER BY id",
1809 | rec_table);
1810 | if (sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1811 | &result) == -1)
1812 | {
1813 | sql_error = SQ_errno(*sql_connection);
1814 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1815 | }
1816 | }
1817 |
1818 | /* well, it might not be -1, but if the watchdog worked then the
1819 | result is NULL */
1820 | if (!sql_error && (result != NULL)) {
1821 |
1822 | allnames[0]=0;
1823 | /* now go through the results and collect names */
1824 | while ( !sql_error &&
1825 | (qe->condat.rtc == 0) &&
1826 | (row = SQ_row_next(result)) != NULL )
1827 | {
1828 | char *id = SQ_get_column_string(result, row, 0);
1829 | char *name = SQ_get_column_string(result, row, 1);
1830 |
1831 | thisid = atoi(id);
1832 |
1833 | /* when the id changes, the name is complete */
1834 | if( thisid != oldid && oldid != 0 ) {
1835 | sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1836 | &qe->condat);
1837 | }
1838 |
1839 | strcat(allnames, name);
1840 | strcat(allnames, " ");
1841 | oldid = thisid;
1842 | UT_free(id);
1843 | UT_free(name);
1844 | }
1845 | /* also do the last name */
1846 | if (!sql_error) {
1847 | sql_error = add_ref_name( *sql_connection, rec_table, allnames,
1848 | &qe->condat);
1849 | }
1850 |
1851 | SQ_free_result(result); /* we can do it only because the watchdog */
1852 | /* has not started between the check for non-NULL result and here */
1853 | }
1854 |
1855 | /* if we've lost connection, don't bother with this extra work */
1856 | if (!sql_error && (qe->condat.rtc == 0)) {
1857 | /* now copy things back to the main temporary table */
1858 | sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s",
1859 | id_table, rec_table);
1860 | if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1861 | sql_error = SQ_errno(*sql_connection);
1862 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1863 | }
1864 | }
1865 |
1866 | /* Now drop the IDS recursive table (try to do this even if
1867 | we had an SQL error, to avoid leaving extra tables lying around) */
1868 | sprintf(sql_command, "DROP TABLE IF EXISTS %s", rec_table);
1869 | if (SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ) {
1870 | sql_error = SQ_errno(*sql_connection);
1871 | report_sql_error(&qe->condat, *sql_connection, sql_command);
1872 | }
1873 |
1874 | /* return error, if any */
1875 | return sql_error;
1876 | }
1877 | /* qi_fetch_references */
1878 |
1879 |
1880 | /* QI_execute() */
1881 | /*++++++++++++++++++++++++++++++++++++++
1882 | Execute the query instructions. This is called for each source.
1883 | This is linked into MySQL by the fact that MySQL doesn't have sub selects
1884 | (yet). The queries are done in two stages. Make some temporary tables and
1885 | insert into them. Then use them in the next select.
1886 |
1887 |
1888 | ca_dbSource_t *dbhdl source-specific identifier (defined in CA)
1889 |
1890 | Query_instructions *qis query instructions.
1891 |
1892 | Query_environ *qe query environment.
1893 |
1894 | acc_st *acc_credit object display credit
1895 |
1896 | acl_st *acl copy of the original acl for this client
1897 |
1898 | More:
1899 | +html+ <PRE>
1900 | Authors:
1901 | ottrey - original version,
1902 | marek - the rest.
1903 | +html+ </PRE>
1904 | ++++++++++++++++++++++++++++++++++++++*/
1905 | er_ret_t QI_execute(ca_dbSource_t *dbhdl,
1906 | Query_instructions *qis,
1907 | Query_environ *qe,
1908 | acc_st *acc_credit,
1909 | acl_st *acl
1910 | )
1911 | {
1912 | /* those things must be freed after use! */
1913 | char *dbhost = ca_get_srcdbmachine(dbhdl);
1914 | char *dbname = ca_get_srcdbname(dbhdl);
1915 | char *dbuser = ca_get_srcdbuser(dbhdl);
1916 | char *dbpass = ca_get_srcdbpassword(dbhdl);
1917 | char *srcnam;
1918 | unsigned dbport = ca_get_srcdbport(dbhdl);
1919 | char id_table[64];
1920 | char sql_command[STR_XL];
1921 | GList *datlist=NULL;
1922 | SQ_connection_t *sql_connection=NULL;
1923 | int sql_error;
1924 |
1925 | sql_connection = SQ_get_connection( dbhost, dbport,
1926 | dbname, dbuser, dbpass );
1927 | /* free parameters when done */
1928 | UT_free(dbhost);
1929 | UT_free(dbuser);
1930 | UT_free(dbpass);
1931 |
1932 | /* return error if occurred */
1933 | if (sql_connection == NULL) {
1934 | ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s",
1935 | dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1936 | UT_free(dbname);
1937 | return QI_CANTDB;
1938 | }
1939 | UT_free(dbname);
1940 |
1941 | /* from here on out, we use the sql_error flag to verify our
1942 | connection to the SQL database is still good */
1943 | sql_error = 0;
1944 |
1945 | sprintf(id_table, "ID_%lu_%u", mysql_thread_id(sql_connection),
1946 | pthread_self());
1947 |
1948 | /* see if there was a leftover table from a crashed session
1949 | * (assume the ID cannot be currently in use)
1950 | */
1951 | sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1952 | if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1953 | sql_error = SQ_errno(sql_connection);
1954 | report_sql_error(&qe->condat, sql_connection, sql_command);
1955 | }
1956 |
1957 | /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1958 | if (!sql_error) {
1959 | sprintf(sql_command,
1960 | "CREATE " TEMPORARY
1961 | " TABLE %s ( id INT PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1962 | if (SQ_execute_query(sql_connection, sql_command, NULL) == -1 ) {
1963 | sql_error = SQ_errno(sql_connection);
1964 | report_sql_error(&qe->condat, sql_connection, sql_command);
1965 | }
1966 | }
1967 |
1968 | if (!sql_error) {
1969 | srcnam = ca_get_srcname(dbhdl);
1970 | sql_error = qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe,
1971 | id_table, &datlist, acc_credit, acl);
1972 | UT_free(srcnam);
1973 | }
1974 |
1975 | /* post-processing */
1976 | if (!sql_error && (qis->filtered == 0)) {
1977 | /* start the watchdog just to set the rtc flag */
1978 | SK_watch_setclear(&(qe->condat));
1979 | SK_watchstart(&(qe->condat));
1980 |
1981 | /* add radix results (only if -K is not active and still connected) */
1982 | if (qe->condat.rtc == 0) {
1983 |
1984 | if (qis->qc->c_irt_search) {
1985 | mnt_irt_filter(&(qe->condat), sql_connection, &datlist);
1986 | }
1987 |
1988 | sql_error = insert_radix_serials(&(qe->condat),
1989 | sql_connection,
1990 | id_table,
1991 | datlist);
1992 | }
1993 |
1994 | SK_watchstop(&(qe->condat));
1995 | }
1996 |
1997 | /* fetch recursive objects (ac,tc,zc,ah) */
1998 | if (!sql_error && qis->recursive && (qe->condat.rtc == 0)) {
1999 | sql_error = qi_fetch_references(&sql_connection,
2000 | qe,
2001 | id_table,
2002 | acc_credit,
2003 | acl);
2004 | } /* if recursive */
2005 |
2006 | /* display */
2007 | /* -K filtering:
2008 | * right now only filtering, no expanding sets like write_set_objects()
2009 | */
2010 |
2011 | /* display the immediate data from the radix tree */
2012 | if (!sql_error && (qis->filtered == 1)) {
2013 | write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
2014 | }
2015 |
2016 | /* display objects from the IDs table */
2017 | if (!sql_error) {
2018 | sql_error = write_objects( &sql_connection, id_table, qis->filtered,
2019 | qis->fast, &(qe->condat), acc_credit, acl);
2020 | }
2021 |
2022 | /* drop the table */
2023 | /* try to do this, even if there is an SQL error */
2024 | sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
2025 | if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) {
2026 | sql_error = SQ_errno(sql_connection);
2027 | report_sql_error(&qe->condat, sql_connection, sql_command);
2028 | }
2029 |
2030 | /* Now disconnect (temporary tables get dropped automatically) */
2031 | SQ_close_connection(sql_connection);
2032 |
2033 | /* return appropriate value */
2034 | if (sql_error) {
2035 | return QI_SQLERR;
2036 | } else {
2037 | return QI_OK;
2038 | }
2039 | } /* QI_execute() */
2040 |
2041 |
2042 | /* instruction_free() */
2043 | /*++++++++++++++++++++++++++++++++++++++
2044 | Free the instruction.
2045 |
2046 | Query_instruction *qi query_instruction to be freed.
2047 |
2048 | More:
2049 | +html+ <PRE>
2050 | Authors:
2051 | ottrey
2052 | +html+ </PRE>
2053 | ++++++++++++++++++++++++++++++++++++++*/
2054 | static void instruction_free(Query_instruction *qi) {
2055 | if (qi != NULL) {
2056 | if (qi->query_str != NULL) {
2057 | UT_free(qi->query_str);
2058 | }
2059 | UT_free(qi);
2060 | }
2061 | } /* instruction_free() */
2062 |
2063 | /* QI_free() */
2064 | /*++++++++++++++++++++++++++++++++++++++
2065 | Free the query_instructions.
2066 |
2067 | Query_instructions *qis Query_instructions to be freed.
2068 |
2069 | More:
2070 | +html+ <PRE>
2071 | Authors:
2072 | ottrey, marek
2073 | +html+ </PRE>
2074 | ++++++++++++++++++++++++++++++++++++++*/
2075 | void QI_free(Query_instructions *qis) {
2076 | int i;
2077 |
2078 | for (i=0; qis->instruction[i] != NULL; i++) {
2079 | instruction_free(qis->instruction[i]);
2080 | }
2081 |
2082 | if (qis != NULL) {
2083 | UT_free(qis);
2084 | }
2085 |
2086 | } /* QI_free() */
2087 |
2088 | /*++++++++++++++++++++++++++++++++++++++
2089 | Determine if this query should be conducted or not.
2090 |
2091 | If it was an inverse query - if the attribute appears in the query command's bitmap.
2092 | If it was a lookup query - if the attribute appears in the object type bitmap or
2093 | disregard if there is no object_type bitmap (Ie object filter).
2094 |
2095 | mask_t bitmap The bitmap of attribute to be converted.
2096 |
2097 | const Query_command *qc The query_command that the instructions are created
2098 | from.
2099 |
2100 | const Query_t q The query being considered.
2101 | +html+ <PRE>
2102 | Authors:
2103 | ottrey,
2104 | marek.
2105 | +html+ </PRE>
2106 | ++++++++++++++++++++++++++++++++++++++*/
2107 | static int valid_query(const Query_command *qc, const Query_t q) {
2108 | int result=0;
2109 |
2110 | if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
2111 | if (q.query != NULL) {
2112 | switch (q.querytype) {
2113 | case Q_INVERSE:
2114 | if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
2115 | result = 1;
2116 | }
2117 | break;
2118 |
2119 | case Q_LOOKUP:
2120 | if (q.class == C_ANY
2121 | || MA_isset(qc->object_type_bitmap, (unsigned) q.class)) {
2122 | result=1;
2123 | }
2124 | break;
2125 |
2126 | default:
2127 | /* XXX */fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
2128 | }
2129 | }
2130 | }
2131 |
2132 | return result;
2133 | } /* valid_query() */
2134 |
2135 | /* QI_new() */
2136 | /*++++++++++++++++++++++++++++++++++++++
2137 | Create a new set of query_instructions. Returns an allocated structure which
2138 | must be freed after use with QI_free().
2139 |
2140 | const Query_command *qc The query_command that the instructions are created
2141 | from.
2142 |
2143 | const Query_environ *qe The environmental variables that they query is being
2144 | performed under.
2145 |
2146 | +html+ <PRE>
2147 | Authors:
2148 | ottrey,
2149 | marek.
2150 | +html+ </PRE>
2151 | ++++++++++++++++++++++++++++++++++++++*/
2152 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
2153 | Query_instructions *qis=NULL;
2154 | Query_instruction *qi=NULL;
2155 | int i_no=0;
2156 | int i;
2157 | char *query_str;
2158 |
2159 | qis = (Query_instructions *)UT_calloc(1, sizeof(Query_instructions));
2160 |
2161 | qis->filtered = qc->filtered;
2162 | qis->fast = qc->fast;
2163 | qis->recursive = qc->recursive;
2164 | qis->qc = (qc);
2165 |
2166 |
2167 | for (i=0; Query[i].query != NULL; i++) {
2168 |
2169 | /* If a valid query. */
2170 | if ( valid_query(qc, Query[i]) == 1) {
2171 |
2172 | qi = (Query_instruction *)UT_calloc(1, sizeof(Query_instruction));
2173 |
2174 | qi->queryindex = i;
2175 |
2176 | /* SQL Query */
2177 | if ( Query[i].refer == R_SQL) {
2178 | qi->search_type = R_SQL;
2179 | query_str = create_query(Query[i], qc);
2180 |
2181 | if (query_str!= NULL) {
2182 | qi->query_str = query_str;
2183 | qis->instruction[i_no++] = qi;
2184 | }
2185 | }
2186 | /* Radix Query */
2187 | else if (Query[i].refer == R_RADIX) {
2188 |
2189 | /* no inverse queries should use the RADIX tree */
2190 | if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
2191 | continue;
2192 | }
2193 |
2194 | qi->search_type = R_RADIX;
2195 |
2196 | if (map_qc2rx(qi, qc) == 1) {
2197 | int j;
2198 | int found=0;
2199 |
2200 | /* check that there is no such query yet, for example if
2201 | more than one keytype (wk) matched */
2202 | for (j=0; j<i_no; j++) {
2203 | Query_instruction *qij = qis->instruction[j];
2204 |
2205 | if( qij->search_type == R_RADIX
2206 | && Query[qij->queryindex].attribute
2207 | == Query[qi ->queryindex].attribute) {
2208 |
2209 | found=1;
2210 | break;
2211 | }
2212 | }
2213 |
2214 | if ( found ) {
2215 | /* Discard the Query Instruction */
2216 | UT_free(qi);
2217 | }
2218 | else {
2219 | /* Add the query_instruction to the array */
2220 | qis->instruction[i_no++] = qi;
2221 | }
2222 | }
2223 | }
2224 | else {
2225 | /* ERROR: bad search_type */
2226 | die;
2227 | }
2228 | }
2229 | }
2230 | qis->instruction[i_no++] = NULL;
2231 |
2232 |
2233 | { /* tracing */
2234 | char *descrstr = QI_queries_to_string(qis);
2235 |
2236 | ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
2237 | UT_free( descrstr );
2238 | }
2239 |
2240 | return qis;
2241 |
2242 | } /* QI_new() */
2243 |
2244 |
2245 |
2246 |
2247 |
2248 | /*++++++++++++++++++++++++++++++++++++++
2249 |
2250 | char *QI_queries_to_string returns a list of descriptions for queries
2251 | that will be performed (debugging only).
2252 | Allocated text, must be freed after use.
2253 |
2254 | Query_instructions *qis query instructions structure
2255 |
2256 | Author:
2257 | marek.
2258 | ++++++++++++++++++++++++++++++++++++++*/
2259 |
2260 | char *QI_queries_to_string(Query_instructions *qis)
2261 | {
2262 | Query_instruction *qi;
2263 | int i;
2264 | char *resstr;
2265 |
2266 | resstr = (char *)UT_malloc(2);
2267 | strcpy(resstr, "{");
2268 |
2269 | for( i = 0; ( qi=qis->instruction[i] ) != NULL; i++ ) {
2270 | char *descr = Query[qi->queryindex].descr;
2271 | int oldres = strlen( resstr );
2272 |
2273 | resstr = (char *)UT_realloc(resstr, oldres+strlen(descr)+2);
2274 | strcat(resstr, descr);
2275 | strcat(resstr, ",");
2276 | }
2277 | if( i>0 ) {
2278 | /* cancel the last comma */
2279 | resstr[strlen(resstr)-1] = 0;
2280 | }
2281 |
2282 | resstr = (char *)UT_realloc(resstr, strlen(resstr) + 2);
2283 | strcat(resstr, "}");
2284 |
2285 | return resstr;
2286 | }