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