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