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