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 | }