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