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