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