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) 86 | { 87 | SQ_connection_t *sql_connection = arg; 88 | ER_dbg_va(FAC_QI, ASP_QI_WATCH, 89 | "rtc: killing SQL connection %d", (sql_connection)->thread_id); 90 | /* abort the running query */ 91 | SQ_abort_query(sql_connection); 92 | 93 | return NULL; 94 | } 95 | 96 | 97 | 98 | /*++++++++++++++++++++++++++++++++++++++ 99 | wrapper around sq_execute_query: starts a query 100 | in a separate thread and starts the socket watchdog to cancel the query 101 | if the socket is closed. If the socket has problems already (its 102 | reason-to-close flag is set) no query is attempted. 103 | 104 | The execution of the query or watchdog is not guaranteed at all! 105 | 106 | int sql_execute_watched Returns the return code of SQ_execute_query, 107 | Returns 0 for cancelled queries. 108 | 109 | sk_conn_st *condat connection to watch 110 | 111 | SQ_connection_t **sql_connection sql connection 112 | 113 | const char *query sql query to execute 114 | 115 | SQ_result_set_t **result_ptr storage for the query result structure 116 | (passed to SQ_execute_query). Must either 117 | be NULL, or the pointer it points to must 118 | be NULL - in that case the result struct. 119 | will be allocated in SQ. 120 | 121 | Author: 122 | marek. 123 | ++++++++++++++++++++++++++++++++++++++*/ 124 | int sql_execute_watched(sk_conn_st *condat, SQ_connection_t **sql_connection, 125 | const char *query, SQ_result_set_t **result_ptr) 126 | { 127 | int retval = 0; /* return value of sq_execute_query */ 128 | SQ_connection_t *tempcon; 129 | 130 | /* assert that, if defined, result_ptr is initialised to NULL 131 | prior to calling this function */ 132 | if( result_ptr != NULL ) { 133 | dieif( *result_ptr != NULL ); 134 | } 135 | 136 | /* don't even try to perform the query/fire up watchdog 137 | if rtc is already set. Do this only if not set yet. */ 138 | if( condat->rtc == 0 ) { 139 | 140 | /* make clean */ 141 | SK_watch_setclear(condat); 142 | 143 | /* set watchdog to execute the abort function */ 144 | SK_watch_setexec(condat, qi_kill_body, *sql_connection); 145 | 146 | /* start the watchdog */ 147 | SK_watchstart(condat); 148 | 149 | /* start query. An error may be returned if the query is aborted */ 150 | retval = SQ_execute_query(*sql_connection, query, result_ptr); 151 | 152 | /* but short queries will complete before the watchdog kills the 153 | connection */ 154 | 155 | SK_watchstop(condat); 156 | 157 | 158 | /* if the watchdog triggered, then it is guaranteed that 159 | the kill_body function was invoked and therefore the sql-connection 160 | is now unusable... 161 | Close and reopen it for cleanup, use temporary connection 162 | to keep the login details */ 163 | if( condat->rtc != 0 ) { 164 | /* can't rely on the error code from mysql! 165 | */ 166 | 167 | /* one thing: this code must be entered ONLY if the kill_body 168 | thing was invoked by the watchdog. 169 | */ 170 | 171 | /* if result is defined, free it here before destroying the 172 | associated connection */ 173 | if( retval == 0 && result_ptr && *result_ptr ) { 174 | SQ_free_result( *result_ptr ); 175 | *result_ptr = NULL; 176 | } 177 | 178 | tempcon = SQ_duplicate_connection(*sql_connection); 179 | 180 | ER_dbg_va(FAC_QI, ASP_QI_WATCH, 181 | "rtc: closing SQL thread %d", (*sql_connection)->thread_id); 182 | SQ_close_connection(*sql_connection); 183 | 184 | *sql_connection = tempcon; 185 | ER_dbg_va(FAC_QI, ASP_QI_WATCH, 186 | "rtc: reopened as thread %d", (*sql_connection)->thread_id); 187 | 188 | /* make it look as if there was no error and 189 | the result is empty */ 190 | retval = 0; 191 | } /* if watchdog set rtc */ 192 | 193 | } /* if rtc not set before */ 194 | 195 | return retval; 196 | } 197 | 198 | /* create_name_query() */ 199 | /*++++++++++++++++++++++++++++++++++++++ 200 | Create an sql query for the names table. 201 | 202 | char *query_str 203 | 204 | const char *sql_query 205 | 206 | const char *keys 207 | 208 | More: 209 | +html+ <PRE> 210 | Authors: 211 | ottrey 212 | +html+ </PRE> 213 | ++++++++++++++++++++++++++++++++++++++*/ 214 | static void create_name_query(char *query_str, const char *sql_query, const char *keys) { 215 | int i; 216 | /* Allocate stuff - use dynamic strings (initialised to some length) */ 217 | GString *from_clause = g_string_sized_new(STR_L); 218 | GString *where_clause = g_string_sized_new(STR_L); 219 | gchar **words = g_strsplit(keys, " ", 0); 220 | 221 | /* double quotes " are used in queries to allow querying for 222 | names like O'Hara */ 223 | 224 | g_string_sprintfa(from_clause, "names N%.2d", 0); 225 | g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]); 226 | 227 | for (i=1; words[i] != NULL; i++) { 228 | g_string_sprintfa(from_clause, ", names N%.2d", i); 229 | g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i); 230 | } 231 | 232 | sprintf(query_str, sql_query, from_clause->str, where_clause->str); 233 | 234 | /* Free up stuff */ 235 | g_strfreev(words); 236 | g_string_free(where_clause,/* CONSTCOND */ TRUE); 237 | g_string_free(from_clause, /* CONSTCOND */ TRUE); 238 | 239 | } /* create_name_query() */ 240 | 241 | 242 | /*++++++++++++++++++++++++++++++++++++++ 243 | construct a range query for the as_block table 244 | (a query for an AS block object) given a string like: 245 | AS1 246 | AS1 - AS10 247 | AS1-AS10 248 | 249 | int create_asblock_query Returns 0 on success, -1 on failure 250 | (search term not an AS# nor range) 251 | 252 | char *query_str buffer for the final query (must be big enough) 253 | 254 | const char *sql_query rest of the sql query (with %d %d formats for 255 | AS numbers) 256 | 257 | const char *keys user-supplied search term. 258 | 259 | Author: 260 | marek 261 | ++++++++++++++++++++++++++++++++++++++*/ 262 | static int create_asblock_query(char *query_str, 263 | const char *sql_query, 264 | const char *keys) { 265 | char *keycopy = wr_string(keys); 266 | char *token, *cursor = keycopy; 267 | int asnums[2] = {0,0}; 268 | int index = 0; /* index into the asnums array */ 269 | 270 | 271 | while( (token = strsep( &cursor, "-" )) != NULL && index < 2) { 272 | /* discard the letters (or leading whitespace), take the number */ 273 | if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) { 274 | return -1; /* error */ 275 | } 276 | } 277 | /* if only beginning was supplied, copy it as end */ 278 | if( index == 1 ) { 279 | asnums[1] = asnums[0]; 280 | } 281 | 282 | /* now construct the query */ 283 | sprintf(query_str, sql_query, asnums[0], asnums[1]); 284 | 285 | wr_free(keycopy); 286 | return 0; 287 | } 288 | 289 | 290 | /*++++++++++++++++++++++++++++++++++++++ 291 | add_filter(): construct a query to limit the objects returned from the last 292 | table to predefined types. 293 | 294 | char *query_str buffer for the final query, containing the initial 295 | part of the query (must be big enough) 296 | 297 | const Query_command *qc query command structure with the bitmap of 298 | object types to be included. 299 | 300 | Author: 301 | ottrey. 302 | ++++++++++++++++++++++++++++++++++++++*/ 303 | static void add_filter(char *query_str, const Query_command *qc) { 304 | unsigned i; 305 | int qlen; 306 | char filter_atom[STR_M]; 307 | 308 | #if 0 309 | /* glib string manipulation - untested yet */ 310 | 311 | if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) { 312 | g_string_sprintfa(query_str, " AND ("); 313 | for (i=0; i < C_END; i++) { 314 | if (MA_isset(qc->object_type_bitmap, i)) { 315 | g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i)); 316 | } 317 | } 318 | g_string_truncate(query_str, query_str->len-3); 319 | g_string_append_c(query_str, ')'); 320 | } 321 | 322 | #else /* classic string operations */ 323 | 324 | /* add filters only if any bits are 0 (the number of 1's is < MAX_MAX */ 325 | if (MA_bitcount(qc->object_type_bitmap) != MASK_MAX) { 326 | strcat(query_str, " AND ("); 327 | for (i=0; i < C_END; i++) { 328 | if (MA_isset(qc->object_type_bitmap, i)) { 329 | strcpy(filter_atom, ""); 330 | sprintf(filter_atom, "i.object_type = %d OR ", i); 331 | /* XXX class codes should be used instead: 332 | DF_get_class_dbase_code(i)) 333 | but currently the tables contain values of enums 334 | (C_IN, etc) and not codes 335 | */ 336 | strcat(query_str, filter_atom); 337 | } 338 | } 339 | qlen = strlen(query_str); 340 | query_str[qlen-3] = ')'; 341 | query_str[qlen-2] = '\0'; 342 | query_str[qlen-1] = '\0'; 343 | } 344 | 345 | #endif 346 | 347 | } /* add_filter() */ 348 | 349 | /* create_query() */ 350 | /*++++++++++++++++++++++++++++++++++++++ 351 | Create an sql query from the query_command and the matching keytype and the 352 | selected inverse attributes. 353 | Note this clears the first inv_attribute it sees, so is called sequentially 354 | until there are no inv_attributes left. 355 | 356 | WK_Type keytype The matching keytype. 357 | 358 | const Query_command *qc The query command. 359 | 360 | mask_t *inv_attrs_bitmap The selected inverse attributes. 361 | 362 | More: 363 | +html+ <PRE> 364 | Authors: 365 | ottrey 366 | +html+ </PRE> 367 | 368 | ++++++++++++++++++++++++++++++++++++++*/ 369 | static char *create_query(const Query_t q, const Query_command *qc) { 370 | char *result=NULL; 371 | char result_buff[STR_XL]; 372 | Q_Type_t querytype; 373 | int addquery = 0; /* controls if the query should be added to the list */ 374 | 375 | if (MA_bitcount(qc->inv_attrs_bitmap) > 0) { 376 | querytype = Q_INVERSE; 377 | } 378 | else { 379 | querytype = Q_LOOKUP; 380 | } 381 | 382 | if ( (q.query != NULL) 383 | && (q.querytype == querytype) ) { 384 | 385 | /* addquery = 1; */ 386 | /* if it got here, it should be added, unless.(see asblock)*/ 387 | 388 | if (q.keytype == WK_NAME) { 389 | /* Name queries require special treatment. */ 390 | create_name_query(result_buff, q.query, qc->keys); 391 | addquery = 1; 392 | } 393 | else if( q.keytype == WK_IPADDRESS ) { /* ifaddr sql lookups */ 394 | ip_range_t myrang; 395 | unsigned begin, end; 396 | ip_keytype_t key_type; 397 | 398 | /* The only inverse query for IPADDRESS is nserver. */ 399 | /* We need to insure that we don't try to use the numeric values for this 400 | * query, because the address of the server is stored as a string, and 401 | * the SQL query is formatted appropriately. */ 402 | if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) { 403 | if(IP_rang_b2_space(&myrang) == IP_V4 ) { 404 | IP_rang_b2v4(&myrang, &begin, &end); 405 | if (querytype == Q_INVERSE) { 406 | /* for inverse queries, convert number to dotted-quad */ 407 | char buf[64]; 408 | const char *inet_ntop_ret; 409 | inet_ntop_ret = inet_ntop(AF_INET, &begin, buf, sizeof(buf)); 410 | dieif(inet_ntop_ret == NULL); 411 | sprintf(result_buff, q.query, buf); 412 | } else { 413 | /* otherwise, execute appropriate query on numeric values */ 414 | sprintf(result_buff, q.query, begin, end); 415 | } 416 | addquery = 1; 417 | } 418 | else { 419 | die; 420 | } 421 | } 422 | } 423 | else if( q.keytype == WK_ASRANGE ) { /* as_block range composition */ 424 | if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) { 425 | addquery = 0; /* ... unless it's not correct */ 426 | } 427 | else { 428 | addquery = 1; 429 | } 430 | } 431 | else { 432 | sprintf(result_buff, q.query, qc->keys); 433 | addquery = 1; 434 | } 435 | 436 | if (q.class == C_ANY && addquery == 1 ) { 437 | /* It is class type ANY so add the object filtering */ 438 | add_filter(result_buff, qc); 439 | } 440 | } 441 | 442 | if( addquery == 1 ) { 443 | dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK); 444 | strcpy(result, result_buff); 445 | return result; 446 | } 447 | else { 448 | return NULL; 449 | } 450 | } /* create_query() */ 451 | 452 | /* QI_fast_output() */ 453 | /*++++++++++++++++++++++++++++++++++++++ 454 | This is for the '-F' flag. 455 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute. 456 | Fast isn't fast anymore - it's just there for compatibility reasons. 457 | 458 | const char *str The object to be "fast output'ed". 459 | 460 | More: 461 | +html+ <PRE> 462 | Authors: 463 | ottrey, 464 | marek - glib strings + small changes 465 | +html+ </PRE> 466 | ++++++++++++++++++++++++++++++++++++++*/ 467 | char *QI_fast_output(const char *str) 468 | { 469 | int i,j; 470 | char *result; 471 | GString *result_buff = g_string_sized_new(STR_XL); 472 | gchar **lines = g_strsplit(str, "\n", 0); 473 | unsigned char *value, *colon; 474 | char *attr; 475 | 476 | g_string_assign(result_buff, ""); 477 | 478 | for (j=0; lines[j] != NULL; j++) { 479 | 480 | switch (lines[j][0]) { 481 | /* line continuation */ 482 | case ' ': 483 | case '\t': 484 | case '+': 485 | value = (unsigned char *) lines[j]+1; 486 | while(*value != '\0' && isspace(*value)) { 487 | value++; 488 | } 489 | g_string_append(result_buff, "\n+ "); 490 | g_string_append(result_buff, (char *)value); 491 | break; 492 | 493 | default: 494 | /* a line of the form "attribute: value" */ 495 | /* first: close the last line (if there was any, i.e. j>0) */ 496 | if( j > 0 ) { 497 | g_string_append_c(result_buff, '\n'); 498 | } 499 | 500 | /* get attribute name */ 501 | attr = lines[j]; 502 | colon = (unsigned char *) strchr(lines[j], ':'); 503 | /* if there's no colon for whatever reason, dump the object 504 | and report the condition */ 505 | if( colon == NULL ) { 506 | ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]); 507 | goto fast_output_cleanup; 508 | } 509 | *colon = '\0'; 510 | for(value = colon+1; *value != '\0' && isspace(*value) ; value++) { 511 | ; 512 | } 513 | 514 | if( (i = DF_attribute_name2type(attr)) == -1 ) { 515 | /* warning! error in the object format */ 516 | ER_perror(FAC_QI, QI_INVOBJ, " [%s]", lines[0]); 517 | goto fast_output_cleanup; 518 | 519 | } 520 | else { 521 | /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */ 522 | g_string_append_c(result_buff, '*'); 523 | g_string_append(result_buff, DF_get_attribute_code(i)); 524 | g_string_append(result_buff, ": "); 525 | g_string_append(result_buff, (char *)value); 526 | } 527 | } /* switch */ 528 | } /* for every line */ 529 | 530 | fast_output_cleanup: 531 | 532 | g_strfreev(lines); 533 | 534 | g_string_append_c(result_buff, '\n'); 535 | result = strdup(result_buff->str); 536 | dieif(result == NULL); 537 | 538 | g_string_free(result_buff,/* CONSTCOND */ TRUE); 539 | 540 | return result; 541 | } /* fast_output() */ 542 | 543 | /* filter() */ 544 | /*++++++++++++++++++++++++++++++++++++++ 545 | Basically it's for the '-K' flag for non-set (and non-radix) objects. 546 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute. 547 | 548 | This could be speed up if there were breaks out of the loops, once it matched something. 549 | 550 | const char *string The string to be filtered. 551 | 552 | More: 553 | +html+ <PRE> 554 | Authors: 555 | ottrey 556 | +html+ </PRE> 557 | 558 | ++++++++++++++++++++++++++++++++++++++*/ 559 | char *filter(const char *str) { 560 | int i,j, passed=0; 561 | char *result; 562 | GString *result_buff = g_string_sized_new(STR_XL); 563 | gchar **lines = g_strsplit(str, "\n", 0); 564 | char * const *filter_names; 565 | gboolean filtering_an_attribute = FALSE; 566 | 567 | filter_names = DF_get_filter_names(); 568 | 569 | g_string_assign(result_buff, ""); 570 | 571 | for (i=0; filter_names[i] != NULL; i++) { 572 | for (j=0; lines[j] != NULL; j++) { 573 | if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) { 574 | 575 | g_string_sprintfa(result_buff, "%s\n", lines[j]); 576 | passed++; 577 | 578 | /* CONSTCOND */ 579 | filtering_an_attribute = TRUE; 580 | } 581 | /* CONSTCOND */ 582 | else if (filtering_an_attribute == TRUE) { 583 | switch (lines[j][0]) { 584 | case ' ': 585 | case '\t': 586 | case '+': 587 | 588 | g_string_sprintfa(result_buff, "%s\n", lines[j]); 589 | 590 | break; 591 | 592 | default: 593 | filtering_an_attribute = FALSE; 594 | } 595 | } 596 | } 597 | } 598 | 599 | g_strfreev(lines); 600 | 601 | if(passed) { 602 | g_string_append(result_buff, "\n"); 603 | } 604 | result = strdup(result_buff->str); 605 | g_string_free(result_buff,/* CONSTCOND */ TRUE); 606 | 607 | return result; 608 | } /* filter() */ 609 | 610 | /* write_results() */ 611 | /*++++++++++++++++++++++++++++++++++++++ 612 | Write the results to the client socket. 613 | 614 | SQ_result_set_t *result The result set returned from the sql query. 615 | unsigned filtered if the objects should go through a filter (-K) 616 | sk_conn_st *condat Connection data for the client 617 | 618 | More: 619 | +html+ <PRE> 620 | Authors: 621 | ottrey - initial design 622 | marek - rewritten for accounting and cancellation. 623 | +html+ </PRE> 624 | 625 | ++++++++++++++++++++++++++++++++++++++*/ 626 | static int write_results(SQ_result_set_t *result, 627 | unsigned filtered, 628 | unsigned fast, 629 | sk_conn_st *condat, 630 | acc_st *acc_credit, 631 | acl_st *acl 632 | ) { 633 | SQ_row_t *row; 634 | char *str; 635 | char *filtrate; 636 | char *fasted; 637 | int retrieved_objects=0; 638 | char *objt; 639 | int type; 640 | 641 | /* Get all the results - one at a time */ 642 | if (result != NULL) { 643 | /* here we are making use of the mysql_store_result capability 644 | of interrupting the cycle of reading rows. mysql_use_result 645 | would not allow that, would have to be read until end */ 646 | 647 | while ( condat->rtc == 0 648 | && AC_credit_isdenied( acc_credit ) == 0 649 | && (row = SQ_row_next(result)) != NULL ) { 650 | 651 | if ( (str = SQ_get_column_string(result, row, 0)) == NULL 652 | || (objt = SQ_get_column_string(result, row, 3)) == NULL ) { 653 | /* handle it somehow ? */ 654 | die; 655 | } 656 | else { 657 | /* get + add object type */ 658 | type = atoi(objt); 659 | 660 | /* ASP_QI_LAST_DET */ 661 | ER_dbg_va(FAC_QI, ASP_QI_LAST_DET, 662 | "Retrieved serial id = %d , type = %s", atoi(str), objt); 663 | 664 | wr_free(str); 665 | wr_free(objt); 666 | } 667 | 668 | /* decrement credit for accounting purposes */ 669 | AC_count_object( acc_credit, acl, 670 | type == C_PN || type == C_RO ); /* is private? */ 671 | 672 | /* break the loop if the credit has just been exceeded and 673 | further results denied */ 674 | if( AC_credit_isdenied( acc_credit ) ) { 675 | continue; 676 | } 677 | 678 | if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 679 | else { 680 | 681 | /* The fast output stage */ 682 | if (fast == 1) { 683 | fasted = QI_fast_output(str); 684 | wr_free(str); 685 | str = fasted; 686 | } 687 | 688 | /* The filtering stage */ 689 | if (filtered == 0) { 690 | SK_cd_puts(condat, str); 691 | SK_cd_puts(condat, "\n"); 692 | } 693 | else { 694 | 695 | /* XXX accounting should be done AFTER filtering, not to count 696 | objects filtered out */ 697 | 698 | filtrate = filter(str); 699 | SK_cd_puts(condat, filtrate); 700 | wr_free(filtrate); 701 | } 702 | retrieved_objects++; 703 | } 704 | wr_free(str); 705 | } 706 | } 707 | 708 | return retrieved_objects; 709 | } /* write_results() */ 710 | 711 | /* generic SQL error message - it could be a configurable parameter, but 712 | it also shouldn't happen! */ 713 | static const char *sql_error_text = 714 | "% An internal database error has occurred.\n" 715 | "% It has been logged, and an adminstrator should look at it shorly.\n" 716 | "% Please try your query again.\n" 717 | "\n" 718 | "\n"; 719 | 720 | /* use a macro so we can get our file and line number */ 721 | #define report_sql_error(condat,sql_connection,sql_command) \ 722 | __report_sql_error((condat), (sql_connection), (sql_command), \ 723 | __FILE__,__LINE__,pthread_self()) 724 | 725 | /* report_sql_error() */ 726 | /*++++++++++++++++++++++++++++++++++++++ 727 | 728 | sk_conn_st *condat connection with user 729 | SQ_connection_t *sql_connection connection to MySQL 730 | const char *sql_command SQL command sent to MySQL 731 | 732 | ++++++++++++++++++++++++++++++++++++++*/ 733 | 734 | void 735 | __report_sql_error(sk_conn_st *condat, 736 | SQ_connection_t *sql_connection, 737 | const char *sql_command, 738 | const char *file, 739 | int line, 740 | pthread_t tid) 741 | { 742 | /* first, let user know what has happened */ 743 | SK_cd_puts(condat, sql_error_text); 744 | 745 | /* next, log this error */ 746 | ER_perror(FAC_QI, QI_SQLERR,"sql='%s' at %s:%d, tid:%d [%d] %s", 747 | sql_command, 748 | file, line, tid, 749 | SQ_errno(sql_connection), 750 | SQ_error(sql_connection)); 751 | } 752 | 753 | /* write_objects() */ 754 | /*++++++++++++++++++++++++++++++++++++++ 755 | 756 | SQ_connection_t *sql_connection The connection to the database. 757 | 758 | char *id_table The id of the temporary table (This is a result of the hacky 759 | way we've tried to get MySQL to do sub-selects.) 760 | 761 | sk_conn_st *condat Connection data for the client 762 | 763 | More: 764 | +html+ <PRE> 765 | Authors: 766 | ottrey, 767 | marek. 768 | +html+ </PRE> 769 | ++++++++++++++++++++++++++++++++++++++*/ 770 | static int 771 | write_objects(SQ_connection_t **sql_connection, 772 | char *id_table, 773 | unsigned int filtered, 774 | unsigned int fast, 775 | sk_conn_st *condat, 776 | acc_st *acc_credit, 777 | acl_st *acl 778 | ) 779 | { 780 | SQ_result_set_t *result = NULL; 781 | int retrieved_objects=0; 782 | char sql_command[STR_XL]; 783 | 784 | sprintf(sql_command, Q_OBJECTS, id_table); 785 | 786 | if (sql_execute_watched(condat, sql_connection, sql_command, &result) == -1) { 787 | report_sql_error(condat, *sql_connection, sql_command); 788 | return SQ_errno(*sql_connection); 789 | } 790 | 791 | /* Problem: if the query was aborted, the result structure does not 792 | refer to any existing connection anymore. So we check rtc here. 793 | */ 794 | 795 | if( condat->rtc == 0) { 796 | retrieved_objects = write_results(result, filtered, fast, condat, 797 | acc_credit, acl); 798 | SQ_free_result(result); 799 | } 800 | return 0; 801 | } /* write_objects() */ 802 | 803 | /* insert_radix_serials() */ 804 | /*++++++++++++++++++++++++++++++++++++++ 805 | Insert the radix serial numbers into a temporary table in the database. 806 | 807 | mask_t bitmap The bitmap of attribute to be converted. 808 | 809 | SQ_connection_t *sql_connection The connection to the database. 810 | 811 | char *id_table The id of the temporary table (This is a result of the hacky 812 | way we've tried to get MySQL to do sub-selects.) 813 | 814 | GList *datlist The list of data from the radix tree. 815 | 816 | More: 817 | +html+ <PRE> 818 | Authors: 819 | ottrey, 820 | marek 821 | +html+ </PRE> 822 | 823 | ++++++++++++++++++++++++++++++++++++++*/ 824 | static int insert_radix_serials(sk_conn_st *condat, 825 | SQ_connection_t *sql_connection, 826 | char *id_table, GList *datlist) { 827 | GList *qitem; 828 | char sql_command[STR_XL]; 829 | int serial; 830 | int sql_error; 831 | 832 | sql_error = 0; 833 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) { 834 | rx_datcpy_t *datcpy = qitem->data; 835 | 836 | serial = datcpy->leafcpy.data_key; 837 | 838 | /* don't bother to insert values into our temporary table */ 839 | /* if we've lost the client connection */ 840 | if ((condat->rtc == 0) && !sql_error) { 841 | sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial); 842 | if (SQ_execute_query(sql_connection, sql_command, NULL) == -1) { 843 | sql_error = SQ_errno(sql_connection); 844 | report_sql_error(condat, sql_connection, sql_command); 845 | } 846 | } 847 | 848 | wr_free(datcpy->leafcpy.data_ptr); 849 | } 850 | 851 | wr_clear_list( &datlist ); 852 | 853 | /* return error, if any */ 854 | return sql_error; 855 | 856 | } /* insert_radix_serials() */ 857 | 858 | 859 | /* write_radix_immediate() */ 860 | /*++++++++++++++++++++++++++++++++++++++ 861 | Display the immediate data carried with the objects returned by the 862 | radix tree. 863 | 864 | GList *datlist The linked list of dataleaf copies 865 | 866 | sk_conn_st *condat Connection data for the client 867 | 868 | acc_st *acc_credit Accounting struct 869 | 870 | More: 871 | +html+ <PRE> 872 | Authors: 873 | marek 874 | +html+ </PRE> 875 | 876 | Also free the list of answers. 877 | ++++++++++++++++++++++++++++++++++++++*/ 878 | static void write_radix_immediate(GList *datlist, 879 | sk_conn_st *condat, 880 | acc_st *acc_credit, 881 | acl_st *acl) 882 | { 883 | GList *qitem; 884 | 885 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) { 886 | rx_datcpy_t *datcpy = qitem->data; 887 | 888 | SK_cd_puts(condat, datcpy->leafcpy.data_ptr ); 889 | SK_cd_puts(condat, "\n"); 890 | 891 | wr_free(datcpy->leafcpy.data_ptr); 892 | 893 | AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ ); 894 | 895 | if(condat->rtc != 0) { 896 | break; 897 | } 898 | } 899 | 900 | wr_clear_list( &datlist ); 901 | } /* write_radix_immediate() */ 902 | 903 | 904 | /* map_qc2rx() */ 905 | /*++++++++++++++++++++++++++++++++++++++ 906 | The mapping between a query_command and a radix query. 907 | 908 | Query_instruction *qi The Query Instruction to be created from the mapping 909 | of the query command. 910 | 911 | const Query_command *qc The query command to be mapped. 912 | 913 | More: 914 | +html+ <PRE> 915 | Authors: 916 | ottrey, 917 | marek - simplified the logic, added stealth -S option 918 | +html+ </PRE> 919 | 920 | ++++++++++++++++++++++++++++++++++++++*/ 921 | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) { 922 | int result=1; 923 | int allflags = (qc->L == 1) + (qc->M == 1) + (qc->l == 1) 924 | + (qc->m == 1) + (qc->x == 1); 925 | 926 | qi->rx_keys = qc->keys; 927 | 928 | /* only one option can be active at a time */ 929 | 930 | if( allflags > 1 ) { 931 | /* user error (this should have been checked before) */ 932 | 933 | ER_dbg_va(FAC_QI, ASP_QI_SKIP, 934 | "ERROR in qc2rx mapping: bad combination of flags"); 935 | result = 0; 936 | } 937 | if( allflags == 0 ) { 938 | /* no options active - default search */ 939 | qi->rx_srch_mode = RX_SRCH_EXLESS; 940 | qi->rx_par_a = 0; 941 | } 942 | else if ( qc->L == 1 ) { 943 | qi->rx_srch_mode = RX_SRCH_LESS; 944 | qi->rx_par_a = RX_ALL_DEPTHS; 945 | } 946 | else if (qc->M == 1) { 947 | qi->rx_srch_mode = RX_SRCH_MORE; 948 | qi->rx_par_a = RX_ALL_DEPTHS; 949 | } 950 | else if (qc->l == 1) { 951 | qi->rx_srch_mode = RX_SRCH_LESS; 952 | qi->rx_par_a = 1; 953 | } 954 | else if (qc->m == 1) { 955 | qi->rx_srch_mode = RX_SRCH_MORE; 956 | qi->rx_par_a = 1; 957 | } 958 | else if (qc->x == 1) { 959 | qi->rx_srch_mode = RX_SRCH_EXACT; 960 | qi->rx_par_a = 0; 961 | } 962 | 963 | if( qi->rx_srch_mode == RX_SRCH_MORE && (qc->S == 1) ) { 964 | qi->rx_srch_mode = RX_SRCH_DBLS; 965 | } 966 | 967 | return result; 968 | 969 | } /* map_qc2rx() */ 970 | 971 | 972 | /* run_referral() */ 973 | /*++++++++++++++++++++++++++++++++++++++ 974 | 975 | invoked when no such domain found. Goes through the domain table 976 | and searches for shorter domains, then if it finds one with referral 977 | it performs it, otherwise it just returns nothing. 978 | 979 | to perform referral, it actually composes the referral query 980 | for a given host/port/type and calls the whois query function. 981 | 982 | Well, it returns nothing anyway (void). It just prints to the socket. 983 | 984 | char *ref_host referral server host name 985 | 986 | unsigned ref_port_int referral server port number 987 | 988 | char *qry query to be run 989 | 990 | Author: 991 | marek 992 | ++++++++++++++++++++++++++++++++++++++*/ 993 | void run_referral(Query_environ *qe, 994 | char *ref_host, 995 | unsigned ref_port_int, 996 | char *qry) 997 | { 998 | 999 | #if 1 /* switch off for testing */ 1000 | er_ret_t err; 1001 | char *rep; 1002 | 1003 | /* WH_sock(sock, host, port, query, maxlines, timeout)) */ 1004 | err= WH_cd_sock(&(qe->condat), ref_host, ref_port_int, qry, 1005 | ca_get_referralmaxlines, ca_get_referraltimeout 1006 | ); 1007 | 1008 | switch( err ) { 1009 | case SK_OK: 1010 | /* OK */ 1011 | break; 1012 | case SK_TIMEOUT: 1013 | /* Referral timeout */ 1014 | rep = ca_get_qi_ref_tmout ; 1015 | SK_cd_puts(&(qe->condat), rep); 1016 | wr_free(rep); 1017 | break; 1018 | 1019 | case SK_BADHOST: 1020 | /* Referral host not found */ 1021 | rep = ca_get_qi_ref_badhost ; 1022 | SK_cd_puts(&(qe->condat), rep); 1023 | wr_free(rep); 1024 | break; 1025 | 1026 | case SK_CONNECT: 1027 | /* Referral host not responding */ 1028 | rep = ca_get_qi_ref_hostnottresp ; 1029 | SK_cd_puts(&(qe->condat), rep); 1030 | wr_free(rep); 1031 | break; 1032 | 1033 | case SK_BIND: 1034 | case SK_SOCKET: 1035 | /* XXX internal server problem... */ 1036 | die; 1037 | 1038 | case WH_MAXLINES: 1039 | /* Referral reply line limit exceeded */ 1040 | rep = ca_get_qi_ref_overmaxlin ; 1041 | SK_cd_puts(&(qe->condat), rep); 1042 | wr_free(rep); 1043 | break; 1044 | 1045 | default: /* any other errors ? */ 1046 | die; 1047 | ; 1048 | } /*switch WH_sock */ 1049 | #endif 1050 | 1051 | }/*run_referral*/ 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | /*++++++++++++++++++++++++++++++++++++++ 1058 | 1059 | prepare and run the referral, displaying the results directly to the 1060 | client's connection. 1061 | 1062 | XXX still missing protection against a referral loop 1063 | XXX handling inverse flag not needed, to be removed 1064 | 1065 | char *domain domain being looked up 1066 | 1067 | Query_instructions *qis original query instructions structure 1068 | 1069 | Query_environ *qe original query environment structure 1070 | 1071 | Query_instruction *qi specific query instruction triggered 1072 | 1073 | SQ_result_set_t *result result of the lookup containing referral details 1074 | 1075 | SQ_row_t *row first row (should be only 1) of the result 1076 | this should contain columns: type, port, host 1077 | 1078 | char *sourcename name of the database "source" 1079 | 1080 | Author: 1081 | marek 1082 | ++++++++++++++++++++++++++++++++++++++*/ 1083 | static 1084 | void qi_prep_run_refer(char *domain, 1085 | Query_instructions *qis, 1086 | Query_environ *qe, 1087 | Query_instruction *qi, 1088 | SQ_result_set_t *result, SQ_row_t *row, 1089 | char *sourcename ) 1090 | { 1091 | int err; 1092 | long ref_type; 1093 | long ref_port; 1094 | char *ref_host; 1095 | char querystr[STR_L]; 1096 | 1097 | /* get values from SQL query */ 1098 | err = SQ_get_column_int(result, row, 0, &ref_type); 1099 | dieif(err); 1100 | err = SQ_get_column_int(result, row, 1, &ref_port); 1101 | dieif(err); 1102 | ref_host = SQ_get_column_string(result, row, 2); 1103 | dieif(ref_host == NULL); 1104 | 1105 | strcpy(querystr,""); 1106 | 1107 | /* put -r if the reftype is RIPE and -r or -i were used */ 1108 | if( (ref_type == RF_RIPE) 1109 | && ( Query[qi->queryindex].querytype == Q_INVERSE 1110 | || qis->recursive > 0 ) ) 1111 | { 1112 | strcpy(querystr,"-r "); 1113 | } 1114 | 1115 | /* prepend with -Vversion,IP for type CLIENTADDRESS */ 1116 | if( ref_type == RF_CLIENTADDRESS ) { 1117 | snprintf(querystr,sizeof(querystr),"-V%s,%s ",VERSION, qe->condat.ip); 1118 | } 1119 | 1120 | 1121 | /* now set the search term - set to the stripped down version 1122 | for inverse query, full-length otherwise */ 1123 | if( Query[qi->queryindex].querytype == Q_INVERSE ) { 1124 | strcat(querystr, domain); 1125 | } 1126 | else { 1127 | strcat(querystr, qis->qc->keys); 1128 | } 1129 | 1130 | { 1131 | /* the object is not from %s, 1132 | it comes from %s %d, use -R to see %s */ 1133 | char *rep = ca_get_qi_fmt_refheader ; 1134 | SK_cd_printf(&(qe->condat), rep, 1135 | sourcename, 1136 | ref_host, ref_port, 1137 | sourcename ); 1138 | wr_free(rep); 1139 | } 1140 | 1141 | /* do the referral */ 1142 | ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host); 1143 | 1144 | run_referral( qe, ref_host, ref_port, querystr); 1145 | 1146 | { /* End of referred query result */ 1147 | char *rep = ca_get_qi_reftrailer ; 1148 | SK_cd_puts(&(qe->condat), rep); 1149 | wr_free(rep); 1150 | } 1151 | 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, 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, 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, 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, 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, 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) { 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) { 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) { 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) { 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) 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 | }