1 | /*************************************** 2 | $Revision: 1.49 $ 3 | 4 | 5 | Sql module (sq). This is a mysql implementation of an sql module. 6 | 7 | Status: NOT REVUED, NOT TESTED 8 | 9 | Note: this code has been heavily coupled to MySQL, and may need to be changed 10 | (to improve performance) if a new RDBMS is used. 11 | 12 | ******************/ /****************** 13 | Filename : query_instructions.c 14 | Author : ottrey@ripe.net 15 | OSs Tested : Solaris 16 | Problems : Moderately linked to MySQL. Not sure which inverse 17 | attributes each option has. Would like to modify this 18 | after re-designing the objects module. 19 | Comments : Not sure about the different keytypes. 20 | ******************/ /****************** 21 | Copyright (c) 1999 RIPE NCC 22 | 23 | All Rights Reserved 24 | 25 | Permission to use, copy, modify, and distribute this software and its 26 | documentation for any purpose and without fee is hereby granted, 27 | provided that the above copyright notice appear in all copies and that 28 | both that copyright notice and this permission notice appear in 29 | supporting documentation, and that the name of the author not be 30 | used in advertising or publicity pertaining to distribution of the 31 | software without specific, written prior permission. 32 | 33 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 34 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL 35 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 36 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 37 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 38 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 39 | ***************************************/ 40 | #include <stdio.h> 41 | #include <string.h> 42 | #include <glib.h> 43 | 44 | #include "which_keytypes.h" 45 | #include "query_instructions.h" 46 | #include "mysql_driver.h" 47 | #include "rp.h" 48 | #include "stubs.h" 49 | #include "constants.h" 50 | #include "memwrap.h" 51 | #include "wh_queries.h" 52 | 53 | 54 | 55 | /*+ String sizes +*/ 56 | #define STR_S 63 57 | #define STR_M 255 58 | #define STR_L 1023 59 | #define STR_XL 4095 60 | #define STR_XXL 16383 61 | 62 | /* XXX this must be removed from here!!! a .h file must be 63 | generated from xml */ 64 | 65 | #include "defs.h" 66 | 67 | /* body of the query thread. 68 | 69 | takes a ptr to structure with all arguments. 70 | returns an int (result of sq_execute_query) cast to (void*) 71 | 72 | by marek 73 | */ 74 | static 75 | void *qi_kill_body(void *arg) 76 | { 77 | SQ_connection_t *sql_connection = arg; 78 | ER_dbg_va(FAC_QI, ASP_QI_LAST_DET, 79 | "rtc: killing SQL connection %d", (sql_connection)->thread_id); 80 | /* abort the running query */ 81 | SQ_abort_query(sql_connection); 82 | 83 | return NULL; 84 | } 85 | 86 | /* 87 | wrapper around sq_execute_query: starts a query 88 | in a separate thread and starts the socket watcher to cancel the query 89 | if the socket is closed. 90 | 91 | the execution of the query or watchdog is not guaranteed at all! 92 | 93 | if the rtc was set before, there will be even no attempt to start 94 | a query or watchdog. 95 | 96 | by marek 97 | */ 98 | int sql_execute_watched(sk_conn_st *condat, SQ_connection_t **sql_connection, 99 | const char *query, SQ_result_set_t **result_ptr) 100 | { 101 | int retval = 0; /* return value of sq_execute_query */ 102 | SQ_connection_t *tempcon; 103 | 104 | /* assert that, if defined, result_ptr is initialised to NULL 105 | prior to calling this function */ 106 | if( result_ptr != NULL ) { 107 | dieif( *result_ptr != NULL ); 108 | } 109 | 110 | /* don't even try to perform the query/fire up watchdog 111 | if rtc is already set. Do this only if not set yet. */ 112 | if( condat->rtc == 0 ) { 113 | 114 | /* make clean */ 115 | SK_watchclear(condat); 116 | 117 | /* set watchdog to execute the abort function */ 118 | SK_watchexec(condat, qi_kill_body, *sql_connection); 119 | 120 | /* start the watchdog */ 121 | SK_watchstart(condat); 122 | 123 | /* start query. An error may be returned if the query is aborted */ 124 | retval = SQ_execute_query(*sql_connection, query, result_ptr); 125 | 126 | /* but short queries will complete before the watchdog kills the 127 | connection */ 128 | 129 | SK_watchstop(condat); 130 | 131 | 132 | /* if the watchdog triggered, then it is guaranteed that 133 | the kill_body function was invoked and therefore the sql-connection 134 | is now unusable... 135 | Close and reopen it for cleanup, use temporary connection 136 | to keep the login details */ 137 | if( condat->rtc != 0 ) { 138 | /* can't rely on the error code from mysql! 139 | */ 140 | 141 | /* one thing: this code must be entered ONLY if the kill_body 142 | thing was invoked by the watchdog. 143 | */ 144 | 145 | /* if result is defined, free it here before destroying the 146 | associated connection */ 147 | if( retval == 0 && result_ptr && *result_ptr ) { 148 | SQ_free_result( *result_ptr ); 149 | *result_ptr = NULL; 150 | } 151 | 152 | tempcon = SQ_duplicate_connection(*sql_connection); 153 | 154 | ER_dbg_va(FAC_QI, ASP_QI_LAST_DET, 155 | "rtc: closing SQL thread %d", (*sql_connection)->thread_id); 156 | SQ_close_connection(*sql_connection); 157 | 158 | *sql_connection = tempcon; 159 | ER_dbg_va(FAC_QI, ASP_QI_LAST_DET, 160 | "rtc: reopened as thread %d", (*sql_connection)->thread_id); 161 | 162 | /* make it look as if there was no error and 163 | the result is empty */ 164 | retval = 0; 165 | } /* if watchdog set rtc */ 166 | 167 | } /* if rtc not set before */ 168 | 169 | return retval; 170 | } 171 | 172 | /* create_name_query() */ 173 | /*++++++++++++++++++++++++++++++++++++++ 174 | Create an sql query for the names table. 175 | 176 | char *query_str 177 | 178 | const char *sql_query 179 | 180 | const char *keys 181 | 182 | More: 183 | +html+ <PRE> 184 | Authors: 185 | ottrey 186 | +html+ </PRE><DL COMPACT> 187 | +html+ <DT>Online References: 188 | +html+ <DD><UL> 189 | +html+ </UL></DL> 190 | 191 | ++++++++++++++++++++++++++++++++++++++*/ 192 | static void create_name_query(char *query_str, const char *sql_query, const char *keys) { 193 | int i; 194 | /* Allocate stuff */ 195 | GString *from_clause = g_string_sized_new(STR_L); 196 | GString *where_clause = g_string_sized_new(STR_L); 197 | gchar **words = g_strsplit(keys, " ", 0); 198 | 199 | /* double quotes " are used in queries to allow querying for 200 | names like O'Hara */ 201 | 202 | g_string_sprintfa(from_clause, "names N%.2d", 0); 203 | g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]); 204 | 205 | for (i=1; words[i] != NULL; i++) { 206 | g_string_sprintfa(from_clause, ", names N%.2d", i); 207 | g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i); 208 | } 209 | 210 | sprintf(query_str, sql_query, from_clause->str, where_clause->str); 211 | 212 | /* Free up stuff */ 213 | g_strfreev(words); 214 | g_string_free(where_clause,/* CONSTCOND */ TRUE); 215 | g_string_free(from_clause, /* CONSTCOND */ TRUE); 216 | 217 | } /* create_name_query() */ 218 | 219 | /*+ create_asblock_query: 220 | 221 | given a string like: AS1 222 | AS1 - AS10 223 | AS1-AS10 224 | construct a range query for the as_block table 225 | */ 226 | static int create_asblock_query(char *query_str, 227 | const char *sql_query, 228 | const char *keys) { 229 | char *keycopy = wr_string(keys); 230 | char *token, *cursor = keycopy; 231 | int asnums[2] = {0,0}; 232 | int index = 0; /* index into the asnums array */ 233 | 234 | 235 | while( (token = strsep( &cursor, "-" )) != NULL && index < 2) { 236 | /* discard the letters (or leading whitespace), take the number */ 237 | if( sscanf(token, "%*[ AS]%d", &asnums[index++]) < 1 ) { 238 | return -1; /* error */ 239 | } 240 | } 241 | /* if only beginning was supplied, copy it as end */ 242 | if( index == 1 ) { 243 | asnums[1] = asnums[0]; 244 | } 245 | 246 | /* now construct the query */ 247 | sprintf(query_str, sql_query, asnums[0], asnums[1]); 248 | 249 | wr_free(keycopy); 250 | return 0; 251 | } 252 | 253 | static void add_filter(char *query_str, const Query_command *qc) { 254 | int i; 255 | int qlen; 256 | char filter_atom[STR_M]; 257 | 258 | /* 259 | if (MA_bitcount(qc->object_type_bitmap) > 0) { 260 | g_string_sprintfa(query_str, " AND ("); 261 | for (i=0; i < C_END; i++) { 262 | if (MA_isset(qc->object_type_bitmap, i)) { 263 | g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i)); 264 | } 265 | } 266 | g_string_truncate(query_str, query_str->len-3); 267 | g_string_append_c(query_str, ')'); 268 | } 269 | */ 270 | if (MA_bitcount(qc->object_type_bitmap) > 0) { 271 | strcat(query_str, " AND ("); 272 | for (i=0; i < C_END; i++) { 273 | if (MA_isset(qc->object_type_bitmap, i)) { 274 | strcpy(filter_atom, ""); 275 | sprintf(filter_atom, "i.object_type = %d OR ", i); 276 | /* XXX class codes should be used instead: 277 | DF_get_class_dbase_code(i)) 278 | but currently the tables contain values of enums 279 | (C_IN, etc) and not codes 280 | */ 281 | strcat(query_str, filter_atom); 282 | } 283 | } 284 | qlen = strlen(query_str); 285 | query_str[qlen-3] = ')'; 286 | query_str[qlen-2] = '\0'; 287 | query_str[qlen-1] = '\0'; 288 | } 289 | 290 | } /* add_filter() */ 291 | 292 | /* create_query() */ 293 | /*++++++++++++++++++++++++++++++++++++++ 294 | Create an sql query from the query_command and the matching keytype and the 295 | selected inverse attributes. 296 | Note this clears the first inv_attribute it sees, so is called sequentially 297 | until there are no inv_attributes left. 298 | 299 | WK_Type keytype The matching keytype. 300 | 301 | const Query_command *qc The query command. 302 | 303 | mask_t *inv_attrs_bitmap The selected inverse attributes. 304 | 305 | More: 306 | +html+ <PRE> 307 | Authors: 308 | ottrey 309 | +html+ </PRE><DL COMPACT> 310 | +html+ <DT>Online References: 311 | +html+ <DD><UL> 312 | +html+ </UL></DL> 313 | 314 | ++++++++++++++++++++++++++++++++++++++*/ 315 | static char *create_query(const Query_t q, const Query_command *qc) { 316 | char *result=NULL; 317 | char result_buff[STR_XL]; 318 | Q_Type_t querytype; 319 | int addquery = 0; /* controls if the query should be added to the list */ 320 | 321 | if (MA_bitcount(qc->inv_attrs_bitmap) > 0) { 322 | querytype = Q_INVERSE; 323 | } 324 | else { 325 | querytype = Q_LOOKUP; 326 | } 327 | 328 | if ( (q.query != NULL) 329 | && (q.querytype == querytype) ) { 330 | 331 | addquery = 1; /* if it got here, it should be added, unless.(see asblock)*/ 332 | 333 | if (q.keytype == WK_NAME) { 334 | /* Name queries require special treatment. */ 335 | create_name_query(result_buff, q.query, qc->keys); 336 | } 337 | else if( q.keytype == WK_IPADDRESS ) { /* ifaddr sql lookups */ 338 | ip_range_t myrang; 339 | unsigned begin, end; 340 | ip_keytype_t key_type; 341 | 342 | if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) { 343 | if(IP_rang_b2_space(&myrang) == IP_V4 ) { 344 | IP_rang_b2v4(&myrang, &begin, &end); 345 | sprintf(result_buff, q.query, begin, end); 346 | } 347 | else { 348 | die; 349 | } 350 | } 351 | } 352 | else if( q.keytype == WK_ASRANGE ) { /* as_block range composition */ 353 | if( create_asblock_query(result_buff, q.query, qc->keys) != 0 ) { 354 | addquery = 0; /* ... unless it's not correct */ 355 | } 356 | } 357 | else { 358 | sprintf(result_buff, q.query, qc->keys); 359 | } 360 | 361 | if (q.class == -1 && addquery == 1 ) { 362 | /* It is class type ANY so add the object filtering */ 363 | add_filter(result_buff, qc); 364 | } 365 | } 366 | 367 | if( addquery == 1 ) { 368 | dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK); 369 | strcpy(result, result_buff); 370 | return result; 371 | } 372 | else { 373 | return NULL; 374 | } 375 | } /* create_query() */ 376 | 377 | /* fast_output() */ 378 | /*++++++++++++++++++++++++++++++++++++++ 379 | This is for the '-F' flag. 380 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute. 381 | 382 | Fast isn't fast anymore - it's just there for compatibility reasons. 383 | This could be speed up if there were breaks out of the loops, once it matched something. 384 | (Wanna add a goto Marek? :-) ). 385 | 386 | const char *string The string to be "fast outputed". 387 | 388 | More: 389 | +html+ <PRE> 390 | Authors: 391 | ottrey 392 | +html+ </PRE><DL COMPACT> 393 | +html+ <DT>Online References: 394 | +html+ <DD><UL> 395 | +html+ </UL></DL> 396 | 397 | ++++++++++++++++++++++++++++++++++++++*/ 398 | 399 | char *fast_output(const char *str) 400 | { 401 | int i,j; 402 | char *result; 403 | char result_bit[STR_L]; 404 | char result_buff[STR_XL]; 405 | gchar **lines = g_strsplit(str, "\n", 0); 406 | char * const *attribute_names; 407 | gboolean filtering_an_attribute = FALSE; 408 | char *value; 409 | 410 | attribute_names = DF_get_attribute_names(); 411 | 412 | strcpy(result_buff, ""); 413 | for (j=0; lines[j] != NULL; j++) { 414 | for(i=0; attribute_names[i] != NULL; i++) { 415 | if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) { 416 | strcpy(result_bit, ""); 417 | /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */ 418 | value = strchr(lines[j], ':'); 419 | value++; 420 | /* Now get rid of whitespace. */ 421 | while (*value == ' ' || *value == '\t') { 422 | value++; 423 | } 424 | sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value); 425 | strcat(result_buff, result_bit); 426 | } 427 | /* CONSTCOND */ 428 | else if (filtering_an_attribute == TRUE) { 429 | switch (lines[j][0]) { 430 | case ' ': 431 | case '\t': 432 | case '+': 433 | strcpy(result_bit, ""); 434 | sprintf(result_bit, "%s\n", lines[j]); 435 | strcat(result_buff, result_bit); 436 | break; 437 | 438 | default: 439 | filtering_an_attribute = FALSE; 440 | } 441 | } 442 | } 443 | } 444 | 445 | 446 | dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK); 447 | 448 | strcpy(result, result_buff); 449 | 450 | return result; 451 | } /* fast_output() */ 452 | 453 | /* filter() */ 454 | /*++++++++++++++++++++++++++++++++++++++ 455 | Basically it's for the '-K' flag for non-set (and non-radix) objects. 456 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute. 457 | 458 | This could be speed up if there were breaks out of the loops, once it matched something. 459 | (Wanna add a goto Marek? :-) ). 460 | 461 | const char *string The string to be filtered. 462 | 463 | More: 464 | +html+ <PRE> 465 | Authors: 466 | ottrey 467 | +html+ </PRE><DL COMPACT> 468 | +html+ <DT>Online References: 469 | +html+ <DD><UL> 470 | +html+ </UL></DL> 471 | 472 | ++++++++++++++++++++++++++++++++++++++*/ 473 | char *filter(const char *str) { 474 | int i,j, passed=0; 475 | char *result; 476 | char result_bit[STR_L]; 477 | char result_buff[STR_XL]; 478 | gchar **lines = g_strsplit(str, "\n", 0); 479 | char * const *filter_names; 480 | gboolean filtering_an_attribute = FALSE; 481 | 482 | filter_names = DF_get_filter_names(); 483 | 484 | strcpy(result_buff, ""); 485 | for (i=0; filter_names[i] != NULL; i++) { 486 | for (j=0; lines[j] != NULL; j++) { 487 | if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) { 488 | strcpy(result_bit, ""); 489 | sprintf(result_bit, "%s\n", lines[j]); 490 | strcat(result_buff, result_bit); 491 | passed++; 492 | 493 | /* CONSTCOND */ 494 | filtering_an_attribute = TRUE; 495 | } 496 | /* CONSTCOND */ 497 | else if (filtering_an_attribute == TRUE) { 498 | switch (lines[j][0]) { 499 | case ' ': 500 | case '\t': 501 | case '+': 502 | strcpy(result_bit, ""); 503 | sprintf(result_bit, "%s\n", lines[j]); 504 | strcat(result_buff, result_bit); 505 | break; 506 | 507 | default: 508 | filtering_an_attribute = FALSE; 509 | } 510 | } 511 | } 512 | } 513 | 514 | if(passed) { 515 | strcat(result_buff, "\n"); 516 | } 517 | 518 | dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK); 519 | strcpy(result, result_buff); 520 | 521 | return result; 522 | } /* filter() */ 523 | 524 | /* write_results() */ 525 | /*++++++++++++++++++++++++++++++++++++++ 526 | Write the results to the client socket. 527 | 528 | SQ_result_set_t *result The result set returned from the sql query. 529 | unsigned filtered if the objects should go through a filter (-K) 530 | sk_conn_st *condat Connection data for the client 531 | 532 | XXX NB. this is very dependendant on what rows are returned in the result!!! 533 | 534 | More: 535 | +html+ <PRE> 536 | Authors: 537 | ottrey 538 | +html+ </PRE><DL COMPACT> 539 | +html+ <DT>Online References: 540 | +html+ <DD><UL> 541 | +html+ </UL></DL> 542 | 543 | ++++++++++++++++++++++++++++++++++++++*/ 544 | static int write_results(SQ_result_set_t *result, 545 | unsigned filtered, 546 | unsigned fast, 547 | sk_conn_st *condat, 548 | acc_st *acc_credit, 549 | acl_st *acl 550 | ) { 551 | SQ_row_t *row; 552 | char *str; 553 | char *filtrate; 554 | char *fasted; 555 | int retrieved_objects=0; 556 | char *objt; 557 | int type; 558 | 559 | /* Get all the results - one at a time */ 560 | if (result != NULL) { 561 | /* here we are making use of the mysql_store_result capability 562 | of interrupting the cycle of reading rows. mysql_use_result 563 | would not allow that, would have to be read until end */ 564 | 565 | while ( condat->rtc == 0 566 | && AC_credit_isdenied( acc_credit ) == 0 567 | && (row = SQ_row_next(result)) != NULL ) { 568 | 569 | if ( (str = SQ_get_column_string(result, row, 0)) == NULL 570 | || (objt = SQ_get_column_string(result, row, 3)) == NULL ) { 571 | /* handle it somehow ? */ 572 | die; 573 | } 574 | else { 575 | /* get + add object type */ 576 | type = atoi(objt); 577 | 578 | /* ASP_QI_LAST_DET */ 579 | ER_dbg_va(FAC_QI, ASP_QI_LAST_DET, 580 | "Retrieved serial id = %d , type = %s", atoi(str), objt); 581 | 582 | wr_free(str); 583 | wr_free(objt); 584 | } 585 | 586 | /* decrement credit for accounting purposes */ 587 | AC_count_object( acc_credit, acl, 588 | type == C_PN || type == C_RO ); /* is private? */ 589 | 590 | /* break the loop if the credit has just been exceeded and 591 | further results denied */ 592 | if( AC_credit_isdenied( acc_credit ) ) { 593 | continue; 594 | } 595 | 596 | if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 597 | else { 598 | 599 | /* The fast output stage */ 600 | if (fast == 1) { 601 | fasted = fast_output(str); 602 | wr_free(str); 603 | str = fasted; 604 | } 605 | 606 | /* The filtering stage */ 607 | if (filtered == 0) { 608 | SK_cd_puts(condat, str); 609 | SK_cd_puts(condat, "\n"); 610 | } 611 | else { 612 | 613 | /* XXX accounting should be done AFTER that, and not for objects 614 | filtered out */ 615 | 616 | filtrate = filter(str); 617 | SK_cd_puts(condat, filtrate); 618 | wr_free(filtrate); 619 | } 620 | retrieved_objects++; 621 | } 622 | wr_free(str); 623 | } 624 | } 625 | 626 | return retrieved_objects; 627 | } /* write_results() */ 628 | 629 | /* write_objects() */ 630 | /*++++++++++++++++++++++++++++++++++++++ 631 | This is linked into MySQL by the fact that MySQL doesn't have sub selects 632 | (yet). The queries are done in two stages. Make some temporary tables and 633 | insert into them. Then use them in the next select. 634 | 635 | SQ_connection_t *sql_connection The connection to the database. 636 | 637 | char *id_table The id of the temporary table (This is a result of the hacky 638 | way we've tried to get MySQL to do sub-selects.) 639 | 640 | sk_conn_st *condat Connection data for the client 641 | 642 | More: 643 | +html+ <PRE> 644 | Authors: 645 | ottrey 646 | +html+ </PRE><DL COMPACT> 647 | ++++++++++++++++++++++++++++++++++++++*/ 648 | static void write_objects(SQ_connection_t **sql_connection, 649 | char *id_table, 650 | unsigned int filtered, 651 | unsigned int fast, 652 | sk_conn_st *condat, 653 | acc_st *acc_credit, 654 | acl_st *acl 655 | ) 656 | { 657 | /* XXX This should really return a linked list of the objects */ 658 | 659 | SQ_result_set_t *result = NULL; 660 | int retrieved_objects=0; 661 | char sql_command[STR_XL]; 662 | #if 0 663 | SQ_result_set_t *order_res; 664 | SQ_row_t *order_row; 665 | 666 | SQ_execute_query( *sql_connection, "SELECT object_type FROM object_order ORDER BY order_code", &order_res ); 667 | while( (order_row = SQ_row_next(order_res)) != NULL ) { 668 | char *object_type = SQ_get_column_string(order_res, order_row, 0); 669 | sprintf(sql_command, Q_OBJECTS, id_table, object_type); 670 | 671 | exec/write 672 | } 673 | SQ_free_result(order_res); 674 | #endif 675 | 676 | sprintf(sql_command, Q_OBJECTS, id_table); 677 | 678 | dieif(sql_execute_watched(condat, sql_connection, sql_command, &result) == -1 ); 679 | 680 | /* Problem: if the query was aborted, the result structure does not 681 | refer to any existing connection anymore. So we check rtc here. 682 | */ 683 | 684 | if( condat->rtc == 0) { 685 | retrieved_objects = write_results(result, filtered, fast, condat, 686 | acc_credit, acl); 687 | SQ_free_result(result); 688 | } 689 | } /* write_objects() */ 690 | 691 | /* insert_radix_serials() */ 692 | /*++++++++++++++++++++++++++++++++++++++ 693 | Insert the radix serial numbers into a temporary table in the database. 694 | 695 | mask_t bitmap The bitmap of attribute to be converted. 696 | 697 | SQ_connection_t *sql_connection The connection to the database. 698 | 699 | char *id_table The id of the temporary table (This is a result of the hacky 700 | way we've tried to get MySQL to do sub-selects.) 701 | 702 | GList *datlist The list of data from the radix tree. 703 | 704 | XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty. :-( 705 | 706 | More: 707 | +html+ <PRE> 708 | Authors: 709 | ottrey 710 | +html+ </PRE><DL COMPACT> 711 | +html+ <DT>Online References: 712 | +html+ <DD><UL> 713 | <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A> 714 | +html+ </UL></DL> 715 | 716 | ++++++++++++++++++++++++++++++++++++++*/ 717 | static void insert_radix_serials(sk_conn_st *condat, 718 | SQ_connection_t *sql_connection, 719 | char *id_table, GList *datlist) { 720 | GList *qitem; 721 | char sql_command[STR_XL]; 722 | int serial; 723 | 724 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) { 725 | rx_datcpy_t *datcpy = qitem->data; 726 | 727 | serial = datcpy->leafcpy.data_key; 728 | 729 | sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial); 730 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1); 731 | 732 | wr_free(datcpy->leafcpy.data_ptr); 733 | 734 | if(condat->rtc != 0) { 735 | break; 736 | } 737 | } 738 | 739 | wr_clear_list( &datlist ); 740 | 741 | } /* insert_radix_serials() */ 742 | 743 | 744 | /* write_radix_immediate() */ 745 | /*++++++++++++++++++++++++++++++++++++++ 746 | Display the immediate data carried with the objects returned by the 747 | radix tree. 748 | 749 | GList *datlist The linked list of dataleaf copies 750 | sk_conn_st *condat Connection data for the client 751 | acc_st *acc_credit Accounting struct 752 | 753 | More: 754 | +html+ <PRE> 755 | Authors: 756 | marek 757 | +html+ </PRE><DL COMPACT> 758 | +html+ <DT>Online References: 759 | +html+ <DD><UL> 760 | +html+ </UL></DL> 761 | 762 | 763 | Also free the list of answers. 764 | */ 765 | static void write_radix_immediate(GList *datlist, 766 | sk_conn_st *condat, 767 | acc_st *acc_credit, 768 | acl_st *acl) 769 | { 770 | GList *qitem; 771 | 772 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) { 773 | rx_datcpy_t *datcpy = qitem->data; 774 | 775 | SK_cd_puts(condat, datcpy->leafcpy.data_ptr ); 776 | SK_cd_puts(condat, "\n"); 777 | 778 | wr_free(datcpy->leafcpy.data_ptr); 779 | 780 | AC_count_object(acc_credit, acl, 0 /* public object (private=0) */ ); 781 | 782 | if(condat->rtc != 0) { 783 | break; 784 | } 785 | } 786 | 787 | wr_clear_list( &datlist ); 788 | } /* write_radix_immediate() */ 789 | 790 | 791 | /* map_qc2rx() */ 792 | /*++++++++++++++++++++++++++++++++++++++ 793 | The mapping between a query_command and a radix query. 794 | 795 | Query_instruction *qi The Query Instruction to be created from the mapping 796 | of the query command. 797 | 798 | const Query_command *qc The query command to be mapped. 799 | 800 | More: 801 | +html+ <PRE> 802 | Authors: 803 | ottrey 804 | +html+ </PRE><DL COMPACT> 805 | +html+ <DT>Online References: 806 | +html+ <DD><UL> 807 | +html+ </UL></DL> 808 | 809 | ++++++++++++++++++++++++++++++++++++++*/ 810 | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) { 811 | int result=1; 812 | 813 | qi->rx_keys = qc->keys; 814 | 815 | if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 816 | qi->rx_srch_mode = RX_SRCH_EXLESS; 817 | qi->rx_par_a = 0; 818 | } 819 | else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 820 | qi->rx_srch_mode = RX_SRCH_LESS; 821 | qi->rx_par_a = RX_ALL_DEPTHS; 822 | } 823 | else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 824 | qi->rx_srch_mode = RX_SRCH_MORE; 825 | qi->rx_par_a = RX_ALL_DEPTHS; 826 | } 827 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) { 828 | qi->rx_srch_mode = RX_SRCH_LESS; 829 | qi->rx_par_a = 1; 830 | } 831 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) { 832 | qi->rx_srch_mode = RX_SRCH_MORE; 833 | qi->rx_par_a = 1; 834 | } 835 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) { 836 | qi->rx_srch_mode = RX_SRCH_EXACT; 837 | qi->rx_par_a = 0; 838 | } 839 | else { 840 | /* user error ( XXX : this should have been checked before) */ 841 | 842 | ER_dbg_va(FAC_QI, ASP_QI_SKIP, 843 | "ERROR in qc2rx mapping: bad combination of flags"); 844 | result = 0; 845 | } 846 | 847 | return result; 848 | 849 | } /* map_qc2rx() */ 850 | 851 | 852 | /* run_referral() */ 853 | /* 854 | invoked when no such domain found. Goes through the domain table 855 | and searches for shorter domains, then if it finds one with referral 856 | it performs it, otherwise it just returns nothing. 857 | 858 | to perform referral, it actually composes the referral query 859 | for a given host/port/type and calls the whois query function. 860 | 861 | Well, it returns nothing anyway (void). It just prints to the socket. 862 | 863 | */ 864 | void run_referral(char *sourcename, 865 | SQ_connection_t *sql_connection, 866 | Query_instructions *qis, 867 | Query_environ *qe, 868 | int qi_index, 869 | acc_st *acc_credit) { 870 | char *dot = qis->qc->keys; 871 | char querystr[STR_L]; 872 | SQ_row_t *row; 873 | SQ_result_set_t *result; 874 | char sql_command[STR_XL]; 875 | int stop_loop=0; 876 | char *ref_host; 877 | char *ref_type; 878 | char *ref_port; 879 | int ref_port_int; 880 | 881 | strcpy(querystr,""); 882 | 883 | while( !stop_loop && (dot=index(dot,'.')) != NULL ) { 884 | dot++; 885 | 886 | ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot); 887 | 888 | sprintf(sql_command, "SELECT domain.object_id, domain, type, port, host FROM domain, refer WHERE domain.object_id = refer.object_id AND domain = '%s'", dot); 889 | dieif( SQ_execute_query(sql_connection, sql_command, &result) == -1); 890 | 891 | switch( SQ_num_rows(result) ) { 892 | case 0: /* no such domain -> no action, will try next chunk */ 893 | break; 894 | 895 | case 1: /* check for referral host and perform query if present 896 | in any case end the loop */ 897 | stop_loop=1; 898 | assert( (row = SQ_row_next(result)) != NULL); 899 | 900 | ref_host = SQ_get_column_string(result, row, 4); 901 | 902 | ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host); 903 | 904 | if( ref_host != NULL && strlen(ref_host) > 0 ) { 905 | ref_type = SQ_get_column_string(result, row, 2); 906 | ref_port = SQ_get_column_string(result, row, 3); 907 | 908 | /* get the integer value, it should be correct */ 909 | if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) { 910 | die; 911 | } 912 | 913 | /* compose the query: */ 914 | 915 | /* put -r if the reftype is RIPE and -r or -i were used */ 916 | if( strcmp(ref_type,"RIPE") == 0 917 | && ( Query[qis->instruction[qi_index]->queryindex] 918 | .querytype == Q_INVERSE 919 | || qis->recursive > 0 ) ) { 920 | strcat(querystr," -r "); 921 | } 922 | 923 | /* prepend with -Vversion,IP for type CLIENTADDRESS */ 924 | if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) { 925 | char optv[STR_M]; 926 | 927 | snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip); 928 | strcat(querystr,optv); 929 | } 930 | 931 | /* now set the search term - set to the stripped down version 932 | for inverse query, full-length otherwise */ 933 | if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) { 934 | strcat(querystr,dot); 935 | } 936 | else { 937 | strcat(querystr,qis->qc->keys); 938 | } 939 | 940 | SK_cd_printf(&(qe->condat), 941 | "%% The object shown below is NOT in the %s database.\n" 942 | "%% It has been obtained by querying a remote server:\n" 943 | "%% (%s) at port %d.\n" 944 | "%% To see the object stored in the %s database\n" 945 | "%% use the -R flag in your query\n" 946 | "%%\n" 947 | "%%%%%% Start of referred query result\n", 948 | sourcename, 949 | ref_host, ref_port_int, 950 | sourcename ); 951 | SK_cd_puts(&(qe->condat), "\n"); 952 | 953 | /* WH_sock(sock, host, port, query, maxlines, timeout)) */ 954 | switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr, 25, 5) ) { 955 | case WH_TIMEOUT: 956 | SK_cd_puts(&(qe->condat),"referral timeout\n"); 957 | break; 958 | 959 | case WH_MAXLINES: 960 | SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n"); 961 | break; 962 | 963 | case WH_BADHOST: 964 | SK_cd_puts(&(qe->condat),"referral host not found\n"); 965 | break; 966 | 967 | case WH_CONNECT: 968 | SK_cd_puts(&(qe->condat),"referral host not responding\n"); 969 | break; 970 | 971 | case WH_BIND: 972 | case WH_SOCKET: 973 | /* XXX internal server problem... what to do - wait ? */ 974 | default: 975 | ; 976 | } /*switch WH_sock */ 977 | 978 | SK_cd_puts(&(qe->condat), "\n%%% End of referred query result\n"); 979 | acc_credit->referrals -= 1; 980 | } 981 | break; 982 | 983 | default: /* more than one domain in this file: something broken */ 984 | die; 985 | } 986 | SQ_free_result(result); 987 | } 988 | } /*run_referral*/ 989 | 990 | static 991 | void 992 | add_ref_name(SQ_connection_t *sql_connection, 993 | char *rectable, 994 | char *allnames 995 | ) 996 | { 997 | /* construct the query, allow zero-length list */ 998 | if( strlen(allnames) > 0 ) { 999 | char final_query[STR_XL]; 1000 | char select_query[STR_XL]; 1001 | 1002 | create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s " 1003 | "AND N00.object_type != 100 AND N00.thread_id = 0", 1004 | allnames); 1005 | 1006 | sprintf(final_query, "INSERT INTO %s %s", 1007 | rectable, 1008 | select_query); 1009 | 1010 | dieif(SQ_execute_query(sql_connection, final_query, NULL) == -1 ); 1011 | 1012 | allnames[0]=0; 1013 | } 1014 | } 1015 | 1016 | static 1017 | void 1018 | qi_collect_ids(ca_dbSource_t *dbhdl, 1019 | char *sourcename, 1020 | SQ_connection_t **sql_connection, 1021 | Query_instructions *qis, 1022 | Query_environ *qe, 1023 | char *id_table, 1024 | GList **datlist, 1025 | acc_st *acc_credit, 1026 | acl_st *acl 1027 | ) 1028 | { 1029 | Query_instruction **ins=NULL; 1030 | int i; 1031 | int count, errors=0; 1032 | char sql_command[STR_XL]; 1033 | er_ret_t err; 1034 | char sub_table[32]; 1035 | int limit ; 1036 | /* a limit on the max number of objects to be returned 1037 | from a single search. For some queries the object types 1038 | are not known at this stage, so the limit must be 1039 | the higher number of the two: private / public, 1040 | or unlimited if any of them is 'unlimited'. 1041 | */ 1042 | char limit_str[32]; 1043 | 1044 | if( (limit = AC_get_higher_limit(acc_credit,acl)) == -1) { 1045 | strcpy(limit_str,""); 1046 | } else { 1047 | sprintf(limit_str," LIMIT %d", limit+1); /* make sure we collect more 1048 | so that the client hits 1049 | the limit */ 1050 | } 1051 | 1052 | sprintf(sub_table, "%s_S ", id_table); 1053 | 1054 | /* see if there was a leftover table from a crashed session 1055 | * (assume the ID cannot be currently in use) 1056 | */ 1057 | sprintf(sql_command, "DROP TABLE IF EXISTS %s", sub_table); 1058 | dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ); 1059 | 1060 | /* create a table for special subqueries (domain only for now) */ 1061 | sprintf(sql_command, "CREATE TABLE %s ( id int ) TYPE=HEAP", sub_table); 1062 | dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ); 1063 | 1064 | /* Iterate through query instructions */ 1065 | ins = qis->instruction; 1066 | for (i=0; ins[i] != NULL && errors == 0; i++) { 1067 | Query_instruction *qi = ins[i]; 1068 | 1069 | /* check if the client is still there */ 1070 | if( qe->condat.rtc ) { 1071 | break; 1072 | } 1073 | 1074 | switch ( qi->search_type ) { 1075 | case R_SQL: 1076 | if ( qi->query_str != NULL ) { 1077 | 1078 | /* handle special cases first */ 1079 | if( Query[qi->queryindex].class == C_DN ) { 1080 | 1081 | /* XXX if any more cases than just domain appear, we will be 1082 | cleaning the _S table from the previous query here 1083 | 1084 | "DELETE FROM %s_S" 1085 | */ 1086 | 1087 | /* now query into the _S table */ 1088 | sprintf(sql_command, "INSERT INTO %s%s", sub_table, qi->query_str); 1089 | dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1); 1090 | 1091 | /* if any results - copy to the id's table. 1092 | Otherwise, run referral */ 1093 | count = SQ_get_affected_rows(*sql_connection); 1094 | 1095 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET, 1096 | "DN lookup for %s found %d entries", qis->qc->keys, count); 1097 | 1098 | if( count ) { 1099 | sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s", 1100 | id_table, sub_table); 1101 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1); 1102 | } 1103 | 1104 | if( count == 0 1105 | || Query[qi->queryindex].querytype == Q_INVERSE ) { 1106 | /* now: if the domain was not found, we run referral. 1107 | unless prohibited by a flag 1108 | 1109 | But for inverse queries we return the things that were 1110 | or were not found AND also do the referral unless prohibited. 1111 | */ 1112 | if (qis->qc->R == 0) { 1113 | run_referral(sourcename, *sql_connection, qis, qe, i, acc_credit); 1114 | } 1115 | } 1116 | 1117 | } /* if class DN */ 1118 | else { 1119 | /* any other class of query */ 1120 | 1121 | sprintf(sql_command, "INSERT INTO %s %s %s", 1122 | id_table, qi->query_str, limit_str); 1123 | 1124 | if(sql_execute_watched( &(qe->condat), sql_connection, 1125 | sql_command, NULL) == -1 ) { 1126 | 1127 | ER_perror(FAC_QI, QI_SQLERR," query='%s' [%d] %s", 1128 | sql_command, 1129 | SQ_errno(*sql_connection), SQ_error(*sql_connection)); 1130 | errors++; 1131 | } 1132 | count = SQ_get_affected_rows(*sql_connection); 1133 | } /* not DN */ 1134 | } /* if SQL query not NULL */ 1135 | 1136 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET, 1137 | "%d entries added in %s query for %s", 1138 | count, Query[qi->queryindex].descr, qis->qc->keys 1139 | ); 1140 | break; 1141 | 1142 | case R_RADIX: 1143 | 1144 | if( ! qis->qc->S ) /* XXX patch: use new search algorithm by default */ { 1145 | err = RP_new_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 1146 | qi->rx_keys, dbhdl, 1147 | Query[qi->queryindex].attribute, 1148 | datlist, limit); 1149 | 1150 | } 1151 | else { 1152 | err = RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, 1153 | qi->rx_keys, dbhdl, 1154 | Query[qi->queryindex].attribute, 1155 | datlist, limit); 1156 | } 1157 | 1158 | if( NOERR(err)) { 1159 | if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) { 1160 | /* prevent unnecessary g_list_length call */ 1161 | 1162 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET, 1163 | "%d entries after %s (mode %d par %d reg %d) query for %s", 1164 | g_list_length(*datlist), 1165 | Query[qi->queryindex].descr, 1166 | qi->rx_srch_mode, qi->rx_par_a, 1167 | dbhdl, 1168 | qi->rx_keys); 1169 | } 1170 | } 1171 | else { 1172 | ER_inf_va(FAC_QI, ASP_QI_COLL_DET, 1173 | "RP_asc_search returned %x ", err); 1174 | } 1175 | break; 1176 | 1177 | default: die; 1178 | } /* switch */ 1179 | 1180 | } /* for <every instruction> */ 1181 | 1182 | /* Now drop the _S table */ 1183 | sprintf(sql_command, "DROP TABLE %s", sub_table); 1184 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ); 1185 | 1186 | } 1187 | 1188 | static 1189 | void 1190 | qi_fetch_references(SQ_connection_t **sql_connection, 1191 | Query_environ *qe, 1192 | char *id_table, 1193 | acc_st *acc_credit, 1194 | acl_st *acl 1195 | ) 1196 | { 1197 | char rec_table[32]; 1198 | SQ_result_set_t *result = NULL; 1199 | SQ_row_t *row; 1200 | int thisid = 0; 1201 | int oldid = 0; 1202 | char allnames[STR_M]; 1203 | char sql_command[STR_XL]; 1204 | 1205 | sprintf(rec_table, "%s_R", id_table); 1206 | 1207 | /* see if there was a leftover table from a crashed session 1208 | * (assume the ID cannot be currently in use) 1209 | */ 1210 | sprintf(sql_command, "DROP TABLE IF EXISTS %s ", rec_table); 1211 | dieif( SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ); 1212 | 1213 | /* a temporary table for recursive data must be created, because 1214 | a query using the same table as a source and target is illegal 1215 | ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... ) 1216 | */ 1217 | sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", rec_table); 1218 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ); 1219 | 1220 | /* find the contacts */ 1221 | sprintf(sql_command, Q_REC, rec_table, id_table, "author"); 1222 | dieif(sql_execute_watched( &(qe->condat), sql_connection, sql_command, NULL) == -1 ); 1223 | 1224 | sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c"); 1225 | dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 ); 1226 | 1227 | sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c" ); 1228 | dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 ); 1229 | 1230 | sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c" ); 1231 | dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, NULL) == -1 ); 1232 | 1233 | 1234 | /* replace references to dummies by references by name */ 1235 | sprintf(sql_command, 1236 | " SELECT id, name FROM %s IDS STRAIGHT_JOIN names " 1237 | " WHERE IDS.id = names.object_id " 1238 | " AND names.object_type = 100" 1239 | " ORDER BY id", 1240 | rec_table); 1241 | 1242 | dieif(sql_execute_watched(&(qe->condat), sql_connection, sql_command, 1243 | &result) == -1 ); 1244 | /* well, it might not be -1, but if the watchdog worked then the 1245 | result is NULL */ 1246 | if( result != NULL ) { 1247 | 1248 | allnames[0]=0; 1249 | /* now go through the results and collect names */ 1250 | while ( (qe->condat.rtc == 0) 1251 | && (row = SQ_row_next(result)) != NULL ) { 1252 | char *id = SQ_get_column_string(result, row, 0); 1253 | char *name = SQ_get_column_string(result, row, 1); 1254 | 1255 | thisid = atoi(id); 1256 | 1257 | /* when the id changes, the name is complete */ 1258 | if( thisid != oldid && oldid != 0 ) { 1259 | add_ref_name( *sql_connection, rec_table, allnames); 1260 | } 1261 | 1262 | strcat(allnames, name); 1263 | strcat(allnames, " "); 1264 | oldid = thisid; 1265 | wr_free(id); 1266 | wr_free(name); 1267 | } 1268 | /* also do the last name */ 1269 | add_ref_name( *sql_connection, rec_table, allnames); 1270 | 1271 | SQ_free_result(result); /* we can do it only because the watchdog */ 1272 | /* has not started between the check for non-NULL result and here */ 1273 | } 1274 | 1275 | /* now copy things back to the main temporary table */ 1276 | sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s", 1277 | id_table, rec_table); 1278 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ); 1279 | 1280 | /* Now drop the IDS recursive table */ 1281 | sprintf(sql_command, "DROP TABLE %s", rec_table); 1282 | dieif(SQ_execute_query(*sql_connection, sql_command, NULL) == -1 ); 1283 | } 1284 | 1285 | 1286 | /* QI_execute() */ 1287 | /*++++++++++++++++++++++++++++++++++++++ 1288 | Execute the query instructions. This is called for each source. 1289 | 1290 | void *database_voidptr Pointer to the database name 1291 | 1292 | void *qis_voidptr Pointer to the query_instructions. 1293 | 1294 | More: 1295 | +html+ <PRE> 1296 | Authors: 1297 | ottrey 1298 | +html+ </PRE> 1299 | ++++++++++++++++++++++++++++++++++++++*/ 1300 | er_ret_t QI_execute(ca_dbSource_t *dbhdl, 1301 | Query_instructions *qis, 1302 | Query_environ *qe, 1303 | acc_st *acc_credit, 1304 | acl_st *acl 1305 | ) 1306 | { 1307 | /* those things must be freed after use! */ 1308 | char *dbhost = ca_get_srcdbmachine(dbhdl); 1309 | char *dbname = ca_get_srcdbname(dbhdl); 1310 | char *dbuser = ca_get_srcdbuser(dbhdl); 1311 | char *dbpass = ca_get_srcdbpassword(dbhdl); 1312 | char *srcnam = ca_get_srcname(dbhdl); 1313 | char id_table[STR_S]; 1314 | char sql_command[STR_XL]; 1315 | GList *datlist=NULL; 1316 | SQ_connection_t *sql_connection=NULL; 1317 | 1318 | sql_connection = SQ_get_connection( dbhost, ca_get_srcdbport(dbhdl), 1319 | dbname, dbuser, dbpass ); 1320 | if (sql_connection == NULL) { 1321 | ER_perror(FAC_QI, QI_CANTDB," database='%s' [%d] %s", 1322 | dbname, SQ_errno(sql_connection), SQ_error(sql_connection)); 1323 | return QI_CANTDB; 1324 | } 1325 | 1326 | sprintf(id_table, "ID_%ld_%d", mysql_thread_id(sql_connection), 1327 | pthread_self()); 1328 | 1329 | /* see if there was a leftover table from a crashed session 1330 | * (assume the ID cannot be currently in use) 1331 | */ 1332 | sprintf(sql_command, "DROP TABLE IF EXISTS %s ", id_table); 1333 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1334 | 1335 | /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */ 1336 | sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table); 1337 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1338 | 1339 | qi_collect_ids(dbhdl, srcnam, &sql_connection, qis, qe, id_table, 1340 | &datlist, acc_credit, acl); 1341 | 1342 | /* post-processing */ 1343 | if( qis->filtered == 0 ) { 1344 | /* start the watchdog just to set the rtc flag */ 1345 | SK_watchclear(&(qe->condat)); 1346 | SK_watchstart(&(qe->condat)); 1347 | 1348 | /* add radix results (only if -K is not active) */ 1349 | insert_radix_serials(&(qe->condat), sql_connection, id_table, datlist); 1350 | 1351 | SK_watchstop(&(qe->condat)); 1352 | } 1353 | 1354 | /* fetch recursive objects (ac,tc,zc,ah) */ 1355 | if ( qis->recursive ) { 1356 | qi_fetch_references( &sql_connection, qe, id_table, acc_credit, acl); 1357 | } /* if recursive */ 1358 | 1359 | /* display */ 1360 | /* -K filtering: 1361 | * right now only filtering, no expanding sets like write_set_objects() 1362 | */ 1363 | 1364 | /* display the immediate data from the radix tree */ 1365 | if( qis->filtered == 1 ) { 1366 | write_radix_immediate(datlist, &(qe->condat), acc_credit, acl ); 1367 | } 1368 | 1369 | /* display objects from the IDs table */ 1370 | write_objects( &sql_connection, id_table, qis->filtered, 1371 | qis->fast, &(qe->condat), acc_credit, acl); 1372 | 1373 | /* Now drop the IDS table */ 1374 | sprintf(sql_command, "DROP TABLE %s", id_table); 1375 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1376 | SQ_close_connection(sql_connection); 1377 | 1378 | /* free allocated parameters */ 1379 | wr_free(dbhost); 1380 | wr_free(dbname); 1381 | wr_free(dbuser); 1382 | wr_free(dbpass); 1383 | wr_free(srcnam); 1384 | 1385 | return QI_OK; 1386 | } /* QI_execute() */ 1387 | 1388 | 1389 | /* instruction_free() */ 1390 | /*++++++++++++++++++++++++++++++++++++++ 1391 | Free the instruction. 1392 | 1393 | Query_instruction *qi query_instruction to be freed. 1394 | 1395 | More: 1396 | +html+ <PRE> 1397 | Authors: 1398 | ottrey 1399 | +html+ </PRE> 1400 | ++++++++++++++++++++++++++++++++++++++*/ 1401 | static void instruction_free(Query_instruction *qi) { 1402 | if (qi != NULL) { 1403 | if (qi->query_str != NULL) { 1404 | wr_free(qi->query_str); 1405 | } 1406 | wr_free(qi); 1407 | } 1408 | } /* instruction_free() */ 1409 | 1410 | /* QI_free() */ 1411 | /*++++++++++++++++++++++++++++++++++++++ 1412 | Free the query_instructions. 1413 | 1414 | Query_instructions *qis Query_instructions to be freed. 1415 | 1416 | XXX This isn't working too well at the moment. 1417 | 1418 | More: 1419 | +html+ <PRE> 1420 | Authors: 1421 | ottrey 1422 | +html+ </PRE> 1423 | ++++++++++++++++++++++++++++++++++++++*/ 1424 | void QI_free(Query_instructions *qis) { 1425 | int i; 1426 | 1427 | for (i=0; qis->instruction[i] != NULL; i++) { 1428 | instruction_free(qis->instruction[i]); 1429 | } 1430 | 1431 | if (qis != NULL) { 1432 | wr_free(qis); 1433 | } 1434 | 1435 | } /* QI_free() */ 1436 | 1437 | /*++++++++++++++++++++++++++++++++++++++ 1438 | Determine if this query should be conducted or not. 1439 | 1440 | If it was an inverse query - it the attribute appears in the query command's bitmap. 1441 | If it was a lookup query - if the attribute appears in the object type bitmap or 1442 | disregard if there is no object_type bitmap (Ie object filter). 1443 | 1444 | mask_t bitmap The bitmap of attribute to be converted. 1445 | 1446 | const Query_command *qc The query_command that the instructions are created 1447 | from. 1448 | 1449 | const Query_t q The query being investigated. 1450 | 1451 | ++++++++++++++++++++++++++++++++++++++*/ 1452 | static int valid_query(const Query_command *qc, const Query_t q) { 1453 | int result=0; 1454 | 1455 | if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) { 1456 | if (q.query != NULL) { 1457 | switch (q.querytype) { 1458 | case Q_INVERSE: 1459 | if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) { 1460 | result = 1; 1461 | } 1462 | break; 1463 | 1464 | case Q_LOOKUP: 1465 | if (MA_bitcount(qc->object_type_bitmap) == 0) { 1466 | result=1; 1467 | } 1468 | else if (q.class<0 || MA_isset(qc->object_type_bitmap, q.class)) { 1469 | result=1; 1470 | } 1471 | break; 1472 | 1473 | default: 1474 | fprintf(stderr, "qi:valid_query() -> Bad querytype\n"); 1475 | } 1476 | } 1477 | } 1478 | 1479 | return result; 1480 | } /* valid_query() */ 1481 | 1482 | /* QI_new() */ 1483 | /*++++++++++++++++++++++++++++++++++++++ 1484 | Create a new set of query_instructions. 1485 | 1486 | const Query_command *qc The query_command that the instructions are created 1487 | from. 1488 | 1489 | const Query_environ *qe The environmental variables that they query is being 1490 | performed under. 1491 | More: 1492 | +html+ <PRE> 1493 | Authors: 1494 | ottrey 1495 | +html+ </PRE> 1496 | ++++++++++++++++++++++++++++++++++++++*/ 1497 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) { 1498 | Query_instructions *qis=NULL; 1499 | Query_instruction *qi=NULL; 1500 | int i_no=0; 1501 | int i; 1502 | char *query_str; 1503 | 1504 | dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK); 1505 | 1506 | qis->filtered = qc->filtered; 1507 | qis->fast = qc->fast; 1508 | qis->recursive = qc->recursive; 1509 | qis->qc = (qc); 1510 | 1511 | 1512 | for (i=0; Query[i].query != NULL; i++) { 1513 | 1514 | /* If a valid query. */ 1515 | if ( valid_query(qc, Query[i]) == 1) { 1516 | 1517 | dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK); 1518 | 1519 | qi->queryindex = i; 1520 | 1521 | /* SQL Query */ 1522 | if ( Query[i].refer == R_SQL) { 1523 | qi->search_type = R_SQL; 1524 | query_str = create_query(Query[i], qc); 1525 | 1526 | if (query_str!= NULL) { 1527 | qi->query_str = query_str; 1528 | qis->instruction[i_no++] = qi; 1529 | } 1530 | } 1531 | /* Radix Query */ 1532 | else if (Query[i].refer == R_RADIX) { 1533 | qi->search_type = R_RADIX; 1534 | 1535 | if (map_qc2rx(qi, qc) == 1) { 1536 | int j; 1537 | int found=0; 1538 | 1539 | /* check that there is no such query yet, for example if 1540 | more than one keytype (wk) matched */ 1541 | for (j=0; j<i_no; j++) { 1542 | Query_instruction *qij = qis->instruction[j]; 1543 | 1544 | if( qij->search_type == R_RADIX 1545 | && Query[qij->queryindex].attribute 1546 | == Query[qi ->queryindex].attribute) { 1547 | 1548 | found=1; 1549 | break; 1550 | } 1551 | } 1552 | 1553 | if ( found ) { 1554 | /* Discard the Query Instruction */ 1555 | wr_free(qi); 1556 | } 1557 | else { 1558 | /* Add the query_instruction to the array */ 1559 | qis->instruction[i_no++] = qi; 1560 | } 1561 | } 1562 | } 1563 | else { 1564 | /* ERROR: bad search_type */ 1565 | die; 1566 | } 1567 | } 1568 | } 1569 | qis->instruction[i_no++] = NULL; 1570 | 1571 | 1572 | { /* tracing */ 1573 | char *descrstr = QI_queries_to_string(qis); 1574 | 1575 | ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr ); 1576 | wr_free( descrstr ); 1577 | } 1578 | 1579 | return qis; 1580 | 1581 | } /* QI_new() */ 1582 | 1583 | /* QI_queries_to_string() 1584 | 1585 | returns a list of descriptions for queries that will be performed. 1586 | */ 1587 | 1588 | char *QI_queries_to_string(Query_instructions *qis) 1589 | { 1590 | Query_instruction *qi; 1591 | int i; 1592 | char *resstr = NULL; 1593 | 1594 | dieif( wr_realloc((void **)&resstr, 2 ) != UT_OK); 1595 | strcpy(resstr, "{"); 1596 | 1597 | for( i = 0; ( qi=qis->instruction[i] ) != NULL; i++ ) { 1598 | char *descr = Query[qi->queryindex].descr; 1599 | int oldres = strlen( resstr ); 1600 | 1601 | dieif( wr_realloc((void **)&resstr, oldres+strlen(descr)+2) != UT_OK); 1602 | strcat(resstr, descr); 1603 | strcat(resstr, ","); 1604 | } 1605 | if( i>0 ) { 1606 | /* cancel the last comma */ 1607 | resstr[strlen(resstr)-1] = 0; 1608 | } 1609 | 1610 | dieif( wr_realloc((void **)&resstr, strlen( resstr ) + 2 ) 1611 | != UT_OK); 1612 | strcat(resstr, "}"); 1613 | 1614 | return resstr; 1615 | }