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
- write_objects
- 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.75 $
3
4 Query instructions (qi). This is where the queries are executed.
5
6 Status: NOT REVUED, TESTED
7
8 ******************/ /******************
9 Filename : query_instructions.c
10 Authors : ottrey@ripe.net - framework and draft implementation
11 marek@ripe.net - cleaned and extended, added referral,
12 accounting support and watchdog cancellation.
13 OSs Tested : Solaris
14 ******************/ /******************
15 Copyright (c) 1999 RIPE NCC
16
17 All Rights Reserved
18
19 Permission to use, copy, modify, and distribute this software and its
20 documentation for any purpose and without fee is hereby granted,
21 provided that the above copyright notice appear in all copies and that
22 both that copyright notice and this permission notice appear in
23 supporting documentation, and that the name of the author not be
24 used in advertising or publicity pertaining to distribution of the
25 software without specific, written prior permission.
26
27 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
28 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
29 AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
30 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
31 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33 ***************************************/
34 #include <stdio.h>
35 #include <string.h>
36 #include <glib.h>
37
38 #include "which_keytypes.h"
39 #include "query_instructions.h"
40 #include "mysql_driver.h"
41 #include "rp.h"
42 #include "stubs.h"
43 #include "constants.h"
44 #include "memwrap.h"
45 #include "wh_queries.h"
46
47 #include "defs.h"
48
49 /*+ String sizes +*/
50 #define STR_S 63
51 #define STR_M 255
52 #define STR_L 1023
53 #define STR_XL 4095
54 #define STR_XXL 16383
55
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(char *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 g_string_sprintfa(from_clause, "names N%.2d", 0);
213 g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]);
214
215 for (i=1; words[i] != NULL; i++) {
216 g_string_sprintfa(from_clause, ", names N%.2d", i);
217 g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i);
218 }
219
220 sprintf(query_str, sql_query, from_clause->str, where_clause->str);
221
222 /* Free up stuff */
223 g_strfreev(words);
224 g_string_free(where_clause,/* CONSTCOND */ TRUE);
225 g_string_free(from_clause, /* CONSTCOND */ TRUE);
226
227 } /* create_name_query() */
228
229
230 /*++++++++++++++++++++++++++++++++++++++
231 construct a range query for the as_block table
232 (a query for an AS block object) given a string like:
233 AS1
234 AS1 - AS10
235 AS1-AS10
236
237 int create_asblock_query Returns 0 on success, -1 on failure
238 (search term not an AS# nor range)
239
240 char *query_str buffer for the final query (must be big enough)
241
242 const char *sql_query rest of the sql query (with %d %d formats for
243 AS numbers)
244
245 const char *keys user-supplied search term.
246
247 Author:
248 marek
249 ++++++++++++++++++++++++++++++++++++++*/
250 static int create_asblock_query(char *query_str,
/* [<][>][^][v][top][bottom][index][help] */
251 const char *sql_query,
252 const char *keys) {
253 char *keycopy = wr_string(keys);
254 char *token, *cursor = keycopy;
255 int asnums[2] = {0,0};
256 int index = 0; /* index into the asnums array */
257
258
259 while( (token = strsep( &cursor, "-" )) != NULL && index < 2) {
260 /* discard the letters (or leading whitespace), take the number */
261 if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) {
262 return -1; /* error */
263 }
264 }
265 /* if only beginning was supplied, copy it as end */
266 if( index == 1 ) {
267 asnums[1] = asnums[0];
268 }
269
270 /* now construct the query */
271 sprintf(query_str, sql_query, asnums[0], asnums[1]);
272
273 wr_free(keycopy);
274 return 0;
275 }
276
277
278 /*++++++++++++++++++++++++++++++++++++++
279 add_filter(): construct a query to limit the objects returned from the last
280 table to predefined types.
281
282 char *query_str buffer for the final query, containing the initial
283 part of the query (must be big enough)
284
285 const Query_command *qc query command structure with the bitmap of
286 object types to be included.
287
288 Author:
289 ottrey.
290 ++++++++++++++++++++++++++++++++++++++*/
291 static void add_filter(char *query_str, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
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 strcat(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 strcat(query_str, filter_atom);
325 }
326 }
327 qlen = strlen(query_str);
328 query_str[qlen-3] = ')';
329 query_str[qlen-2] = '\0';
330 query_str[qlen-1] = '\0';
331 }
332
333 #endif
334
335 } /* add_filter() */
336
337 /* create_query() */
338 /*++++++++++++++++++++++++++++++++++++++
339 Create an sql query from the query_command and the matching keytype and the
340 selected inverse attributes.
341 Note this clears the first inv_attribute it sees, so is called sequentially
342 until there are no inv_attributes left.
343
344 WK_Type keytype The matching keytype.
345
346 const Query_command *qc The query command.
347
348 mask_t *inv_attrs_bitmap The selected inverse attributes.
349
350 More:
351 +html+ <PRE>
352 Authors:
353 ottrey
354 +html+ </PRE>
355
356 ++++++++++++++++++++++++++++++++++++++*/
357 static char *create_query(const Query_t q, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
358 char *result=NULL;
359 char result_buff[STR_XL];
360 Q_Type_t querytype;
361 int addquery = 0; /* controls if the query should be added to the list */
362
363 if (MA_bitcount(qc->inv_attrs_bitmap) > 0) {
364 querytype = Q_INVERSE;
365 }
366 else {
367 querytype = Q_LOOKUP;
368 }
369
370 if ( (q.query != NULL)
371 && (q.querytype == querytype) ) {
372
373 /* addquery = 1; */
374 /* if it got here, it should be added, unless.(see asblock)*/
375
376 if (q.keytype == WK_NAME) {
377 /* Name queries require special treatment. */
378 create_name_query(result_buff, q.query, qc->keys);
379 addquery = 1;
380 }
381 else if( q.keytype == WK_IPADDRESS ) { /* ifaddr sql lookups */
382 ip_range_t myrang;
383 unsigned begin, end;
384 ip_keytype_t key_type;
385
386 if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) {
387 if(IP_rang_b2_space(&myrang) == IP_V4 ) {
388 IP_rang_b2v4(&myrang, &begin, &end);
389 sprintf(result_buff, q.query, begin, end);
390 addquery = 1;
391 }
392 else {
393 die;
394 }
395 }
396 }
397 else if( q.keytype == WK_ASRANGE ) { /* as_block range composition */
398 if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) {
399 addquery = 0; /* ... unless it's not correct */
400 }
401 else {
402 addquery = 1;
403 }
404 }
405 else {
406 sprintf(result_buff, q.query, qc->keys);
407 addquery = 1;
408 }
409
410 if (q.class == C_ANY && addquery == 1 ) {
411 /* It is class type ANY so add the object filtering */
412 add_filter(result_buff, qc);
413 }
414 }
415
416 if( addquery == 1 ) {
417 dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK);
418 strcpy(result, result_buff);
419 return result;
420 }
421 else {
422 return NULL;
423 }
424 } /* create_query() */
425
426 /* QI_fast_output() */
427 /*++++++++++++++++++++++++++++++++++++++
428 This is for the '-F' flag.
429 It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
430 Fast isn't fast anymore - it's just there for compatibility reasons.
431
432 const char *str The object to be "fast output'ed".
433
434 More:
435 +html+ <PRE>
436 Authors:
437 ottrey,
438 marek - glib strings + small changes
439 +html+ </PRE>
440 ++++++++++++++++++++++++++++++++++++++*/
441 char *QI_fast_output(const char *str)
/* [<][>][^][v][top][bottom][index][help] */
442 {
443 int i,j;
444 char *result;
445 GString *result_buff = g_string_sized_new(STR_XL);
446 gchar **lines = g_strsplit(str, "\n", 0);
447 unsigned char *value, *colon;
448 char *attr;
449
450 g_string_assign(result_buff, "");
451
452 for (j=0; lines[j] != NULL; j++) {
453
454 switch (lines[j][0]) {
455 /* line continuation */
456 case ' ':
457 case '\t':
458 case '+':
459 value = (unsigned char *) lines[j]+1;
460 while(*value != '\0' && isspace(*value)) {
461 value++;
462 }
463 g_string_append(result_buff, "\n+ ");
464 g_string_append(result_buff, (char *)value);
465 break;
466
467 default:
468 /* a line of the form "attribute: value" */
469 /* first: close the last line (if there was any, i.e. j>0) */
470 if( j > 0 ) {
471 g_string_append_c(result_buff, '\n');
472 }
473
474 /* get attribute name */
475 attr = lines[j];
476 colon = (unsigned char *) strchr(lines[j], ':');
477 /* if there's no colon for whatever reason, dump the object
478 and report the condition */
479 if( colon == NULL ) {
480 ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
481 goto fast_output_cleanup;
482 }
483 *colon = '\0';
484 for(value = colon+1; *value != '\0' && isspace(*value) ; value++) {
485 ;
486 }
487
488 if( (i = DF_attribute_name2type(attr)) == -1 ) {
489 /* warning! error in the object format */
490 ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]);
491 goto fast_output_cleanup;
492
493 }
494 else {
495 /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */
496 g_string_append_c(result_buff, '*');
497 g_string_append(result_buff, DF_get_attribute_code(i));
498 g_string_append(result_buff, ": ");
499 g_string_append(result_buff, (char *)value);
500 }
501 } /* switch */
502 } /* for every line */
503
504 fast_output_cleanup:
505
506 g_strfreev(lines);
507
508 g_string_append_c(result_buff, '\n');
509 result = strdup(result_buff->str);
510 dieif(result == NULL);
511
512 g_string_free(result_buff,/* CONSTCOND */ TRUE);
513
514 return result;
515 } /* fast_output() */
516
517 /* filter() */
518 /*++++++++++++++++++++++++++++++++++++++
519 Basically it's for the '-K' flag for non-set (and non-radix) objects.
520 It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute.
521
522 This could be speed up if there were breaks out of the loops, once it matched something.
523
524 const char *string The string to be filtered.
525
526 More:
527 +html+ <PRE>
528 Authors:
529 ottrey
530 +html+ </PRE>
531
532 ++++++++++++++++++++++++++++++++++++++*/
533 char *filter(const char *str) {
/* [<][>][^][v][top][bottom][index][help] */
534 int i,j, passed=0;
535 char *result;
536 GString *result_buff = g_string_sized_new(STR_XL);
537 gchar **lines = g_strsplit(str, "\n", 0);
538 char * const *filter_names;
539 gboolean filtering_an_attribute = FALSE;
540
541 filter_names = DF_get_filter_names();
542
543 g_string_assign(result_buff, "");
544
545 for (i=0; filter_names[i] != NULL; i++) {
546 for (j=0; lines[j] != NULL; j++) {
547 if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) {
548
549 g_string_sprintfa(result_buff, "%s\n", lines[j]);
550 passed++;
551
552 /* CONSTCOND */
553 filtering_an_attribute = TRUE;
554 }
555 /* CONSTCOND */
556 else if (filtering_an_attribute == TRUE) {
557 switch (lines[j][0]) {
558 case ' ':
559 case '\t':
560 case '+':
561
562 g_string_sprintfa(result_buff, "%s\n", lines[j]);
563
564 break;
565
566 default:
567 filtering_an_attribute = FALSE;
568 }
569 }
570 }
571 }
572
573 g_strfreev(lines);
574
575 if(passed) {
576 g_string_append(result_buff, "\n");
577 }
578 result = strdup(result_buff->str);
579 g_string_free(result_buff,/* CONSTCOND */ TRUE);
580
581 return result;
582 } /* filter() */
583
584 /* write_results() */
585 /*++++++++++++++++++++++++++++++++++++++
586 Write the results to the client socket.
587
588 SQ_result_set_t *result The result set returned from the sql query.
589 unsigned filtered if the objects should go through a filter (-K)
590 sk_conn_st *condat Connection data for the client
591
592 More:
593 +html+ <PRE>
594 Authors:
595 ottrey - initial design
596 marek - rewritten for accounting and cancellation.
597 +html+ </PRE>
598
599 ++++++++++++++++++++++++++++++++++++++*/
600 static int write_results(SQ_result_set_t *result,
/* [<][>][^][v][top][bottom][index][help] */
601 unsigned filtered,
602 unsigned fast,
603 sk_conn_st *condat,
604 acc_st *acc_credit,
605 acl_st *acl
606 ) {
607 SQ_row_t *row;
608 char *str;
609 char *filtrate;
610 char *fasted;
611 int retrieved_objects=0;
612 char *objt;
613 int type;
614
615 /* Get all the results - one at a time */
616 if (result != NULL) {
617 /* here we are making use of the mysql_store_result capability
618 of interrupting the cycle of reading rows. mysql_use_result
619 would not allow that, would have to be read until end */
620
621 while ( condat->rtc == 0
622 && AC_credit_isdenied( acc_credit ) == 0
623 && (row = SQ_row_next(result)) != NULL ) {
624
625 if ( (str = SQ_get_column_string(result, row, 0)) == NULL
626 || (objt = SQ_get_column_string(result, row, 3)) == NULL ) {
627 /* handle it somehow ? */
628 die;
629 }
630 else {
631 /* get + add object type */
632 type = atoi(objt);
633
634 /* ASP_QI_LAST_DET */
635 ER_dbg_va(FAC_QI, ASP_QI_LAST_DET,
636 "Retrieved serial id = %d , type = %s", atoi(str), objt);
637
638 wr_free(str);
639 wr_free(objt);
640 }
641
642 /* decrement credit for accounting purposes */
643 AC_count_object( acc_credit, acl,
644 type == C_PN || type == C_RO ); /* is private? */
645
646 /* break the loop if the credit has just been exceeded and
647 further results denied */
648 if( AC_credit_isdenied( acc_credit ) ) {
649 continue;
650 }
651
652 if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; }
653 else {
654
655 /* The fast output stage */
656 if (fast == 1) {
657 fasted = QI_fast_output(str);
658 wr_free(str);
659 str = fasted;
660 }
661
662 /* The filtering stage */
663 if (filtered == 0) {
664 SK_cd_puts(condat, str);
665 SK_cd_puts(condat, "\n");
666 }
667 else {
668
669 /* XXX accounting should be done AFTER filtering, not to count
670 objects filtered out */
671
672 filtrate = filter(str);
673 SK_cd_puts(condat, filtrate);
674 wr_free(filtrate);
675 }
676 retrieved_objects++;
677 }
678 wr_free(str);
679 }
680 }
681
682 return retrieved_objects;
683 } /* write_results() */
684
685
686 /* write_objects() */
687 /*++++++++++++++++++++++++++++++++++++++
688
689 SQ_connection_t *sql_connection The connection to the database.
690
691 char *id_table The id of the temporary table (This is a result of the hacky
692 way we've tried to get MySQL to do sub-selects.)
693
694 sk_conn_st *condat Connection data for the client
695
696 More:
697 +html+ <PRE>
698 Authors:
699 ottrey,
700 marek.
701 +html+ </PRE>
702 ++++++++++++++++++++++++++++++++++++++*/
703 static void write_objects(SQ_connection_t **sql_connection,
/* [<][>][^][v][top][bottom][index][help] */
704 char *id_table,
705 unsigned int filtered,
706 unsigned int fast,
707 sk_conn_st *condat,
708 acc_st *acc_credit,
709 acl_st *acl
710 )
711 {
712 SQ_result_set_t *result = NULL;
713 int retrieved_objects=0;
714 char sql_command[STR_XL];
715
716 sprintf(sql_command, Q_OBJECTS, id_table);
717
718 dieif(sql_execute_watched(condat, sql_connection, sql_command, &result) == -1 );
719
720 /* Problem: if the query was aborted, the result structure does not
721 refer to any existing connection anymore. So we check rtc here.
722 */
723
724 if( condat->rtc == 0) {
725 retrieved_objects = write_results(result, filtered, fast, condat,
726 acc_credit, acl);
727 SQ_free_result(result);
728 }
729 } /* write_objects() */
730
731 /* insert_radix_serials() */
732 /*++++++++++++++++++++++++++++++++++++++
733 Insert the radix serial numbers into a temporary table in the database.
734
735 mask_t bitmap The bitmap of attribute to be converted.
736
737 SQ_connection_t *sql_connection The connection to the database.
738
739 char *id_table The id of the temporary table (This is a result of the hacky
740 way we've tried to get MySQL to do sub-selects.)
741
742 GList *datlist The list of data from the radix tree.
743
744 More:
745 +html+ <PRE>
746 Authors:
747 ottrey,
748 marek
749 +html+ </PRE>
750
751 ++++++++++++++++++++++++++++++++++++++*/
752 static void insert_radix_serials(sk_conn_st *condat,
/* [<][>][^][v][top][bottom][index][help] */
753 SQ_connection_t *sql_connection,
754 char *id_table, GList *datlist) {
755 GList *qitem;
756 char sql_command[STR_XL];
757 int serial;
758
759 for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
760 rx_datcpy_t *datcpy = qitem->data;
761
762 serial = datcpy->leafcpy.data_key;
763
764 sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial);
765 dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1);
766
767 wr_free(datcpy->leafcpy.data_ptr);
768
769 if(condat->rtc != 0) {
770 break;
771 }
772 }
773
774 wr_clear_list( &datlist );
775
776 } /* insert_radix_serials() */
777
778
779 /* write_radix_immediate() */
780 /*++++++++++++++++++++++++++++++++++++++
781 Display the immediate data carried with the objects returned by the
782 radix tree.
783
784 GList *datlist The linked list of dataleaf copies
785
786 sk_conn_st *condat Connection data for the client
787
788 acc_st *acc_credit Accounting struct
789
790 More:
791 +html+ <PRE>
792 Authors:
793 marek
794 +html+ </PRE>
795
796 Also free the list of answers.
797 ++++++++++++++++++++++++++++++++++++++*/
798 static void write_radix_immediate(GList *datlist,
/* [<][>][^][v][top][bottom][index][help] */
799 sk_conn_st *condat,
800 acc_st *acc_credit,
801 acl_st *acl)
802 {
803 GList *qitem;
804
805 for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) {
806 rx_datcpy_t *datcpy = qitem->data;
807
808 SK_cd_puts(condat, datcpy->leafcpy.data_ptr );
809 SK_cd_puts(condat, "\n");
810
811 wr_free(datcpy->leafcpy.data_ptr);
812
813 AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ );
814
815 if(condat->rtc != 0) {
816 break;
817 }
818 }
819
820 wr_clear_list( &datlist );
821 } /* write_radix_immediate() */
822
823
824 /* map_qc2rx() */
825 /*++++++++++++++++++++++++++++++++++++++
826 The mapping between a query_command and a radix query.
827
828 Query_instruction *qi The Query Instruction to be created from the mapping
829 of the query command.
830
831 const Query_command *qc The query command to be mapped.
832
833 More:
834 +html+ <PRE>
835 Authors:
836 ottrey,
837 marek - simplified the logic, added stealth -S option
838 +html+ </PRE>
839
840 ++++++++++++++++++++++++++++++++++++++*/
841 static int map_qc2rx(Query_instruction *qi, const Query_command *qc) {
/* [<][>][^][v][top][bottom][index][help] */
842 int result=1;
843 int allflags = (qc->L == 1) + (qc->M == 1) + (qc->l == 1)
844 + (qc->m == 1) + (qc->x == 1);
845
846 qi->rx_keys = qc->keys;
847
848 /* only one option can be active at a time */
849
850 if( allflags > 1 ) {
851 /* user error (this should have been checked before) */
852
853 ER_dbg_va(FAC_QI, ASP_QI_SKIP,
854 "ERROR in qc2rx mapping: bad combination of flags");
855 result = 0;
856 }
857 if( allflags == 0 ) {
858 /* no options active - default search */
859 qi->rx_srch_mode = RX_SRCH_EXLESS;
860 qi->rx_par_a = 0;
861 }
862 else if ( qc->L == 1 ) {
863 qi->rx_srch_mode = RX_SRCH_LESS;
864 qi->rx_par_a = RX_ALL_DEPTHS;
865 }
866 else if (qc->M == 1) {
867 qi->rx_srch_mode = RX_SRCH_MORE;
868 qi->rx_par_a = RX_ALL_DEPTHS;
869 }
870 else if (qc->l == 1) {
871 qi->rx_srch_mode = RX_SRCH_LESS;
872 qi->rx_par_a = 1;
873 }
874 else if (qc->m == 1) {
875 qi->rx_srch_mode = RX_SRCH_MORE;
876 qi->rx_par_a = 1;
877 }
878 else if (qc->x == 1) {
879 qi->rx_srch_mode = RX_SRCH_EXACT;
880 qi->rx_par_a = 0;
881 }
882
883 if( qi->rx_srch_mode == RX_SRCH_MORE && (qc->S == 1) ) {
884 qi->rx_srch_mode = RX_SRCH_DBLS;
885 }
886
887 return result;
888
889 } /* map_qc2rx() */
890
891
892 /* run_referral() */
893 /*++++++++++++++++++++++++++++++++++++++
894
895 invoked when no such domain found. Goes through the domain table
896 and searches for shorter domains, then if it finds one with referral
897 it performs it, otherwise it just returns nothing.
898
899 to perform referral, it actually composes the referral query
900 for a given host/port/type and calls the whois query function.
901
902 Well, it returns nothing anyway (void). It just prints to the socket.
903
904 char *ref_host referral server host name
905
906 unsigned ref_port_int referral server port number
907
908 char *ref_type referral type name
909
910 char *qry query to be run
911
912 Author:
913 marek
914 ++++++++++++++++++++++++++++++++++++++*/
915 void run_referral(Query_environ *qe,
/* [<][>][^][v][top][bottom][index][help] */
916 char *ref_host,
917 unsigned ref_port_int,
918 char *ref_type,
919 char *qry)
920 {
921
922 #if 1 /* switch off for testing */
923 er_ret_t err;
924 char *rep;
925
926 /* WH_sock(sock, host, port, query, maxlines, timeout)) */
927 err= WH_cd_sock(&(qe->condat), ref_host, ref_port_int, qry,
928 ca_get_referralmaxlines, ca_get_referraltimeout
929 );
930
931 switch( err ) {
932 case SK_OK:
933 /* OK */
934 break;
935 case SK_TIMEOUT:
936 /* Referral timeout */
937 rep = ca_get_qi_ref_tmout ;
938 SK_cd_puts(&(qe->condat), rep);
939 wr_free(rep);
940 break;
941
942 case SK_BADHOST:
943 /* Referral host not found */
944 rep = ca_get_qi_ref_badhost ;
945 SK_cd_puts(&(qe->condat), rep);
946 wr_free(rep);
947 break;
948
949 case SK_CONNECT:
950 /* Referral host not responding */
951 rep = ca_get_qi_ref_hostnottresp ;
952 SK_cd_puts(&(qe->condat), rep);
953 wr_free(rep);
954 break;
955
956 case SK_BIND:
957 case SK_SOCKET:
958 /* XXX internal server problem... */
959 die;
960
961 case WH_MAXLINES:
962 /* Referral reply line limit exceeded */
963 rep = ca_get_qi_ref_overmaxlin ;
964 SK_cd_puts(&(qe->condat), rep);
965 wr_free(rep);
966 break;
967
968 default: /* any other errors ? */
969 die;
970 ;
971 } /*switch WH_sock */
972 #endif
973
974 }/*run_referral*/
975
976
977
978
979
980 /*++++++++++++++++++++++++++++++++++++++
981
982 prepare and run the referral, displaying the results directly to the
983 client's connection.
984
985 XXX still missing protection against a referral loop
986 XXX handling inverse flag not needed, to be removed
987
988 char *domain domain being looked up
989
990 Query_instructions *qis original query instructions structure
991
992 Query_environ *qe original query environment structure
993
994 Query_instruction *qi specific query instruction triggered
995
996 SQ_result_set_t *result result of the lookup containing referral details
997
998 SQ_row_t *row first row (should be only 1) of the result
999
1000 char *sourcename name of the database "source"
1001
1002 Author:
1003 marek
1004 ++++++++++++++++++++++++++++++++++++++*/
1005 static
1006 void qi_prep_run_refer(char *domain,
/* [<][>][^][v][top][bottom][index][help] */
1007 Query_instructions *qis,
1008 Query_environ *qe,
1009 Query_instruction *qi,
1010 SQ_result_set_t *result, SQ_row_t *row,
1011 char *sourcename )
1012 {
1013 char *ref_host = SQ_get_column_string(result, row, 2);
1014 char *ref_type = SQ_get_column_string(result, row, 0);
1015 char *ref_port = SQ_get_column_string(result, row, 1);
1016 unsigned ref_port_int;
1017 char querystr[STR_L];
1018
1019 /* get the integer value, it should be correct */
1020 if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) {
1021 die;
1022 }
1023
1024 strcpy(querystr,"");
1025
1026 /* put -r if the reftype is RIPE and -r or -i were used */
1027 if( strcmp(ref_type,"RIPE") == 0
1028 && ( Query[qi->queryindex].querytype == Q_INVERSE
1029 || qis->recursive > 0 ) ) {
1030 strcat(querystr," -r ");
1031 }
1032
1033 /* prepend with -Vversion,IP for type CLIENTADDRESS */
1034 if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) {
1035 char optv[STR_M];
1036
1037 snprintf(optv,STR_M," -V%s,%s ",VERSION, qe->condat.ip);
1038 strcat(querystr,optv);
1039 }
1040
1041 /* now set the search term - set to the stripped down version
1042 for inverse query, full-length otherwise */
1043 if( Query[qi->queryindex].querytype == Q_INVERSE ) {
1044 strcat(querystr, domain);
1045 }
1046 else {
1047 strcat(querystr, qis->qc->keys);
1048 }
1049
1050 {
1051 /* the object is not from %s,
1052 it comes from %s %d, use -R to see %s */
1053 char *rep = ca_get_qi_fmt_refheader ;
1054 SK_cd_printf(&(qe->condat), rep,
1055 sourcename,
1056 ref_host, ref_port_int,
1057 sourcename );
1058 wr_free(rep);
1059 }
1060
1061 /* do the referral */
1062 ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host);
1063
1064 run_referral( qe, ref_host, ref_port_int, ref_type, querystr);
1065
1066 { /* End of referred query result */
1067 char *rep = ca_get_qi_reftrailer ;
1068 SK_cd_puts(&(qe->condat), rep);
1069 wr_free(rep);
1070 }
1071
1072 wr_free(ref_host);
1073 wr_free(ref_type);
1074 wr_free(ref_port);
1075 }
1076
1077
1078 /*++++++++++++++++++++++++++++++++++++++
1079
1080 specific case of the object ID collection: the domains.
1081 Checks to see if the domain exists, and runs the referral if it is defined
1082 and the domain is missing.
1083
1084 Arguments:
1085
1086 char *sourcename name of the database "source"
1087
1088 SQ_connection_t *sql_connection sql connection dedicated to this thread
1089
1090 char *id_table name of the temporary table to be used
1091
1092 char *sub_table name of the temporary subtable
1093
1094 Query_instructions *qis original query instructions structure
1095
1096 Query_environ *qe original query environment structure
1097
1098 Query_instruction *qi specific query instruction triggered
1099
1100 acc_st *acc_credit credit for this client
1101
1102 Author:
1103 marek.
1104 ++++++++++++++++++++++++++++++++++++++*/
1105
1106 static int
1107 qi_collect_domain(char *sourcename,
/* [<][>][^][v][top][bottom][index][help] */
1108 SQ_connection_t *sql_connection,
1109 char *id_table,
1110 char *sub_table,
1111 Query_instructions *qis,
1112 Query_environ *qe,
1113 Query_instruction *qi,
1114 acc_st *acc_credit)
1115 {
1116 char *domain = qis->qc->keys;
1117 char *dot = domain;
1118 int subcount = 0;
1119 int foundcount = 0;
1120
1121 /* while nothing found and still some pieces of the name left */
1122 while( dot != NULL && subcount == 0 ) {
1123 int refcount = 0;
1124 SQ_row_t *row;
1125 SQ_result_set_t *result_referrals = NULL;
1126 char sql_command[STR_XL];
1127
1128 ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot);
1129
1130 /* domain lookup -- query into the _S table */
1131 sprintf(sql_command, "INSERT INTO %s SELECT object_id FROM domain WHERE domain = '%s'", sub_table, dot);
1132
1133 dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1);
1134 subcount = SQ_get_affected_rows(sql_connection);
1135
1136 if( subcount != 0 ) { /* domain exists in the database */
1137
1138 /* referral check. Always done except for -R and INVERSE queries */
1139 if( qis->qc->R == 0 &&
1140 Query[qi->queryindex].querytype != Q_INVERSE ) {
1141 sprintf(sql_command, "SELECT type, port, host FROM %s ID, refer WHERE ID.id = refer.object_id", sub_table);
1142 dieif( SQ_execute_query(sql_connection, sql_command,
1143 &result_referrals) == -1);
1144 refcount = SQ_num_rows(result_referrals);
1145 }
1146
1147 /* if referral allowed and defined, even if domain was found but
1148 contained referral - refer the query */
1149 if( refcount != 0 ) {
1150 /* get the referral parameters from the first row
1151 and perform it
1152 */
1153
1154 row = SQ_row_next(result_referrals);
1155 /* now: query for the original domain */
1156 qi_prep_run_refer(domain,
1157 qis, qe, qi, result_referrals, row, sourcename);
1158
1159 acc_credit->referrals -= 1;
1160 }
1161 else {
1162 /* domain found
1163 and (referral undefined or disabled by -R or inverse)
1164 two possible outcomes depending on whether 'dot' is:
1165 * the original search term -> pass what's in _S and quit
1166 * a 'stripped' domain name -> return no result and quit
1167 */
1168 if( dot == domain ) {
1169 sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s",
1170 id_table, sub_table);
1171 dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1);
1172 foundcount = SQ_get_affected_rows(sql_connection);
1173 }
1174 }
1175 dot = NULL; /* don't make another round */
1176 } /* a domain was found */
1177
1178 if( result_referrals != NULL ) {
1179 SQ_free_result(result_referrals);
1180 result_referrals = NULL;
1181 }
1182
1183 if( dot != NULL && (dot=index(dot,'.')) != NULL) {
1184 dot++;
1185 }
1186 }
1187
1188 return foundcount;
1189 } /* check_domain */
1190
1191
1192 /* add_ref_name */
1193 /*++++++++++++++++++++++++++++++++++++++
1194
1195 Creates a SQL query for a reference-by-name lookup. Uses standard name
1196 lookup query generator (create_name_query), so the order of the names
1197 doesn't matter.
1198
1199 SQ_connection_t *sql_connection sql connection dedicated to this thread
1200
1201 char *rectable table in which to look up
1202
1203 char *allnames all name words to be looked up, space delimited.
1204
1205 ++++++++++++++++++++++++++++++++++++++*/
1206 static
1207 void
1208 add_ref_name(SQ_connection_t *sql_connection,
/* [<][>][^][v][top][bottom][index][help] */
1209 char *rectable,
1210 char *allnames
1211 )
1212 {
1213 /* construct the query, allow zero-length list */
1214 if( strlen(allnames) > 0 ) {
1215 char final_query[STR_XL];
1216 char select_query[STR_XL];
1217
1218 create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s "
1219 "AND N00.object_type != 100 AND N00.thread_id = 0",
1220 allnames);
1221
1222 sprintf(final_query, "INSERT INTO %s %s",
1223 rectable,
1224 select_query);
1225
1226 dieif(SQ_execute_query(sql_connection, final_query, NULL) == -1 );
1227
1228 allnames[0]=0;
1229 }
1230 }/* add_ref_name */
1231
1232
1233
1234 /* qi_collect_ids */
1235 /*++++++++++++++++++++++++++++++++++++++
1236
1237 collects object ID's from all queries defined in the Query_instructions
1238 array. The results from RADIX trees are maintained in a linked list, the
1239 results from SQL lookups are kept in a temporary table. For domains,
1240 a specific function is invoked that may run the referral.
1241 Any sql lookup will be limited to the maximum number of objects allowed
1242 for the client (acl and credit are checked for this).
1243 The routine uses its own temporary _S table, destroyed at exit.
1244
1245 ca_dbSource_t *dbhdl source-specific identifier (defined in CA)
1246
1247 char *sourcename name of the database "source"
1248
1249 SQ_connection_t **sql_connection sql connection dedicated to this thread
1250 (replaced on cancel)
1251
1252 Query_instructions *qis original query instructions structure
1253
1254 Query_environ *qe original query environment structure
1255
1256 char *id_table the table to store the ID's found
1257
1258 GList **datlist the list to store the Radix leaves found
1259
1260 acc_st *acc_credit credit for this client
1261
1262 acl_st *acl acl for this client
1263
1264 ++++++++++++++++++++++++++++++++++++++*/
1265 static
1266 void
1267 qi_collect_ids(ca_dbSource_t *dbhdl,
/* [<][>][^][v][top][bottom][index][help] */
1268 char *sourcename,
1269 SQ_connection_t **sql_connection,
1270 Query_instructions *qis,
1271 Query_environ *qe,
1272 char *id_table,
1273 GList **datlist,
1274 acc_st *acc_credit,
1275 acl_st *acl
1276 )
1277 {
1278 Query_instruction **ins=NULL;
1279 int i;
1280 int count, errors=0;
1281 char sql_command[STR_XL];
1282 er_ret_t err;
1283 char sub_table[32];
1284 int limit ;
1285 /* a limit on the max number of objects to be returned
1286 from a single search. For some queries the object types
1287 are not known at this stage, so the limit must be
1288 the higher number of the two: private / public,
1289 or unlimited if any of them is 'unlimited'.
1290 */
1291 char limit_str[32];
1292
1293 if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) {
1294 strcpy(limit_str,"");
1295 } else {
1296 sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more
1297 so that the client hits
1298 the limit */
1299 }
1300
1301 sprintf(sub_table, "%s_S ", id_table);
1302
1303 /* see if there was a leftover table from a crashed session
1304 * (assume the ID cannot be currently in use)
1305 */
1306 sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table);
1307 dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1308
1309 /* create a table for special subqueries (domain only for now) */
1310 sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", sub_table);
1311 dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1312
1313 /* Iterate through query instructions */
1314 ins = qis->instruction;
1315 for (i=0; ins[i] != NULL && errors == 0; i++) {
1316 Query_instruction *qi = ins[i];
1317
1318 /* check if the client is still there */
1319 if( qe->condat.rtc ) {
1320 break;
1321 }
1322
1323 switch ( qi->search_type ) {
1324 case R_SQL:
1325 if ( qi->query_str != NULL ) {
1326
1327 /* handle special cases first */
1328 if( Query[qi->queryindex].class == C_DN
1329 && Query[qi->queryindex].querytype == Q_LOOKUP ) {
1330
1331 /* if any more cases than just domain appear, we will be
1332 cleaning the _S table from the previous query here
1333
1334 "DELETE FROM %s_S"
1335 */
1336
1337 count = qi_collect_domain(sourcename, *sql_connection, id_table,
1338 sub_table, qis, qe, qi, acc_credit);
1339 } /* if class DN and Straight lookup */
1340 else {
1341 /* any other class of query */
1342
1343 sprintf(sql_command, "INSERT INTO %s %s %s",
1344 id_table, qi->query_str, limit_str);
1345
1346 if(sql_execute_watched( &(qe->condat), sql_connection,
1347 sql_command, NULL) == -1 ) {
1348
1349 ER_perror(FAC_QI, QI_SQLERR," query='%s' [%d] %s",
1350 sql_command,
1351 SQ_errno(*sql_connection), SQ_error(*sql_connection));
1352 errors++;
1353 }
1354 count = SQ_get_affected_rows(*sql_connection);
1355 } /* not DN */
1356 } /* if SQL query not NULL */
1357
1358 ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1359 "%d entries added in %s query for %s",
1360 count, Query[qi->queryindex].descr, qis->qc->keys
1361 );
1362 break;
1363
1364 case R_RADIX:
1365
1366
1367 err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0,
1368 qi->rx_keys, dbhdl,
1369 Query[qi->queryindex].attribute,
1370 datlist, limit);
1371
1372
1373 if( NOERR(err)) {
1374 if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) {
1375 /* prevent unnecessary g_list_length call */
1376
1377 ER_dbg_va(FAC_QI, ASP_QI_COLL_DET,
1378 "%d entries after %s (mode %d par %d reg %d) query for %s",
1379 g_list_length(*datlist),
1380 Query[qi->queryindex].descr,
1381 qi->rx_srch_mode, qi->rx_par_a,
1382 dbhdl,
1383 qi->rx_keys);
1384 }
1385 }
1386 else {
1387 ER_inf_va(FAC_QI, ASP_QI_COLL_DET,
1388 "RP_asc_search returned %x ", err);
1389 }
1390 break;
1391
1392 default: die;
1393 } /* switch */
1394
1395 } /* for <every instruction> */
1396
1397 /* Now drop the _S table */
1398 sprintf(sql_command, "DROP TABLE %s", sub_table);
1399 dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1400
1401 }
1402
1403 /* qi_fetch_references */
1404 /*++++++++++++++++++++++++++++++++++++++
1405
1406 given the list of object ID's collects the references from these objects
1407 to person and role objects. Uses its own temporary SQL table (_R)
1408 and upon completion transfers the results from it to the main
1409 temporary table. Runs queries in watched mode, to be able to cancel them.
1410
1411 SQ_connection_t **sql_connection sql connection dedicated to this thread
1412 (replaced on cancel)
1413
1414 Query_environ *qe original query environment structure
1415
1416 char *id_table the table with the ID's found
1417
1418 acc_st *acc_credit credit for this client
1419
1420 acl_st *acl acl for this client
1421
1422 ++++++++++++++++++++++++++++++++++++++*/
1423 static
1424 void
1425 qi_fetch_references(SQ_connection_t **sql_connection,
/* [<][>][^][v][top][bottom][index][help] */
1426 Query_environ *qe,
1427 char *id_table,
1428 acc_st *acc_credit,
1429 acl_st *acl
1430 )
1431 {
1432 char rec_table[32];
1433 SQ_result_set_t *result = NULL;
1434 SQ_row_t *row;
1435 int thisid = 0;
1436 int oldid = 0;
1437 char allnames[STR_L];
1438 char sql_command[STR_XL];
1439
1440 sprintf(rec_table, "%s_R", id_table);
1441
1442 /* see if there was a leftover table from a crashed session
1443 * (assume the ID cannot be currently in use)
1444 */
1445 sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table);
1446 dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1447
1448 /* a temporary table for recursive data must be created, because
1449 a query using the same table as a source and target is illegal
1450 ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... )
1451 */
1452 sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", rec_table);
1453 dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1454
1455 /* find the contacts */
1456 sprintf(sql_command, Q_REC, rec_table, id_table, "author");
1457 dieif(sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) == -1 );
1458
1459 sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c");
1460 dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1461
1462 sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c" );
1463 dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1464
1465 sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c" );
1466 dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 );
1467
1468
1469 /* replace references to dummies by references by name */
1470 sprintf(sql_command,
1471 " SELECT id, name FROM %s IDS STRAIGHT_JOIN names "
1472 " WHERE IDS.id = names.object_id "
1473 " AND names.object_type = 100"
1474 " ORDER BY id",
1475 rec_table);
1476
1477 dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command,
1478 &result) == -1 );
1479 /* well, it might not be -1, but if the watchdog worked then the
1480 result is NULL */
1481 if( result != NULL ) {
1482
1483 allnames[0]=0;
1484 /* now go through the results and collect names */
1485 while ( (qe->condat.rtc == 0)
1486 && (row = SQ_row_next(result)) != NULL ) {
1487 char *id = SQ_get_column_string(result, row, 0);
1488 char *name = SQ_get_column_string(result, row, 1);
1489
1490 thisid = atoi(id);
1491
1492 /* when the id changes, the name is complete */
1493 if( thisid != oldid && oldid != 0 ) {
1494 add_ref_name( *sql_connection, rec_table, allnames);
1495 }
1496
1497 strcat(allnames, name);
1498 strcat(allnames, " ");
1499 oldid = thisid;
1500 wr_free(id);
1501 wr_free(name);
1502 }
1503 /* also do the last name */
1504 add_ref_name( *sql_connection, rec_table, allnames);
1505
1506 SQ_free_result(result); /* we can do it only because the watchdog */
1507 /* has not started between the check for non-NULL result and here */
1508 }
1509
1510 /* now copy things back to the main temporary table */
1511 sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s",
1512 id_table, rec_table);
1513 dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1514
1515 /* Now drop the IDS recursive table */
1516 sprintf(sql_command, "DROP TABLE %s", rec_table);
1517 dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 );
1518 }
1519 /* qi_fetch_references */
1520
1521
1522 /* QI_execute() */
1523 /*++++++++++++++++++++++++++++++++++++++
1524 Execute the query instructions. This is called for each source.
1525 This is linked into MySQL by the fact that MySQL doesn't have sub selects
1526 (yet). The queries are done in two stages. Make some temporary tables and
1527 insert into them. Then use them in the next select.
1528
1529
1530 ca_dbSource_t *dbhdl source-specific identifier (defined in CA)
1531
1532 Query_instructions *qis query instructions.
1533
1534 Query_environ *qe query environment.
1535
1536 acc_st *acc_credit object display credit
1537
1538 acl_st *acl copy of the original acl for this client
1539
1540 More:
1541 +html+ <PRE>
1542 Authors:
1543 ottrey - original version,
1544 marek - the rest.
1545 +html+ </PRE>
1546 ++++++++++++++++++++++++++++++++++++++*/
1547 er_ret_t QI_execute(ca_dbSource_t *dbhdl,
/* [<][>][^][v][top][bottom][index][help] */
1548 Query_instructions *qis,
1549 Query_environ *qe,
1550 acc_st *acc_credit,
1551 acl_st *acl
1552 )
1553 {
1554 /* those things must be freed after use! */
1555 char *dbhost = ca_get_srcdbmachine(dbhdl);
1556 char *dbname = ca_get_srcdbname(dbhdl);
1557 char *dbuser = ca_get_srcdbuser(dbhdl);
1558 char *dbpass = ca_get_srcdbpassword(dbhdl);
1559 char *srcnam = ca_get_srcname(dbhdl);
1560 unsigned dbport = ca_get_srcdbport(dbhdl);
1561 char id_table[STR_S];
1562 char sql_command[STR_XL];
1563 GList *datlist=NULL;
1564 SQ_connection_t *sql_connection=NULL;
1565
1566 sql_connection = SQ_get_connection( dbhost, dbport,
1567 dbname, dbuser, dbpass );
1568 if (sql_connection == NULL) {
1569 ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s",
1570 dbname, SQ_errno(sql_connection), SQ_error(sql_connection));
1571 return QI_CANTDB;
1572 }
1573
1574 sprintf(id_table, "ID_%ld_%d", mysql_thread_id(sql_connection),
1575 pthread_self());
1576
1577 /* see if there was a leftover table from a crashed session
1578 * (assume the ID cannot be currently in use)
1579 */
1580 sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table);
1581 dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1582
1583 /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */
1584 sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table);
1585 dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1586
1587 qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, id_table,
1588 &datlist, acc_credit, acl);
1589
1590 /* post-processing */
1591 if( qis->filtered == 0 ) {
1592 /* start the watchdog just to set the rtc flag */
1593 SK_watch_setclear(&(qe->condat));
1594 SK_watchstart(&(qe->condat));
1595
1596 /* add radix results (only if -K is not active) */
1597 insert_radix_serials(&(qe->condat), sql_connection, id_table, datlist);
1598
1599 SK_watchstop(&(qe->condat));
1600 }
1601
1602 /* fetch recursive objects (ac,tc,zc,ah) */
1603 if ( qis->recursive ) {
1604 qi_fetch_references( &sql_connection, qe, id_table, acc_credit, acl);
1605 } /* if recursive */
1606
1607 /* display */
1608 /* -K filtering:
1609 * right now only filtering, no expanding sets like write_set_objects()
1610 */
1611
1612 /* display the immediate data from the radix tree */
1613 if( qis->filtered == 1 ) {
1614 write_radix_immediate(datlist, &(qe->condat), acc_credit, acl );
1615 }
1616
1617 /* display objects from the IDs table */
1618 write_objects( &sql_connection, id_table, qis->filtered,
1619 qis->fast, &(qe->condat), acc_credit, acl);
1620
1621 /* Now drop the IDS table */
1622 sprintf(sql_command, "DROP TABLE %s", id_table);
1623 dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 );
1624 SQ_close_connection(sql_connection);
1625
1626 /* free allocated parameters */
1627 wr_free(dbhost);
1628 wr_free(dbname);
1629 wr_free(dbuser);
1630 wr_free(dbpass);
1631 wr_free(srcnam);
1632
1633 return QI_OK;
1634 } /* QI_execute() */
1635
1636
1637 /* instruction_free() */
1638 /*++++++++++++++++++++++++++++++++++++++
1639 Free the instruction.
1640
1641 Query_instruction *qi query_instruction to be freed.
1642
1643 More:
1644 +html+ <PRE>
1645 Authors:
1646 ottrey
1647 +html+ </PRE>
1648 ++++++++++++++++++++++++++++++++++++++*/
1649 static void instruction_free(Query_instruction *qi) {
/* [<][>][^][v][top][bottom][index][help] */
1650 if (qi != NULL) {
1651 if (qi->query_str != NULL) {
1652 wr_free(qi->query_str);
1653 }
1654 wr_free(qi);
1655 }
1656 } /* instruction_free() */
1657
1658 /* QI_free() */
1659 /*++++++++++++++++++++++++++++++++++++++
1660 Free the query_instructions.
1661
1662 Query_instructions *qis Query_instructions to be freed.
1663
1664 More:
1665 +html+ <PRE>
1666 Authors:
1667 ottrey, marek
1668 +html+ </PRE>
1669 ++++++++++++++++++++++++++++++++++++++*/
1670 void QI_free(Query_instructions *qis) {
/* [<][>][^][v][top][bottom][index][help] */
1671 int i;
1672
1673 for (i=0; qis->instruction[i] != NULL; i++) {
1674 instruction_free(qis->instruction[i]);
1675 }
1676
1677 if (qis != NULL) {
1678 wr_free(qis);
1679 }
1680
1681 } /* QI_free() */
1682
1683 /*++++++++++++++++++++++++++++++++++++++
1684 Determine if this query should be conducted or not.
1685
1686 If it was an inverse query - if the attribute appears in the query command's bitmap.
1687 If it was a lookup query - if the attribute appears in the object type bitmap or
1688 disregard if there is no object_type bitmap (Ie object filter).
1689
1690 mask_t bitmap The bitmap of attribute to be converted.
1691
1692 const Query_command *qc The query_command that the instructions are created
1693 from.
1694
1695 const Query_t q The query being considered.
1696 +html+ <PRE>
1697 Authors:
1698 ottrey,
1699 marek.
1700 +html+ </PRE>
1701 ++++++++++++++++++++++++++++++++++++++*/
1702 static int valid_query(const Query_command *qc, const Query_t q) {
/* [<][>][^][v][top][bottom][index][help] */
1703 int result=0;
1704
1705 if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) {
1706 if (q.query != NULL) {
1707 switch (q.querytype) {
1708 case Q_INVERSE:
1709 if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) {
1710 result = 1;
1711 }
1712 break;
1713
1714 case Q_LOOKUP:
1715 if (q.class == C_ANY
1716 || MA_isset(qc->object_type_bitmap, (unsigned) q.class)) {
1717 result=1;
1718 }
1719 break;
1720
1721 default:
1722 /* XXX */fprintf(stderr, "qi:valid_query() -> Bad querytype\n");
1723 }
1724 }
1725 }
1726
1727 return result;
1728 } /* valid_query() */
1729
1730 /* QI_new() */
1731 /*++++++++++++++++++++++++++++++++++++++
1732 Create a new set of query_instructions. Returns an allocated structure which
1733 must be freed after use with QI_free().
1734
1735 const Query_command *qc The query_command that the instructions are created
1736 from.
1737
1738 const Query_environ *qe The environmental variables that they query is being
1739 performed under.
1740
1741 +html+ <PRE>
1742 Authors:
1743 ottrey,
1744 marek.
1745 +html+ </PRE>
1746 ++++++++++++++++++++++++++++++++++++++*/
1747 Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) {
/* [<][>][^][v][top][bottom][index][help] */
1748 Query_instructions *qis=NULL;
1749 Query_instruction *qi=NULL;
1750 int i_no=0;
1751 int i;
1752 char *query_str;
1753
1754 dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK);
1755
1756 qis->filtered = qc->filtered;
1757 qis->fast = qc->fast;
1758 qis->recursive = qc->recursive;
1759 qis->qc = (qc);
1760
1761
1762 for (i=0; Query[i].query != NULL; i++) {
1763
1764 /* If a valid query. */
1765 if ( valid_query(qc, Query[i]) == 1) {
1766
1767 dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK);
1768
1769 qi->queryindex = i;
1770
1771 /* SQL Query */
1772 if ( Query[i].refer == R_SQL) {
1773 qi->search_type = R_SQL;
1774 query_str = create_query(Query[i], qc);
1775
1776 if (query_str!= NULL) {
1777 qi->query_str = query_str;
1778 qis->instruction[i_no++] = qi;
1779 }
1780 }
1781 /* Radix Query */
1782 else if (Query[i].refer == R_RADIX) {
1783 qi->search_type = R_RADIX;
1784
1785 if (map_qc2rx(qi, qc) == 1) {
1786 int j;
1787 int found=0;
1788
1789 /* check that there is no such query yet, for example if
1790 more than one keytype (wk) matched */
1791 for (j=0; j<i_no; j++) {
1792 Query_instruction *qij = qis->instruction[j];
1793
1794 if( qij->search_type == R_RADIX
1795 && Query[qij->queryindex].attribute
1796 == Query[qi ->queryindex].attribute) {
1797
1798 found=1;
1799 break;
1800 }
1801 }
1802
1803 if ( found ) {
1804 /* Discard the Query Instruction */
1805 wr_free(qi);
1806 }
1807 else {
1808 /* Add the query_instruction to the array */
1809 qis->instruction[i_no++] = qi;
1810 }
1811 }
1812 }
1813 else {
1814 /* ERROR: bad search_type */
1815 die;
1816 }
1817 }
1818 }
1819 qis->instruction[i_no++] = NULL;
1820
1821
1822 { /* tracing */
1823 char *descrstr = QI_queries_to_string(qis);
1824
1825 ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr );
1826 wr_free( descrstr );
1827 }
1828
1829 return qis;
1830
1831 } /* QI_new() */
1832
1833
1834
1835
1836
1837 /*++++++++++++++++++++++++++++++++++++++
1838
1839 char *QI_queries_to_string returns a list of descriptions for queries
1840 that will be performed (debugging only).
1841 Allocated text, must be freed after use.
1842
1843 Query_instructions *qis query instructions structure
1844
1845 Author:
1846 marek.
1847 ++++++++++++++++++++++++++++++++++++++*/
1848
1849 char *QI_queries_to_string(Query_instructions *qis)
/* [<][>][^][v][top][bottom][index][help] */
1850 {
1851 Query_instruction *qi;
1852 int i;
1853 char *resstr = NULL;
1854
1855 dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK);
1856 strcpy(resstr, "{");
1857
1858 for( i = 0; ( qi=qis->instruction[i] ) != NULL; i++ ) {
1859 char *descr = Query[qi->queryindex].descr;
1860 int oldres = strlen( resstr );
1861
1862 dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK);
1863 strcat(resstr, descr);
1864 strcat(resstr, ",");
1865 }
1866 if( i>0 ) {
1867 /* cancel the last comma */
1868 resstr[strlen(resstr)-1] = 0;
1869 }
1870
1871 dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 )
1872 != UT_OK);
1873 strcat(resstr, "}");
1874
1875 return resstr;
1876 }