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