1 | /*************************************** 2 | $Revision: 1.37 $ 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 | /*+ String sizes +*/ 54 | #define STR_S 63 55 | #define STR_M 255 56 | #define STR_L 1023 57 | #define STR_XL 4095 58 | #define STR_XXL 16383 59 | 60 | /* XXX this must be removed from here!!! a .h file must be 61 | generated from xml */ 62 | 63 | #include "defs.h" 64 | 65 | /* create_name_query() */ 66 | /*++++++++++++++++++++++++++++++++++++++ 67 | Create an sql query for the names table. 68 | 69 | char *query_str 70 | 71 | const char *sql_query 72 | 73 | const char *keys 74 | 75 | More: 76 | +html+ <PRE> 77 | Authors: 78 | ottrey 79 | +html+ </PRE><DL COMPACT> 80 | +html+ <DT>Online References: 81 | +html+ <DD><UL> 82 | +html+ </UL></DL> 83 | 84 | ++++++++++++++++++++++++++++++++++++++*/ 85 | static void create_name_query(char *query_str, const char *sql_query, const char *keys) { 86 | int i; 87 | /* Allocate stuff */ 88 | GString *from_clause = g_string_sized_new(STR_XL); 89 | GString *where_clause = g_string_sized_new(STR_XL); 90 | gchar **words = g_strsplit(keys, " ", 0); 91 | 92 | /* double quotes " are used in queries to allow querying for 93 | names like O'Hara */ 94 | 95 | g_string_sprintfa(from_clause, "names N%.2d", 0); 96 | g_string_sprintfa(where_clause, "N%.2d.name=\"%s\"", 0, words[0]); 97 | 98 | for (i=1; words[i] != NULL; i++) { 99 | g_string_sprintfa(from_clause, ", names N%.2d", i); 100 | g_string_sprintfa(where_clause, " AND N%.2d.name=\"%s\" AND N00.object_id = N%.2d.object_id", i, words[i], i); 101 | } 102 | 103 | sprintf(query_str, sql_query, from_clause->str, where_clause->str); 104 | 105 | /* Free up stuff */ 106 | g_strfreev(words); 107 | g_string_free(where_clause,/* CONSTCOND */ TRUE); 108 | g_string_free(from_clause, /* CONSTCOND */ TRUE); 109 | 110 | } /* create_name_query() */ 111 | 112 | 113 | 114 | 115 | static void add_filter(char *query_str, const Query_command *qc) { 116 | int i; 117 | int qlen; 118 | char filter_atom[STR_M]; 119 | 120 | /* 121 | if (MA_bitcount(qc->object_type_bitmap) > 0) { 122 | g_string_sprintfa(query_str, " AND ("); 123 | for (i=0; i < C_END; i++) { 124 | if (MA_isset(qc->object_type_bitmap, i)) { 125 | g_string_sprintfa(query_str, "i.object_type = %d OR ", DF_get_class_dbase_code(i)); 126 | } 127 | } 128 | g_string_truncate(query_str, query_str->len-3); 129 | g_string_append_c(query_str, ')'); 130 | } 131 | */ 132 | if (MA_bitcount(qc->object_type_bitmap) > 0) { 133 | strcat(query_str, " AND ("); 134 | for (i=0; i < C_END; i++) { 135 | if (MA_isset(qc->object_type_bitmap, i)) { 136 | strcpy(filter_atom, ""); 137 | sprintf(filter_atom, "i.object_type = %d OR ", i); 138 | /* XXX class codes should be used instead: 139 | DF_get_class_dbase_code(i)) 140 | but currently the tables contain values of enums 141 | (C_IN, etc) and not codes 142 | */ 143 | strcat(query_str, filter_atom); 144 | } 145 | } 146 | qlen = strlen(query_str); 147 | query_str[qlen-3] = ')'; 148 | query_str[qlen-2] = '\0'; 149 | query_str[qlen-1] = '\0'; 150 | } 151 | 152 | } /* add_filter() */ 153 | 154 | /* create_query() */ 155 | /*++++++++++++++++++++++++++++++++++++++ 156 | Create an sql query from the query_command and the matching keytype and the 157 | selected inverse attributes. 158 | Note this clears the first inv_attribute it sees, so is called sequentially 159 | until there are no inv_attributes left. 160 | 161 | WK_Type keytype The matching keytype. 162 | 163 | const Query_command *qc The query command. 164 | 165 | mask_t *inv_attrs_bitmap The selected inverse attributes. 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 char *create_query(const Query_t q, const Query_command *qc) { 178 | char *result=NULL; 179 | char result_buff[STR_XL]; 180 | Q_Type_t querytype; 181 | int conduct_test = 0; 182 | 183 | if (MA_bitcount(qc->inv_attrs_bitmap) > 0) { 184 | querytype = Q_INVERSE; 185 | } 186 | else { 187 | querytype = Q_LOOKUP; 188 | } 189 | 190 | if ( (q.query != NULL) 191 | && (q.querytype == querytype) ) { 192 | conduct_test=1; 193 | } 194 | 195 | if (conduct_test == 1) { 196 | 197 | if (q.keytype == WK_NAME) { 198 | /* Name queries require special treatment. */ 199 | create_name_query(result_buff, q.query, qc->keys); 200 | } 201 | else if( q.keytype == WK_IPADDRESS ) { /* ifaddr sql lookups */ 202 | ip_range_t myrang; 203 | unsigned begin, end; 204 | ip_keytype_t key_type; 205 | 206 | if (NOERR(IP_smart_range(qc->keys, &myrang, IP_EXPN, &key_type))) { 207 | if(IP_rang_b2_space(&myrang) == IP_V4 ) { 208 | IP_rang_b2v4(&myrang, &begin, &end); 209 | sprintf(result_buff, q.query, begin, end); 210 | } 211 | else { 212 | die; 213 | } 214 | } 215 | } 216 | else { 217 | sprintf(result_buff, q.query, qc->keys); 218 | } 219 | 220 | if (q.class == -1) { 221 | /* It is class type ANY so add the object filtering */ 222 | add_filter(result_buff, qc); 223 | } 224 | 225 | dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK); 226 | strcpy(result, result_buff); 227 | } 228 | 229 | return result; 230 | } /* create_query() */ 231 | 232 | /* fast_output() */ 233 | /*++++++++++++++++++++++++++++++++++++++ 234 | This is for the '-F' flag. 235 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute. 236 | 237 | Fast isn't fast anymore - it's just there for compatibility reasons. 238 | This could be speed up if there were breaks out of the loops, once it matched something. 239 | (Wanna add a goto Marek? :-) ). 240 | 241 | const char *string The string to be "fast outputed". 242 | 243 | More: 244 | +html+ <PRE> 245 | Authors: 246 | ottrey 247 | +html+ </PRE><DL COMPACT> 248 | +html+ <DT>Online References: 249 | +html+ <DD><UL> 250 | +html+ </UL></DL> 251 | 252 | ++++++++++++++++++++++++++++++++++++++*/ 253 | 254 | char *fast_output(const char *str) 255 | { 256 | int i,j; 257 | char *result; 258 | char result_bit[STR_L]; 259 | char result_buff[STR_XL]; 260 | gchar **lines = g_strsplit(str, "\n", 0); 261 | char * const *attribute_names; 262 | gboolean filtering_an_attribute = FALSE; 263 | char *value; 264 | 265 | attribute_names = DF_get_attribute_names(); 266 | 267 | strcpy(result_buff, ""); 268 | 269 | for(i=0; attribute_names[i] != NULL; i++) { 270 | for (j=0; lines[j] != NULL; j++) { 271 | if (strncmp(attribute_names[i], lines[j], strlen(attribute_names[i])) == 0) { 272 | strcpy(result_bit, ""); 273 | /* This is the juicy bit that converts the likes of; "source: RIPE" to "*so: RIPE" */ 274 | value = strchr(lines[j], ':'); 275 | value++; 276 | /* Now get rid of whitespace. */ 277 | while (*value == ' ' || *value == '\t') { 278 | value++; 279 | } 280 | sprintf(result_bit, "*%s: %s\n", DF_get_attribute_code(i), value); 281 | strcat(result_buff, result_bit); 282 | } 283 | /* CONSTCOND */ 284 | else if (filtering_an_attribute == TRUE) { 285 | switch (lines[j][0]) { 286 | case ' ': 287 | case '\t': 288 | case '+': 289 | strcpy(result_bit, ""); 290 | sprintf(result_bit, "%s\n", lines[j]); 291 | strcat(result_buff, result_bit); 292 | break; 293 | 294 | default: 295 | filtering_an_attribute = FALSE; 296 | } 297 | } 298 | } 299 | } 300 | 301 | 302 | dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK); 303 | 304 | strcpy(result, result_buff); 305 | 306 | return result; 307 | } /* fast_output() */ 308 | 309 | /* filter() */ 310 | /*++++++++++++++++++++++++++++++++++++++ 311 | Basically it's for the '-K' flag for non-set (and non-radix) objects. 312 | It assumes lines starting with ' ', '\t' or '+' belong to the prior attribute. 313 | 314 | This could be speed up if there were breaks out of the loops, once it matched something. 315 | (Wanna add a goto Marek? :-) ). 316 | 317 | const char *string The string to be filtered. 318 | 319 | More: 320 | +html+ <PRE> 321 | Authors: 322 | ottrey 323 | +html+ </PRE><DL COMPACT> 324 | +html+ <DT>Online References: 325 | +html+ <DD><UL> 326 | +html+ </UL></DL> 327 | 328 | ++++++++++++++++++++++++++++++++++++++*/ 329 | char *filter(const char *str) { 330 | int i,j, passed=0; 331 | char *result; 332 | char result_bit[STR_L]; 333 | char result_buff[STR_XL]; 334 | gchar **lines = g_strsplit(str, "\n", 0); 335 | char * const *filter_names; 336 | gboolean filtering_an_attribute = FALSE; 337 | 338 | filter_names = DF_get_filter_names(); 339 | 340 | strcpy(result_buff, ""); 341 | for (i=0; filter_names[i] != NULL; i++) { 342 | for (j=0; lines[j] != NULL; j++) { 343 | if (strncmp(filter_names[i], lines[j], strlen(filter_names[i])) == 0) { 344 | strcpy(result_bit, ""); 345 | sprintf(result_bit, "%s\n", lines[j]); 346 | strcat(result_buff, result_bit); 347 | passed++; 348 | 349 | /* can someone explain where %^&()! lint sees the condition here ? */ 350 | /* CONSTCOND */ 351 | filtering_an_attribute = TRUE; 352 | } 353 | /* CONSTCOND */ 354 | else if (filtering_an_attribute == TRUE) { 355 | switch (lines[j][0]) { 356 | case ' ': 357 | case '\t': 358 | case '+': 359 | strcpy(result_bit, ""); 360 | sprintf(result_bit, "%s\n", lines[j]); 361 | strcat(result_buff, result_bit); 362 | break; 363 | 364 | default: 365 | filtering_an_attribute = FALSE; 366 | } 367 | } 368 | } 369 | } 370 | 371 | if(passed) { 372 | strcat(result_buff, "\n"); 373 | } 374 | 375 | dieif( wr_malloc((void **)&result, strlen(result_buff)+1) != UT_OK); 376 | strcpy(result, result_buff); 377 | 378 | return result; 379 | } /* filter() */ 380 | 381 | /* write_results() */ 382 | /*++++++++++++++++++++++++++++++++++++++ 383 | Write the results to the client socket. 384 | 385 | SQ_result_set_t *result The result set returned from the sql query. 386 | unsigned filtered if the objects should go through a filter (-K) 387 | sk_conn_st *condat Connection data for the client 388 | int maxobjects max # of objects to write 389 | 390 | XXX NB. this is very dependendant on what rows are returned in the result!!! 391 | 392 | More: 393 | +html+ <PRE> 394 | Authors: 395 | ottrey 396 | +html+ </PRE><DL COMPACT> 397 | +html+ <DT>Online References: 398 | +html+ <DD><UL> 399 | +html+ </UL></DL> 400 | 401 | ++++++++++++++++++++++++++++++++++++++*/ 402 | static int write_results(SQ_result_set_t *result, 403 | unsigned filtered, 404 | unsigned fast, 405 | sk_conn_st *condat, 406 | acc_st *acc_credit, 407 | acl_st *acl 408 | ) { 409 | SQ_row_t *row; 410 | char *str; 411 | char *filtrate; 412 | char *fasted; 413 | int retrieved_objects=0; 414 | char *objt; 415 | int type; 416 | 417 | /* Get all the results - one at a time */ 418 | if (result != NULL) { 419 | /* here we are making use of the mysql_store_result capability 420 | of interrupting the cycle of reading rows. mysql_use_result 421 | does not allow that, must be read until end */ 422 | 423 | while ( (row = SQ_row_next(result)) != NULL && acc_credit->denials == 0 ) { 424 | if ( (str = SQ_get_column_string(result, row, 0)) == NULL 425 | || (objt = SQ_get_column_string(result, row, 3)) == NULL ) { 426 | /* handle it somehow ? */ 427 | die; 428 | } 429 | else { 430 | /* get + add object type */ 431 | type = atoi(objt); 432 | 433 | /* ASP_QI_LAST_DET */ 434 | ER_dbg_va(FAC_QI, ASP_QI_LAST_DET, 435 | "Retrieved serial id = %d , type = %s", atoi(str), objt); 436 | 437 | wr_free(str); 438 | wr_free(objt); 439 | } 440 | 441 | /* decrement credit for accounting purposes */ 442 | 443 | /* XXX the definition of private/public should go into the defs (xml) */ 444 | switch( type ) { 445 | case C_PN: 446 | case C_RO: 447 | if( acc_credit->private_objects <= 0 && acl->maxbonus != -1 ) { 448 | /* must be negative, will be subtracted */ 449 | acc_credit->denials = -1; 450 | continue; /* go to the head of the loop */ 451 | } 452 | acc_credit->private_objects --; 453 | break; 454 | default: 455 | if( acc_credit->public_objects <= 0 && acl->maxpublic != -1 ) { 456 | acc_credit->denials = -1; 457 | continue; /* go to the head of the loop */ 458 | } 459 | acc_credit->public_objects --; 460 | } 461 | 462 | if ((str = SQ_get_column_string(result, row, 2)) == NULL) { die; } 463 | else { 464 | 465 | /* The fast output stage */ 466 | if (fast == 1) { 467 | fasted = fast_output(str); 468 | wr_free(str); 469 | str = fasted; 470 | } 471 | 472 | /* The filtering stage */ 473 | if (filtered == 0) { 474 | SK_cd_puts(condat, str); 475 | SK_cd_puts(condat, "\n"); 476 | } 477 | else { 478 | 479 | /* XXX accounting should be done AFTER that, and not for objects 480 | filtered out */ 481 | 482 | filtrate = filter(str); 483 | SK_cd_puts(condat, filtrate); 484 | wr_free(filtrate); 485 | } 486 | retrieved_objects++; 487 | } 488 | wr_free(str); 489 | } 490 | } 491 | 492 | return retrieved_objects; 493 | } /* write_results() */ 494 | 495 | /* write_objects() */ 496 | /*++++++++++++++++++++++++++++++++++++++ 497 | This is linked into MySQL by the fact that MySQL doesn't have sub selects 498 | (yet). The queries are done in two stages. Make some temporary tables and 499 | insert into them. Then use them in the next select. 500 | 501 | SQ_connection_t *sql_connection The connection to the database. 502 | 503 | char *id_table The id of the temporary table (This is a result of the hacky 504 | way we've tried to get MySQL to do sub-selects.) 505 | 506 | sk_conn_st *condat Connection data for the client 507 | 508 | More: 509 | +html+ <PRE> 510 | Authors: 511 | ottrey 512 | +html+ </PRE><DL COMPACT> 513 | ++++++++++++++++++++++++++++++++++++++*/ 514 | static void write_objects(SQ_connection_t *sql_connection, 515 | char *id_table, 516 | unsigned int filtered, 517 | unsigned int fast, 518 | sk_conn_st *condat, 519 | acc_st *acc_credit, 520 | acl_st *acl 521 | ) 522 | { 523 | /* XXX This should really return a linked list of the objects */ 524 | 525 | SQ_result_set_t *result; 526 | int retrieved_objects=0; 527 | char sql_command[STR_XL]; 528 | 529 | /* XXX These may and should change a lot. */ 530 | sprintf(sql_command, Q_OBJECTS, id_table); 531 | dieif(SQ_execute_query(sql_connection, sql_command, &result) == -1 ); 532 | 533 | retrieved_objects = write_results(result, filtered, fast, condat, 534 | acc_credit, acl); 535 | SQ_free_result(result); 536 | 537 | } /* write_objects() */ 538 | 539 | /* insert_radix_serials() */ 540 | /*++++++++++++++++++++++++++++++++++++++ 541 | Insert the radix serial numbers into a temporary table in the database. 542 | 543 | mask_t bitmap The bitmap of attribute to be converted. 544 | 545 | SQ_connection_t *sql_connection The connection to the database. 546 | 547 | char *id_table The id of the temporary table (This is a result of the hacky 548 | way we've tried to get MySQL to do sub-selects.) 549 | 550 | GList *datlist The list of data from the radix tree. 551 | 552 | XXX Hmmmmm this isn't really a good place to free things... infact it's quite nasty. :-( 553 | 554 | More: 555 | +html+ <PRE> 556 | Authors: 557 | ottrey 558 | +html+ </PRE><DL COMPACT> 559 | +html+ <DT>Online References: 560 | +html+ <DD><UL> 561 | <LI><A HREF="http://www.gtk.org/rdp/glib/glib-doubly-linked-lists.html">Glist</A> 562 | +html+ </UL></DL> 563 | 564 | ++++++++++++++++++++++++++++++++++++++*/ 565 | static void insert_radix_serials(SQ_connection_t *sql_connection, char *id_table, GList *datlist) { 566 | GList *qitem; 567 | char sql_command[STR_XL]; 568 | int serial; 569 | 570 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) { 571 | rx_datcpy_t *datcpy = qitem->data; 572 | 573 | serial = datcpy->leafcpy.data_key; 574 | 575 | sprintf(sql_command, "INSERT INTO %s values (%d)", id_table, serial); 576 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1); 577 | 578 | wr_free(datcpy->leafcpy.data_ptr); 579 | } 580 | 581 | wr_clear_list( &datlist ); 582 | 583 | } /* insert_radix_serials() */ 584 | 585 | 586 | /* write_radix_immediate() */ 587 | /*++++++++++++++++++++++++++++++++++++++ 588 | Display the immediate data carried with the objects returned by the 589 | radix tree. 590 | 591 | GList *datlist The linked list of dataleaf copies 592 | sk_conn_st *condat Connection data for the client 593 | acc_st *acc_credit Accounting struct 594 | 595 | More: 596 | +html+ <PRE> 597 | Authors: 598 | marek 599 | +html+ </PRE><DL COMPACT> 600 | +html+ <DT>Online References: 601 | +html+ <DD><UL> 602 | +html+ </UL></DL> 603 | 604 | 605 | Also free the list of answers. 606 | */ 607 | static void write_radix_immediate(GList *datlist, 608 | sk_conn_st *condat, 609 | acc_st *acc_credit) 610 | { 611 | GList *qitem; 612 | 613 | for( qitem = g_list_first(datlist); qitem != NULL; qitem = g_list_next(qitem)) { 614 | rx_datcpy_t *datcpy = qitem->data; 615 | 616 | SK_cd_puts(condat, datcpy->leafcpy.data_ptr ); 617 | SK_cd_puts(condat, "\n"); 618 | 619 | wr_free(datcpy->leafcpy.data_ptr); 620 | 621 | acc_credit->public_objects --; 622 | } 623 | 624 | wr_clear_list( &datlist ); 625 | } /* write_radix_immediate() */ 626 | 627 | 628 | /* map_qc2rx() */ 629 | /*++++++++++++++++++++++++++++++++++++++ 630 | The mapping between a query_command and a radix query. 631 | 632 | Query_instruction *qi The Query Instruction to be created from the mapping 633 | of the query command. 634 | 635 | const Query_command *qc The query command to be mapped. 636 | 637 | More: 638 | +html+ <PRE> 639 | Authors: 640 | ottrey 641 | +html+ </PRE><DL COMPACT> 642 | +html+ <DT>Online References: 643 | +html+ <DD><UL> 644 | +html+ </UL></DL> 645 | 646 | ++++++++++++++++++++++++++++++++++++++*/ 647 | static int map_qc2rx(Query_instruction *qi, const Query_command *qc) { 648 | int result=1; 649 | 650 | qi->rx_keys = qc->keys; 651 | 652 | if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 653 | qi->rx_srch_mode = RX_SRCH_EXLESS; 654 | qi->rx_par_a = 0; 655 | } 656 | else if ( (qc->L == 1) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 657 | qi->rx_srch_mode = RX_SRCH_LESS; 658 | qi->rx_par_a = RX_ALL_DEPTHS; 659 | } 660 | else if ( (qc->L == 0) && (qc->M == 1) && (qc->l == 0) && (qc->m == 0) && (qc->x == 0) ) { 661 | qi->rx_srch_mode = RX_SRCH_MORE; 662 | qi->rx_par_a = RX_ALL_DEPTHS; 663 | } 664 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 1) && (qc->m == 0) && (qc->x == 0) ) { 665 | qi->rx_srch_mode = RX_SRCH_LESS; 666 | qi->rx_par_a = 1; 667 | } 668 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 1) && (qc->x == 0) ) { 669 | qi->rx_srch_mode = RX_SRCH_MORE; 670 | qi->rx_par_a = 1; 671 | } 672 | else if ( (qc->L == 0) && (qc->M == 0) && (qc->l == 0) && (qc->m == 0) && (qc->x == 1) ) { 673 | qi->rx_srch_mode = RX_SRCH_EXACT; 674 | qi->rx_par_a = 0; 675 | } 676 | else { 677 | /* user error ( XXX : this should have been checked before) */ 678 | 679 | ER_dbg_va(FAC_QI, ASP_QI_SKIP, 680 | "ERROR in qc2rx mapping: bad combination of flags"); 681 | result = 0; 682 | } 683 | 684 | return result; 685 | 686 | } /* map_qc2rx() */ 687 | 688 | /* run_referral() */ 689 | /* 690 | invoked when no such domain found. Goes through the domain table 691 | and searches for shorter domains, then if it finds one with referral 692 | it performs it, otherwise it just returns nothing. 693 | 694 | to perform referral, it actually composes the referral query 695 | for a given host/port/type and calls the whois query function. 696 | 697 | Well, it returns nothing anyway (void). It just prints to the socket. 698 | 699 | */ 700 | void run_referral(SQ_connection_t *sql_connection, Query_instructions *qis, Query_environ *qe, int qi_index) { 701 | char *dot = qis->qc->keys; 702 | char querystr[STR_L]; 703 | SQ_row_t *row; 704 | SQ_result_set_t *result; 705 | char sql_command[STR_XL]; 706 | int stop_loop=0; 707 | char *ref_host; 708 | char *ref_type; 709 | char *ref_port; 710 | int ref_port_int; 711 | 712 | strcpy(querystr,""); 713 | 714 | while( !stop_loop && (dot=index(dot,'.')) != NULL ) { 715 | dot++; 716 | 717 | ER_dbg_va(FAC_QI, ASP_QI_REF_DET, "run_referral: checking %s", dot); 718 | 719 | 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); 720 | dieif( SQ_execute_query(sql_connection, sql_command, &result) == -1); 721 | 722 | switch( SQ_num_rows(result) ) { 723 | case 0: /* no such domain -> no action, will try next chunk */ 724 | break; 725 | 726 | case 1: /* check for referral host and perform query if present 727 | in any case end the loop */ 728 | stop_loop=1; 729 | assert( (row = SQ_row_next(result)) != NULL); 730 | 731 | ref_host = SQ_get_column_string(result, row, 4); 732 | 733 | ER_dbg_va(FAC_QI, ASP_QI_REF_GEN, "referral host is %s", ref_host); 734 | 735 | if( ref_host != NULL && strlen(ref_host) > 0 ) { 736 | ref_type = SQ_get_column_string(result, row, 2); 737 | ref_port = SQ_get_column_string(result, row, 3); 738 | 739 | /* get the integer value, it should be correct */ 740 | if( sscanf( ref_port, "%d",&ref_port_int) < 1 ) { 741 | die; 742 | } 743 | 744 | /* compose the query: */ 745 | 746 | /* put -r if the reftype is RIPE and -r or -i were used */ 747 | if( strcmp(ref_type,"RIPE") == 0 748 | && ( Query[qis->instruction[qi_index]->queryindex] 749 | .querytype == Q_INVERSE 750 | || qis->recursive > 0 ) ) { 751 | strcat(querystr," -r "); 752 | } 753 | 754 | /* prepend with -Vversion,IP for type CLIENTADDRESS */ 755 | if( strcmp(ref_type,"CLIENTADDRESS") == 0 ) { 756 | char optv[STR_M]; 757 | 758 | snprintf(optv,STR_M," -V%s,%s ","RIP0.88", qe->condat.ip); 759 | strcat(querystr,optv); 760 | } 761 | 762 | /* now set the search term - set to the stripped down version 763 | for inverse query, full-length otherwise */ 764 | if( Query[qis->instruction[qi_index]->queryindex].querytype == Q_INVERSE ) { 765 | strcat(querystr,dot); 766 | } 767 | else { 768 | strcat(querystr,qis->qc->keys); 769 | } 770 | 771 | SK_cd_puts(&(qe->condat), "% Please note: this information is not stored in the RIPE database\n%\n% connecting to the remote referral site "); 772 | SK_cd_puts(&(qe->condat), ref_host); 773 | SK_cd_puts(&(qe->condat), "\n\n"); 774 | 775 | /* WH_sock(sock, host, port, query, maxlines, timeout)) */ 776 | switch( WH_sock(qe->condat.sock, ref_host, ref_port_int, querystr, 25, 5) ) { 777 | case WH_TIMEOUT: 778 | SK_cd_puts(&(qe->condat),"referral timeout\n"); 779 | break; 780 | 781 | case WH_MAXLINES: 782 | SK_cd_puts(&(qe->condat),"referral maxlines exceeded\n"); 783 | break; 784 | 785 | case WH_BADHOST: 786 | SK_cd_puts(&(qe->condat),"referral host not found\n"); 787 | break; 788 | 789 | case WH_CONNECT: 790 | SK_cd_puts(&(qe->condat),"referral host not responding\n"); 791 | break; 792 | 793 | case WH_BIND: 794 | case WH_SOCKET: 795 | /* XXX internal server problem... what to do - wait ? */ 796 | default: 797 | ; 798 | } /*switch WH_sock */ 799 | } 800 | break; 801 | 802 | default: /* more than one domain in this file: something broken */ 803 | die; 804 | } 805 | SQ_free_result(result); 806 | } 807 | } /*run_referral*/ 808 | 809 | static 810 | void 811 | add_ref_name(SQ_connection_t *sql_connection, 812 | char *rectable, 813 | char *allnames 814 | ) 815 | { 816 | /* construct the query, allow zero-length list */ 817 | if( strlen(allnames) > 0 ) { 818 | char final_query[STR_XL]; 819 | char select_query[STR_XL]; 820 | 821 | create_name_query(select_query, "SELECT N00.object_id FROM %s WHERE %s " 822 | "AND N00.object_type != 100 AND N00.thread_id = 0", 823 | allnames); 824 | 825 | sprintf(final_query, "INSERT INTO %s %s", 826 | rectable, 827 | select_query); 828 | 829 | dieif(SQ_execute_query(sql_connection, final_query, NULL) == -1 ); 830 | 831 | allnames[0]=0; 832 | } 833 | } 834 | 835 | 836 | /* QI_execute() */ 837 | /*++++++++++++++++++++++++++++++++++++++ 838 | Execute the query instructions. This is called by a g_list_foreach 839 | function, so each of the sources in the "database source" list can be passed 840 | into this function. 841 | 842 | This function has bloated itself. Can we split it up Marek? (ottrey 13/12/99) 843 | 844 | void *database_voidptr Pointer to the database. 845 | 846 | void *qis_voidptr Pointer to the query_instructions. 847 | 848 | More: 849 | +html+ <PRE> 850 | Authors: 851 | ottrey 852 | +html+ </PRE><DL COMPACT> 853 | +html+ <DT>Online References: 854 | +html+ <DD><UL> 855 | <LI><A 856 | HREF="http://www.gtk.org/rdp/glib/glib-singly-linked-lists.html#G-SLIST-FOREACH">g_list_foreach</A> 857 | +html+ </UL></DL> 858 | 859 | ++++++++++++++++++++++++++++++++++++++*/ 860 | void QI_execute(void *database_voidptr, 861 | Query_instructions *qis, 862 | Query_environ *qe, 863 | acc_st *acc_credit, 864 | acl_st *acl 865 | ) { 866 | char *database = (char *)database_voidptr; 867 | Query_instruction **ins=NULL; 868 | char id_table[STR_S]; 869 | char sql_command[STR_XL]; 870 | GList *datlist=NULL; 871 | int i; 872 | SQ_row_t *row; 873 | SQ_result_set_t *result; 874 | SQ_connection_t *sql_connection=NULL; 875 | char *countstr; 876 | int count,afcount; 877 | 878 | sql_connection = SQ_get_connection(CO_get_host(), CO_get_database_port(), database, CO_get_user(), CO_get_password() ); 879 | 880 | if (sql_connection == NULL) { 881 | SK_cd_puts(&(qe->condat), "% WARNING: Failed to make connection to "); 882 | SK_cd_puts(&(qe->condat), database); 883 | SK_cd_puts(&(qe->condat), " database mirror.\n\n"); 884 | 885 | /* XXX void prevents us from sending any error code back. It is OK ? */ 886 | return; 887 | } 888 | 889 | /* XXX This is a really bad thing to do. 890 | It should'nt _have_ to be called here. 891 | But unfortunately it does. -- Sigh. */ 892 | sprintf(id_table, "ID_%ld", mysql_thread_id(sql_connection) ); 893 | 894 | /* create a table for id's of all objects found NOT NULL , UNIQUE(id) */ 895 | sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", id_table); 896 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 897 | 898 | /* create a table for special subqueries (domain only for now) */ 899 | sprintf(sql_command, "CREATE TABLE %s_S ( id int ) TYPE=HEAP", id_table); 900 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 901 | 902 | /* Iterate through query instructions */ 903 | ins = qis->instruction; 904 | for (i=0; ins[i] != NULL; i++) { 905 | Query_instruction *qi = ins[i]; 906 | 907 | switch ( qi->search_type ) { 908 | case R_SQL: 909 | if ( qi->query_str != NULL ) { 910 | 911 | /* handle special cases first */ 912 | if( Query[qi->queryindex].class == C_DN ) { 913 | 914 | /* XXX if any more cases than just domain appear, we will be 915 | cleaning the _S table from the previous query here 916 | 917 | "DELETE FROM %s_S" 918 | */ 919 | 920 | /* now query into the _S table */ 921 | sprintf(sql_command, "INSERT INTO %s_S %s", id_table, qi->query_str); 922 | dieif( SQ_execute_query(sql_connection, sql_command, NULL) == -1); 923 | 924 | /* if any results - copy to the id's table. 925 | Otherwise, run referral */ 926 | 927 | sprintf(sql_command, "SELECT COUNT(*) FROM %s_S", id_table); 928 | dieif(SQ_execute_query(sql_connection, sql_command, &result) == -1 ); 929 | row = SQ_row_next(result); 930 | countstr = SQ_get_column_string(result, row, 0); 931 | sscanf(countstr, "%d", &count); 932 | SQ_free_result(result); 933 | 934 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET, 935 | "DN lookup for %s found %d entries", 936 | qis->qc->keys, count); 937 | 938 | if( count ) { 939 | sprintf(sql_command, "INSERT INTO %s SELECT id FROM %s_S", 940 | id_table, id_table); 941 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1); 942 | } 943 | 944 | if( count == 0 945 | || Query[qi->queryindex].querytype == Q_INVERSE ) { 946 | /* now: if the domain was not found, we run referral. 947 | unless prohibited by a flag 948 | 949 | But for inverse queries we return the things that were 950 | or were not found AND also do the referral unless prohibited. 951 | */ 952 | if (qis->qc->R == 0) { 953 | run_referral(sql_connection, qis, qe, i); 954 | } 955 | } 956 | 957 | } 958 | else { 959 | sprintf(sql_command, "INSERT INTO %s %s", id_table, qi->query_str); 960 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 961 | } 962 | } 963 | 964 | /* debug */ 965 | afcount = SQ_get_affected_rows(sql_connection); 966 | 967 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET, 968 | "%d entries added in %s query for %s", 969 | afcount, Query[qi->queryindex].descr, qis->qc->keys 970 | ); 971 | break; 972 | 973 | #define RIPE_REG 17 974 | case R_RADIX: 975 | if ( RP_asc_search(qi->rx_srch_mode, qi->rx_par_a, 0, qi->rx_keys, 976 | RIPE_REG, Query[qi->queryindex].attribute, 977 | &datlist, RX_ANS_ALL) == RX_OK ) { 978 | if( ER_is_traced(FAC_QI, ASP_QI_COLL_DET ) ) { 979 | /* prevent unnecessary g_list_length call */ 980 | 981 | ER_dbg_va(FAC_QI, ASP_QI_COLL_DET, 982 | "%d entries after %s (mode %d par %d reg %d) query for %s", 983 | g_list_length(datlist), 984 | Query[qi->queryindex].descr, 985 | qi->rx_srch_mode, qi->rx_par_a, 986 | RIPE_REG, 987 | qi->rx_keys); 988 | } 989 | } 990 | else { 991 | die; 992 | } 993 | break; 994 | 995 | default: die; 996 | } /* switch */ 997 | } /* for every instruction */ 998 | 999 | /* post-processing */ 1000 | 1001 | 1002 | 1003 | /* display */ 1004 | 1005 | if( qis->filtered == 0 ) { 1006 | /* add radix results to the end */ 1007 | insert_radix_serials(sql_connection, id_table, datlist); 1008 | 1009 | /* fetch recursive objects (ac,tc,zc,ah) */ 1010 | if ( qis->recursive ) { 1011 | char rec_table[32]; 1012 | SQ_result_set_t *result; 1013 | SQ_row_t *row; 1014 | int thisid = 0; 1015 | int oldid = 0; 1016 | char allnames[STR_M]; 1017 | 1018 | sprintf(rec_table, "%s_R", id_table); 1019 | 1020 | /* a temporary table for recursive data must be created, because 1021 | a query using the same table as a source and target is illegal 1022 | ( like: INSERT into ID_123 SELECT * FROM ID_123,admin_c WHERE ... ) 1023 | */ 1024 | sprintf(sql_command, "CREATE TABLE %s ( id int PRIMARY KEY NOT NULL ) TYPE=HEAP", rec_table); 1025 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1026 | 1027 | /* find the contacts */ 1028 | sprintf(sql_command, Q_REC, rec_table, id_table, "author"); 1029 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1030 | 1031 | sprintf(sql_command, Q_REC, rec_table, id_table, "admin_c"); 1032 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1033 | 1034 | sprintf(sql_command, Q_REC, rec_table, id_table, "tech_c" ); 1035 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1036 | 1037 | sprintf(sql_command, Q_REC, rec_table, id_table, "zone_c" ); 1038 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1039 | 1040 | 1041 | /* replace references to dummies by references by name */ 1042 | sprintf(sql_command, 1043 | " SELECT id, name FROM %s IDS STRAIGHT_JOIN names " 1044 | " WHERE IDS.id = names.object_id " 1045 | " AND names.object_type = 100" 1046 | " ORDER BY id", 1047 | rec_table); 1048 | 1049 | dieif(SQ_execute_query(sql_connection, sql_command, &result) == -1 ); 1050 | 1051 | allnames[0]=0; 1052 | /* now go through the results and collect names */ 1053 | while ( (row = SQ_row_next(result)) != NULL ) { 1054 | char *id = SQ_get_column_string(result, row, 0); 1055 | char *name = SQ_get_column_string(result, row, 1); 1056 | 1057 | thisid = atoi(id); 1058 | 1059 | /* when the id changes, the name is complete */ 1060 | if( thisid != oldid && oldid != 0 ) { 1061 | add_ref_name( sql_connection, rec_table, allnames); 1062 | } 1063 | 1064 | strcat(allnames, name); 1065 | strcat(allnames, " "); 1066 | oldid = thisid; 1067 | wr_free(id); 1068 | wr_free(name); 1069 | } 1070 | /* also do the last name */ 1071 | add_ref_name( sql_connection, rec_table, allnames); 1072 | 1073 | SQ_free_result(result); 1074 | 1075 | /* now copy things back to the main temporary table */ 1076 | sprintf(sql_command, "INSERT INTO %s SELECT * FROM %s_R", 1077 | id_table, id_table); 1078 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1079 | 1080 | /* Now drop the IDS recursive table */ 1081 | sprintf(sql_command, "DROP TABLE %s_R", id_table); 1082 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1083 | } /* if recursive */ 1084 | } 1085 | else { 1086 | /* -K filtering */ 1087 | /* right now only filtering, no expanding sets */ 1088 | /* implies no recursion */ 1089 | 1090 | /* XXX write_set_objects() ?? */ 1091 | 1092 | /* display the immediate data from the radix tree */ 1093 | /* XXX pass+decrease credit here */ 1094 | write_radix_immediate(datlist, &(qe->condat), acc_credit ); 1095 | } 1096 | 1097 | /* display objects */ 1098 | write_objects(sql_connection, id_table, qis->filtered, 1099 | qis->fast, &(qe->condat), acc_credit, acl); 1100 | 1101 | 1102 | /* Now drop the _S table */ 1103 | sprintf(sql_command, "DROP TABLE %s_S", id_table); 1104 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1105 | 1106 | /* Now drop the IDS table */ 1107 | sprintf(sql_command, "DROP TABLE %s", id_table); 1108 | dieif(SQ_execute_query(sql_connection, sql_command, NULL) == -1 ); 1109 | SQ_close_connection(sql_connection); 1110 | 1111 | 1112 | } /* QI_execute() */ 1113 | 1114 | 1115 | 1116 | /* instruction_free() */ 1117 | /*++++++++++++++++++++++++++++++++++++++ 1118 | Free the instruction. 1119 | 1120 | Query_instruction *qi query_instruction to be freed. 1121 | 1122 | More: 1123 | +html+ <PRE> 1124 | Authors: 1125 | ottrey 1126 | +html+ </PRE><DL COMPACT> 1127 | +html+ <DT>Online References: 1128 | +html+ <DD><UL> 1129 | +html+ </UL></DL> 1130 | 1131 | ++++++++++++++++++++++++++++++++++++++*/ 1132 | static void instruction_free(Query_instruction *qi) { 1133 | if (qi != NULL) { 1134 | if (qi->query_str != NULL) { 1135 | wr_free(qi->query_str); 1136 | } 1137 | wr_free(qi); 1138 | } 1139 | } /* instruction_free() */ 1140 | 1141 | /* QI_free() */ 1142 | /*++++++++++++++++++++++++++++++++++++++ 1143 | Free the query_instructions. 1144 | 1145 | Query_instructions *qis Query_instructions to be freed. 1146 | 1147 | XXX This isn't working too well at the moment. 1148 | 1149 | More: 1150 | +html+ <PRE> 1151 | Authors: 1152 | ottrey 1153 | +html+ </PRE><DL COMPACT> 1154 | +html+ <DT>Online References: 1155 | +html+ <DD><UL> 1156 | +html+ </UL></DL> 1157 | 1158 | ++++++++++++++++++++++++++++++++++++++*/ 1159 | void QI_free(Query_instructions *qis) { 1160 | int i; 1161 | 1162 | for (i=0; qis->instruction[i] != NULL; i++) { 1163 | instruction_free(qis->instruction[i]); 1164 | } 1165 | 1166 | if (qis != NULL) { 1167 | wr_free(qis); 1168 | } 1169 | 1170 | } /* QI_free() */ 1171 | 1172 | /*++++++++++++++++++++++++++++++++++++++ 1173 | Determine if this query should be conducted or not. 1174 | 1175 | If it was an inverse query - it the attribute appears in the query command's bitmap. 1176 | If it was a lookup query - if the attribute appears in the object type bitmap or 1177 | disregard if there is no object_type bitmap (Ie object filter). 1178 | 1179 | mask_t bitmap The bitmap of attribute to be converted. 1180 | 1181 | const Query_command *qc The query_command that the instructions are created 1182 | from. 1183 | 1184 | const Query_t q The query being investigated. 1185 | 1186 | ++++++++++++++++++++++++++++++++++++++*/ 1187 | static int valid_query(const Query_command *qc, const Query_t q) { 1188 | int result=0; 1189 | 1190 | if (MA_isset(qc->keytypes_bitmap, q.keytype) == 1) { 1191 | if (q.query != NULL) { 1192 | switch (q.querytype) { 1193 | case Q_INVERSE: 1194 | if (MA_isset(qc->inv_attrs_bitmap, q.attribute) ) { 1195 | result = 1; 1196 | } 1197 | break; 1198 | 1199 | case Q_LOOKUP: 1200 | if (MA_bitcount(qc->object_type_bitmap) == 0) { 1201 | result=1; 1202 | } 1203 | else if (q.class<0 || MA_isset(qc->object_type_bitmap, q.class)) { 1204 | result=1; 1205 | } 1206 | break; 1207 | 1208 | default: 1209 | fprintf(stderr, "qi:valid_query() -> Bad querytype\n"); 1210 | } 1211 | } 1212 | } 1213 | 1214 | return result; 1215 | } /* valid_query() */ 1216 | 1217 | /* QI_new() */ 1218 | /*++++++++++++++++++++++++++++++++++++++ 1219 | Create a new set of query_instructions. 1220 | 1221 | const Query_command *qc The query_command that the instructions are created 1222 | from. 1223 | 1224 | const Query_environ *qe The environmental variables that they query is being 1225 | performed under. 1226 | More: 1227 | +html+ <PRE> 1228 | Authors: 1229 | ottrey 1230 | +html+ </PRE><DL COMPACT> 1231 | +html+ <DT>Online References: 1232 | +html+ <DD><UL> 1233 | +html+ </UL></DL> 1234 | 1235 | ++++++++++++++++++++++++++++++++++++++*/ 1236 | Query_instructions *QI_new(const Query_command *qc, const Query_environ *qe) { 1237 | Query_instructions *qis=NULL; 1238 | Query_instruction *qi=NULL; 1239 | int i_no=0; 1240 | int i; 1241 | char *query_str; 1242 | 1243 | dieif(wr_calloc( (void **) & qis, 1, sizeof(Query_instructions)) != UT_OK); 1244 | 1245 | qis->filtered = qc->filtered; 1246 | qis->fast = qc->fast; 1247 | qis->recursive = qc->recursive; 1248 | qis->qc = (qc); 1249 | 1250 | 1251 | for (i=0; Query[i].query != NULL; i++) { 1252 | 1253 | /* If a valid query. */ 1254 | if ( valid_query(qc, Query[i]) == 1) { 1255 | 1256 | dieif( wr_calloc((void **) &qi, 1, sizeof(Query_instruction)) != UT_OK); 1257 | 1258 | qi->queryindex = i; 1259 | 1260 | /* SQL Query */ 1261 | if ( Query[i].refer == R_SQL) { 1262 | qi->search_type = R_SQL; 1263 | query_str = create_query(Query[i], qc); 1264 | 1265 | if (query_str!= NULL) { 1266 | qi->query_str = query_str; 1267 | qis->instruction[i_no++] = qi; 1268 | } 1269 | } 1270 | /* Radix Query */ 1271 | else if (Query[i].refer == R_RADIX) { 1272 | qi->search_type = R_RADIX; 1273 | 1274 | if (map_qc2rx(qi, qc) == 1) { 1275 | int j; 1276 | int found=0; 1277 | 1278 | /* check that there is no such query yet, for example if 1279 | more than one keytype (wk) matched */ 1280 | for (j=0; j<i_no; j++) { 1281 | Query_instruction *qij = qis->instruction[j]; 1282 | 1283 | if( qij->search_type == R_RADIX 1284 | && Query[qij->queryindex].attribute 1285 | == Query[qi ->queryindex].attribute) { 1286 | 1287 | found=1; 1288 | break; 1289 | } 1290 | } 1291 | 1292 | if ( found ) { 1293 | /* Discard the Query Instruction */ 1294 | wr_free(qi); 1295 | } 1296 | else { 1297 | /* Add the query_instruction to the array */ 1298 | qis->instruction[i_no++] = qi; 1299 | } 1300 | } 1301 | } 1302 | else { 1303 | /* ERROR: bad search_type */ 1304 | die; 1305 | } 1306 | } 1307 | } 1308 | qis->instruction[i_no++] = NULL; 1309 | 1310 | 1311 | { /* tracing */ 1312 | char *descrstr = QI_queries_to_string(qis); 1313 | 1314 | ER_dbg_va(FAC_QI, ASP_QI_COLL_GEN, "Queries: %s", descrstr ); 1315 | wr_free( descrstr ); 1316 | } 1317 | 1318 | return qis; 1319 | 1320 | } /* QI_new() */ 1321 | 1322 | /* QI_queries_to_string() 1323 | 1324 | returns a list of descriptions for queries that will be performed. 1325 | */ 1326 | 1327 | char *QI_queries_to_string(Query_instructions *qis) 1328 | { 1329 | Query_instruction *qi; 1330 | int i; 1331 | char *resstr = NULL; 1332 | 1333 | dieif( wr_realloc((void **)&resstr, resstr, 2 ) != UT_OK); 1334 | strcpy(resstr, "{"); 1335 | 1336 | for( i = 0; ( qi=qis->instruction[i] ) != NULL; i++ ) { 1337 | char *descr = Query[qi->queryindex].descr; 1338 | int oldres = strlen( resstr ); 1339 | 1340 | dieif( wr_realloc((void **)&resstr, resstr, oldres+strlen(descr)+2) != UT_OK); 1341 | strcat(resstr, descr); 1342 | strcat(resstr, ","); 1343 | } 1344 | if( i>0 ) { 1345 | /* cancel the last comma */ 1346 | resstr[strlen(resstr)-1] = 0; 1347 | } 1348 | 1349 | dieif( wr_realloc((void **)&resstr, resstr, strlen( resstr ) + 2 ) 1350 | != UT_OK); 1351 | strcat(resstr, "}"); 1352 | 1353 | return resstr; 1354 | }