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