1    | /***************************************
2    |   $Revision: 1.42 $
3    | 
4    |   SQL module (sq) - this is a MySQL implementation of the SQL module.
5    | 
6    |   Status: NOT REVUED, TESTED
7    | 
8    |   ******************/ /******************
9    |   Filename            : mysql_driver.c
10   |   Authors             : ottrey@ripe.net
11   |                         marek@ripe.net
12   |   OSs Tested          : Solaris 7 / sun4u / sparc
13   |   ******************/ /******************
14   |   Copyright (c) 1999,2000,2001,2002                  RIPE NCC
15   |  
16   |   All Rights Reserved
17   |   
18   |   Permission to use, copy, modify, and distribute this software and its
19   |   documentation for any purpose and without fee is hereby granted,
20   |   provided that the above copyright notice appear in all copies and that
21   |   both that copyright notice and this permission notice appear in
22   |   supporting documentation, and that the name of the author not be
23   |   used in advertising or publicity pertaining to distribution of the
24   |   software without specific, written prior permission.
25   |   
26   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
28   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
29   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
30   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32   |   ***************************************/
33   | 
34   | #include "rip.h"
35   | 
36   | #include <stdlib.h>
37   | #include <stdio.h>
38   | #include <time.h>
39   | #include <sys/timeb.h>
40   | #include <strings.h>
41   | #include <sys/types.h>
42   | #include <limits.h>
43   | #include <errno.h>
44   | #include <glib.h>
45   | 
46   | /* 
47   | Description:
48   | 
49   |   Connect to the the MySQL database, returning an error if unsuccessful.
50   | 
51   | Arguments:
52   | 
53   |   SQ_connection_t **conn; used to return pointer to connection structure
54   | 
55   |   const char *host; database server host to connect to, may be NULL or 
56   |     "localhost", in which case Unix sockets may be used
57   | 
58   |   unsigned int port; port to connect to database server on, may be 0 to use  
59   |     default
60   | 
61   |   const char *db; name of database to use, may be NULL
62   | 
63   |   const char *user; name of user to connect as, if NULL then the current Unix 
64   |     user login is used
65   | 
66   |   const char *password; password to send, may be NULL to not use a password
67   | 
68   | Returns:
69   |   
70   |   SQ_OK on success
71   | 
72   |   SQ_CTCONN on error; the exact reason may be determined by using SQ_error() 
73   |     on the value returned in *conn - this structure should be properly via
74   |     SQ_close_connection(), even on error
75   | 
76   | Notes:
77   | 
78   |   Most parameters are passed straight through to the MySQL connect function,
79   |   so the MySQL documentation should be checked for current meaning.
80   | */
81   | 
82   | er_ret_t 
83   | SQ_try_connection (SQ_connection_t **conn, const char *host,
84   |                    unsigned int port, const char *db,
85   |                    const char *user, const char *password)
86   | {
87   |     SQ_connection_t *res;
88   |     
89   |     *conn = mysql_init(NULL);
90   |     dieif(*conn == NULL);  /* XXX SK - need to call "out of memory handler" */
91   | 
92   |     mysql_options(*conn, MYSQL_READ_DEFAULT_GROUP, "client");
93   | 
94   |     res = mysql_real_connect(*conn, host, user, password, db, port, NULL, 0);
95   |     if (res == NULL) {
96   |         return SQ_CTCONN;
97   |     } else {
98   |         return SQ_OK;
99   |     }
100  | }
101  | 
102  | /* SQ_get_connection() */
103  | /*++++++++++++++++++++++++++++++++++++++
104  |   Get a connection to the database.
105  | 
106  |   const char *host
107  |   
108  |   unsigned int port
109  | 
110  |   const char *db
111  |   
112  |   const char *user
113  |   
114  |   const char *password
115  |    
116  |   More:
117  |   +html+ <PRE>
118  |   Authors:
119  |         ottrey
120  |   +html+ </PRE><DL COMPACT>
121  |   +html+ <DT>Online References:
122  |   +html+ <DD><UL>
123  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_init">mysql_init()</A>
124  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_real_connect">mysql_real_connect()</A>
125  |   +html+ </UL></DL>
126  | 
127  |   ++++++++++++++++++++++++++++++++++++++*/
128  | SQ_connection_t *SQ_get_connection(const char *host, unsigned int port, const char *db, const char *user, const char *password) {
129  | 
130  |   SQ_connection_t *sql_connection;
131  |   er_ret_t res;
132  |   unsigned try = 0;
133  | 
134  |   /* XXX MB.
135  |      This is really kludgy! 
136  |      For some (unknown yet) reason, sometimes the connection does not
137  |      work the first time. So we try up to 3 times here, and give up only
138  |      then.
139  | 
140  |      Check the logfiles for warnings, especially with newer mysql version,
141  |      like 3.23. The problem may or may not go away.
142  | 
143  |      SK - I added a sleep() to avoid crushing the poor server.
144  |   */
145  | 
146  |   for (;;) {
147  |     /* try to connect */
148  |     res = SQ_try_connection(&sql_connection, host, port, db, user, password);
149  | 
150  |     /* on success, return our result */
151  |     if (NOERR(res)) {
152  |         return sql_connection;
153  |     }
154  |     else {
155  |       
156  |       /* if we've tried enough, exit with error */
157  |       if (try >= 3) {
158  |         ER_perror(FAC_SQ, SQ_CTCONN, " %s; %s", db, 
159  | 		  sql_connection ? SQ_error(sql_connection) : "-?");
160  |         die;
161  |       }
162  | 
163  |       /* otherwise, prepare to try again */
164  |       ER_perror(FAC_SQ, SQ_CNCT, " %s; %s", db, 
165  | 		sql_connection ? SQ_error(sql_connection) : "-?");
166  | 
167  |       if (try > 0) {
168  |         sleep(try);
169  |       }
170  |       try++;
171  |       
172  |       if( sql_connection ) {
173  | 	SQ_close_connection(sql_connection);
174  |       }
175  |     }
176  |   }/* for(;;) */
177  | } /* SQ_get_connection() */
178  | 
179  | /* SQ_execute_query() */
180  | /*++++++++++++++++++++++++++++++++++++++
181  |   Execute the sql query.
182  | 
183  |   SQ_connection_t *sql_connection Connection to database.
184  |   
185  |   const char *query SQL query.
186  | 
187  |   SQ_result_set_t *result ptr to the structure to hold result. 
188  |   May be NULL if no result is needed.
189  | 
190  |   Returns: 
191  |     0 if the query was successful.
192  |     Non-zero if an error occured.
193  |   
194  |   More:
195  |   +html+ <PRE>
196  |   Authors:
197  |         ottrey, andrei, marek
198  |   +html+ </PRE><DL COMPACT>
199  |   +html+ <DT>Online References:
200  |   +html+ <DD><UL>
201  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_query">mysql_query()</A>
202  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_use_result">mysql_use_result()</A>
203  |   +html+ </UL></DL>
204  | 
205  |   ++++++++++++++++++++++++++++++++++++++*/
206  | int SQ_execute_query(SQ_connection_t *sql_connection, 
207  | 		     const char *query, SQ_result_set_t **result_ptr) 
208  | {
209  |   int err;
210  |   SQ_result_set_t *result;
211  | 
212  |   ut_timer_t start_time, stop_time; 
213  |   
214  |   UT_timeget(&start_time);
215  | 
216  |   err = mysql_query(sql_connection, query);
217  | 
218  |   /* log the time and result of the query */
219  |   if (err == 0) {
220  |     result = mysql_store_result(sql_connection);
221  |     
222  |     if (ER_is_traced(FAC_SQ, ASP_SQ_QRYTIME)) {
223  |       float seconds;
224  | 
225  |       UT_timeget(&stop_time);      
226  |       seconds = UT_timediff( &start_time, &stop_time );
227  |   
228  |       ER_dbg_va(FAC_SQ, ASP_SQ_QRYTIME,
229  | 		"spent %.2f sec; got %d rows from [%s: %s]", 
230  | 		seconds, 
231  | 		SQ_get_affected_rows(sql_connection),
232  | 		sql_connection->db, 
233  | 		query);
234  |     }
235  |     
236  |     if(result_ptr) *result_ptr=result;
237  |     else if(result) mysql_free_result(result);
238  |     return(0);
239  |   }
240  |   else return(-1);  
241  |   
242  | } /* SQ_execute_query() */
243  | 
244  | /* 
245  | Description:
246  |  
247  |     Performs identially to SQ_execute_query(), except that it does not read the
248  |     entire query into memory.
249  | 
250  | Notes:
251  | 
252  |     No data may be written to the table until the entire result set is read,
253  |     so this should only be used in cases where:
254  | 
255  |     1. an unacceptably large amount of memory will be returned by the query
256  |     2. there is no chance that a user can accidentally or maliciously 
257  |        prevent the result set from being read in a expedicious manner
258  | */
259  | 
260  | int 
261  | SQ_execute_query_nostore(SQ_connection_t *sql_connection, 
262  |                          const char *query, SQ_result_set_t **result_ptr) 
263  | {
264  |   int err;
265  |   SQ_result_set_t *result;
266  | 
267  |   err = mysql_query(sql_connection, query);
268  |   if (err != 0) {
269  |       return -1;
270  |   }
271  |   result = mysql_use_result(sql_connection);
272  |   if (result == NULL) {
273  |       return -1;
274  |   } 
275  |   *result_ptr = result;
276  |   return 0;
277  | } /* SQ_execute_query_nostore() */
278  | 
279  | /* SQ_get_column_count() */
280  | /*++++++++++++++++++++++++++++++++++++++
281  |   Get the column count.
282  | 
283  |   SQ_result_set_t *result The results from the query.
284  |   
285  |   More:
286  |   +html+ <PRE>
287  |   Authors:
288  |         ottrey
289  |   +html+ </PRE><DL COMPACT>
290  |   +html+ <DT>Online References:
291  |   +html+ <DD><UL>
292  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_num_fields">mysql_num_fields()</A>
293  |   +html+ </UL></DL>
294  | 
295  |   ++++++++++++++++++++++++++++++++++++++*/
296  | int SQ_get_column_count(SQ_result_set_t *result) {
297  |   int cols;
298  | 
299  |   cols = mysql_num_fields(result);
300  | 
301  |   return cols;
302  | 
303  | } /* SQ_get_column_count() */
304  | 
305  | /* SQ_get_table_size() */
306  | /*++++++++++++++++++++++++++++++++++++++
307  |   Get the row count of a table
308  | 
309  |   char *table   The table to be examined
310  |   
311  |   More:
312  |   +html+ <PRE>
313  |   Authors:
314  |         marek
315  |   +html+ </PRE>
316  | 
317  |   ++++++++++++++++++++++++++++++++++++++*/
318  | int SQ_get_table_size(SQ_connection_t *sql_connection,
319  | 		     char *table) {  
320  |   int count;
321  |   GString *sql_command;
322  |   SQ_result_set_t *result;
323  |   SQ_row_t *row;
324  |   char *countstr;
325  |   
326  |   sql_command = g_string_new("");
327  |   g_string_sprintf(sql_command, "SELECT COUNT(*) FROM %s", table);
328  |   dieif(SQ_execute_query(sql_connection, sql_command->str, &result) == -1 );
329  |   g_string_free(sql_command, TRUE);
330  | 
331  |   row = SQ_row_next(result);
332  |   
333  |   countstr = SQ_get_column_string(result, row, 0);
334  |   sscanf(countstr, "%d", &count);	
335  |   UT_free(countstr);
336  |   
337  |   SQ_free_result(result);
338  | 	
339  |   return count;  
340  | } /* SQ_get_table_size() */
341  | 
342  | /* SQ_get_affected_rows() */
343  | /*++++++++++++++++++++++++++++++++++++++
344  |   Get the row count of a table
345  | 
346  |   char *table   The table to be examined
347  |   
348  |   More:
349  |   +html+ <PRE>
350  |   Authors:
351  |         marek
352  |   +html+ </PRE>
353  | 
354  |   ++++++++++++++++++++++++++++++++++++++*/
355  | int SQ_get_affected_rows(SQ_connection_t *sql_connection)
356  | {
357  |   return (int)mysql_affected_rows(sql_connection);
358  | }/* SQ_get_affected_rows() */
359  | 		      
360  | /* SQ_get_insert_id() */
361  | /*++++++++++++++++++++++++++++++++++++++
362  |   Get the ID that was most recently generated for an AUTO_INCREMENT field
363  | 
364  |  
365  |   More:
366  |   +html+ <PRE>
367  |   Authors:
368  |         andrei
369  |   +html+ </PRE>
370  | 
371  |   ++++++++++++++++++++++++++++++++++++++*/
372  | long SQ_get_insert_id(SQ_connection_t *sql_connection)
373  | {
374  |   return (long)mysql_insert_id(sql_connection);
375  | }/* SQ_get_insert_id() */
376  | 	
377  | /* SQ_get_column_label() */
378  | /*++++++++++++++++++++++++++++++++++++++
379  |   Get the column label.
380  | 
381  |   SQ_result_set_t *result The results from the query.
382  |   
383  |   unsigned int column The column index.
384  | 
385  |   More:
386  |   +html+ <PRE>
387  |   Authors:
388  |         ottrey
389  |   +html+ </PRE><DL COMPACT>
390  |   +html+ <DT>Online References:
391  |   +html+ <DD><UL>
392  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_fetch_field_direct">mysql_fetch_field_direct()</A>
393  |   +html+ </UL></DL>
394  | 
395  |   ++++++++++++++++++++++++++++++++++++++*/
396  | char *SQ_get_column_label(SQ_result_set_t *result, unsigned int column) {
397  |   char *str;
398  | /* MySQL decided to change their interface.  Doh! */
399  | #ifdef OLDMYSQL
400  |   MYSQL_FIELD field;
401  | 
402  |   field = mysql_fetch_field_direct(result, column);
403  | 
404  |   str = UT_strdup(field.name);
405  | #else
406  |   MYSQL_FIELD *field;
407  | 
408  |   field = mysql_fetch_field_direct(result, column);
409  | 
410  |   str = UT_strdup(field->name);
411  | #endif
412  | 
413  | /*
414  |   printf("column=%d\n", column);
415  |   printf("field.name=%s\n", field.name);
416  |   printf("field.table=%s\n", field.table);
417  | 
418  |   printf("field.def=%s\n", field.def);
419  | 
420  |   printf("field.type=%d\n", field.type);
421  |   printf("field.length=%d\n", field.length);
422  |   printf("field.max_length=%d\n", field.max_length);
423  |   printf("field.flags=%d\n", field.flags);
424  |   printf("field.decimals=%d\n", field.decimals);
425  | */
426  | 
427  |   return str;
428  | 
429  | } /* SQ_get_column_label() */
430  | 
431  | /* SQ_get_column_max_length() */
432  | /*++++++++++++++++++++++++++++++++++++++
433  |   Get the max length of the column.
434  | 
435  |   SQ_result_set_t *result The results from the query.
436  |   
437  |   unsigned int column The column index.
438  | 
439  |   More:
440  |   +html+ <PRE>
441  |   Authors:
442  |         ottrey
443  |   +html+ </PRE><DL COMPACT>
444  |   +html+ <DT>Online References:
445  |   +html+ <DD><UL>
446  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_fetch_field_direct">mysql_fetch_field_direct()</A>
447  |   +html+ </UL></DL>
448  | 
449  |   ++++++++++++++++++++++++++++++++++++++*/
450  | unsigned int SQ_get_column_max_length(SQ_result_set_t *result, unsigned int column) {
451  | /* MySQL decided to change their interface.  Doh! */
452  | #ifdef OLDMYSQL
453  |   MYSQL_FIELD field;
454  | 
455  |   field = mysql_fetch_field_direct(result, column);
456  | 
457  |   return field.length;
458  | #else
459  |   MYSQL_FIELD *field;
460  | 
461  |   field = mysql_fetch_field_direct(result, column);
462  | 
463  |   return field->length;
464  | #endif
465  | 
466  | } /* SQ_get_column_max_length() */
467  | 
468  | /* SQ_row_next() */
469  | /*++++++++++++++++++++++++++++++++++++++
470  |   Get the next row.
471  | 
472  |   SQ_result_set_t *result The results from the query.
473  |   
474  |   unsigned int column The column index.
475  | 
476  |   More:
477  |   +html+ <PRE>
478  |   Authors:
479  |         ottrey
480  |   +html+ </PRE><DL COMPACT>
481  |   +html+ <DT>Online References:
482  |   +html+ <DD><UL>
483  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_fetch_row">mysql_fetch_row()</A>
484  |   +html+ </UL></DL>
485  | 
486  |   ++++++++++++++++++++++++++++++++++++++*/
487  | SQ_row_t *SQ_row_next(SQ_result_set_t *result) {
488  | 
489  |   return (SQ_row_t *)mysql_fetch_row(result);
490  | 
491  | } /* SQ_row_next() */
492  | 
493  | /* SQ_get_column_string() */
494  | /*++++++++++++++++++++++++++++++++++++++
495  |   Get the column string.
496  | 
497  |   SQ_row_t *current_row The current row (obtained from a SQ_row_next() ).
498  |   
499  |   unsigned int column The column index.
500  | 
501  |   More:
502  |   +html+ <PRE>
503  |   Authors:
504  |         ottrey
505  |   +html+ </PRE><DL COMPACT>
506  |   +html+ <DT>Online References:
507  |   +html+ <DD><UL>
508  |   +html+ </UL></DL>
509  | 
510  |   ++++++++++++++++++++++++++++++++++++++*/
511  | char *SQ_get_column_string(SQ_result_set_t *result, SQ_row_t *current_row, unsigned int column) {
512  |   char *str=NULL;
513  |   unsigned length = mysql_fetch_lengths(result)[column];
514  |   
515  |   if (current_row != NULL && current_row[column] != NULL) {
516  |     str = (char *)UT_malloc(length + 1);
517  |     memcpy(str, current_row[column], length );
518  |     str[length] = '\0';
519  |   }
520  | 
521  |   return str;
522  |   
523  | } /* SQ_get_column_string() */
524  | 
525  | /* SQ_get_column_string_nocopy - return pointer to the column string
526  |    without making a copy of it */
527  | char *SQ_get_column_string_nocopy(SQ_result_set_t *result, 
528  | 				  SQ_row_t *current_row, 
529  | 				  unsigned int column) 
530  | {
531  |   if (current_row != NULL && current_row[column] != NULL) {
532  |     return (char *)current_row[column];
533  |   }
534  |   return NULL;
535  | }/* SQ_get_column_string_nocopy */
536  | 
537  | 
538  | 
539  | /* SQ_get_column_strings() */
540  | /*++++++++++++++++++++++++++++++++++++++
541  |   Get the all the strings in one column.
542  | 
543  |   SQ_result_set_t *result The results.
544  |   
545  |   unsigned int column The column index.
546  | 
547  |   More:
548  |   +html+ <PRE>
549  |   Authors:
550  |         ottrey
551  |   +html+ </PRE><DL COMPACT>
552  |   +html+ <DT>Online References:
553  |   +html+ <DD><UL>
554  |   +html+ </UL></DL>
555  | 
556  |   ++++++++++++++++++++++++++++++++++++++*/
557  | char *SQ_get_column_strings(SQ_result_set_t *result, unsigned int column) {
558  |   MYSQL_ROW row;
559  |   GString *buf;
560  |   char *str;
561  | 
562  |   /* allocate a buffer for building the result in */
563  |   buf = g_string_sized_new(STR_XXL);
564  | 
565  |   /* add each row of the result as a line to our result buffer */
566  |   while ((row = mysql_fetch_row(result)) != NULL) {
567  |     if (row[column] != NULL) {
568  |       g_string_append(buf, row[column]);
569  |       g_string_append_c(buf, '\n');
570  |     }
571  |   }
572  | 
573  |   /* if something found, copy to a string for return */
574  |   if (buf->len > 0) {
575  |     str = UT_strdup(buf->str);
576  |   } else {
577  |     str = NULL;
578  |   }
579  | 
580  |   /* free temporary memory */
581  |   g_string_free(buf, TRUE);
582  | 
583  |   return str;
584  | 
585  | } /* SQ_get_column_strings() */
586  | 
587  | /* SQ_get_column_int() */
588  | /*++++++++++++++++++++++++++++++++++++++
589  |   Get an integer from the column.
590  | 
591  |   SQ_result_set_t *result The results.
592  |   
593  |   SQ_row_t *current_row The current row.
594  | 
595  |   unsigned int column The column index.
596  | 
597  |   long *resultptr     pointer where the result should be stored
598  | 
599  |   returns -1 if error occurs, 0 otherwise.
600  |   Note - it never says what error occured....
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  |   +html+ </UL></DL>
610  | 
611  |   ++++++++++++++++++++++++++++++++++++++*/
612  | int SQ_get_column_int(SQ_result_set_t *result, SQ_row_t *current_row, unsigned int column, long  *resultptr) {
613  |   int ret_val;
614  |   long col_val;
615  |   char *endptr;
616  | 
617  |   if (current_row[column] != NULL) {
618  |       col_val = strtol((char *)current_row[column], &endptr, 10);
619  | 
620  |       /* under- or over-flow */
621  |       if (((col_val==LONG_MIN) || (col_val==LONG_MAX)) && (errno==ERANGE)) {
622  | 	ret_val = -1;
623  | 
624  |       /* unrecognized characters in string */
625  |       } else if (*endptr != '\0') {
626  | 	ret_val = -1;
627  | 
628  |       /* good parse */
629  |       } else {
630  | 	*resultptr = col_val;
631  | 	ret_val = 0;
632  |       }
633  |   } else {
634  |       ret_val = -1;
635  |   }
636  |   return ret_val;
637  |   
638  | } /* SQ_get_column_int() */
639  | 
640  | 
641  | /* SQ_result_to_string() */
642  | /*++++++++++++++++++++++++++++++++++++++
643  |   Convert the result set to a string.
644  | 
645  |   SQ_result_set_t *result The results.
646  |   
647  |   More:
648  |   +html+ <PRE>
649  |   Authors:
650  |         ottrey
651  |   +html+ </PRE><DL COMPACT>
652  |   +html+ <DT>Online References:
653  |   +html+ <DD><UL>
654  |   +html+ </UL></DL>
655  | 
656  |   ++++++++++++++++++++++++++++++++++++++*/
657  | char *SQ_result_to_string(SQ_result_set_t *result) {
658  |   MYSQL_ROW row;
659  |   unsigned int no_cols;
660  |   unsigned int i, j;
661  |   char str_buffer[STR_XXL];
662  |   char str_buffer_tmp[STR_L];
663  |   char border[STR_L];
664  | 
665  |   char *label;
666  | 
667  |   unsigned int length[STR_S];
668  | 
669  |   strcpy(str_buffer, "");
670  | 
671  |   no_cols = mysql_num_fields(result);
672  | 
673  |   /* Determine the maximum column widths */
674  |   /* XXX Surely MySQL should keep note of this for me! */
675  |   strcpy(border, "");
676  |   for (i=0; i < no_cols; i++) {
677  |     length[i] = SQ_get_column_max_length(result, i);
678  |     /* Make sure the lenghts don't get too long */
679  |     if (length[i] > STR_M) {
680  |       length[i] = STR_M;
681  |     }
682  |     strcat(border, "*");
683  |     for (j=0; (j <= length[i]) && (j < STR_L); j++) {
684  |       strcat(border, "-");
685  |     }
686  |   }
687  |   strcat(border, "*\n");
688  |   /*
689  |   for (i=0; i < no_cols; i++) {
690  |     printf("length[%d]=%d\n", i, length[i]);
691  |   }
692  |   */
693  | 
694  |   strcat(str_buffer, border);
695  | 
696  |   for (i=0; i < no_cols; i++) {
697  |     label = SQ_get_column_label(result, i);
698  |     if (label != NULL) {
699  |       sprintf(str_buffer_tmp, "| %-*s", length[i], label);
700  |       strcat(str_buffer, str_buffer_tmp);
701  |     }
702  |   }
703  |   strcat(str_buffer, "|\n");
704  |   
705  |   strcat(str_buffer, border);
706  | 
707  | 
708  |   while ((row = mysql_fetch_row(result)) != NULL) {
709  |     for (i=0; i < no_cols; i++) {
710  |       if (row[i] != NULL) {
711  |         sprintf(str_buffer_tmp, "| %-*s", length[i], row[i]);
712  |       }
713  |       else {
714  |         sprintf(str_buffer_tmp, "| %-*s", length[i], "NuLL");
715  |       }
716  |       strcat(str_buffer, str_buffer_tmp);
717  |     }
718  |     strcat(str_buffer, "|\n");
719  | 
720  |     if (strlen(str_buffer) >= (STR_XXL - STR_XL) ) {
721  |       strcat(str_buffer, "And some more stuff...\n");
722  |       break;
723  |     }
724  |   }
725  | 
726  |   strcat(str_buffer, border);
727  |   
728  |   return UT_strdup(str_buffer);
729  | 
730  | } /* SQ_result_to_string() */
731  | 
732  | /* SQ_free_result() */
733  | /*++++++++++++++++++++++++++++++++++++++
734  |   Free the result set.
735  | 
736  |   SQ_result_set_t *result The results.
737  |   
738  |   More:
739  |   +html+ <PRE>
740  |   Authors:
741  |         ottrey
742  |   +html+ </PRE><DL COMPACT>
743  |   +html+ <DT>Online References:
744  |   +html+ <DD><UL>
745  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_free_result">mysql_free_result()</A>
746  |   +html+ </UL></DL>
747  | 
748  |   ++++++++++++++++++++++++++++++++++++++*/
749  | void SQ_free_result(SQ_result_set_t *result) {
750  |   mysql_free_result(result);
751  | } /* SQ_free_result() */
752  | 
753  | 
754  | /* SQ_close_connection() */
755  | /*++++++++++++++++++++++++++++++++++++++
756  |   Call this function to close a connection to the server
757  | 
758  |   SQ_connection_t *sql_connection The connection to the database.
759  |   
760  |   More:
761  |   +html+ <PRE>
762  |   Authors:
763  |         ottrey
764  |   +html+ </PRE><DL COMPACT>
765  |   +html+ <DT>Online References:
766  |   +html+ <DD><UL>
767  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_close">mysql_close()</A>
768  |   +html+ </UL></DL>
769  | 
770  |   ++++++++++++++++++++++++++++++++++++++*/
771  | void SQ_close_connection(SQ_connection_t *sql_connection) {
772  | 
773  |   mysql_close(sql_connection);
774  | 
775  | }
776  | 
777  | /* SQ_num_rows() */
778  | /*++++++++++++++++++++++++++++++++++++++
779  |   Call this function to find out how many rows are in a query result
780  | 
781  |   SQ_result_set_t *result The results.
782  |   
783  |   More:
784  |   +html+ <PRE>
785  |   Authors:
786  |         ottrey
787  |   +html+ </PRE><DL COMPACT>
788  |   +html+ <DT>Online References:
789  |   +html+ <DD><UL>
790  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_num_rows">mysql_num_rows()</A>
791  |   +html+ </UL></DL>
792  | 
793  |   ++++++++++++++++++++++++++++++++++++++*/
794  | int SQ_num_rows(SQ_result_set_t *result) {
795  |   int rows=-1;
796  | 
797  |   if (result != NULL) {
798  |     rows = mysql_num_rows(result);
799  |   }
800  | 
801  |   return rows;
802  | }
803  | 
804  | /* SQ_info_to_string() */
805  | /*++++++++++++++++++++++++++++++++++++++
806  |   Convert all available information about the sql server into a string.
807  | 
808  |   SQ_connection_t *sql_connection The connection to the database.
809  | 
810  |   More:
811  |   +html+ <PRE>
812  |   Authors:
813  |         ottrey
814  |   +html+ </PRE><DL COMPACT>
815  |   +html+ <DT>Online References:
816  |   +html+ <DD><UL>
817  |   +html+ </UL></DL>
818  | 
819  |   ++++++++++++++++++++++++++++++++++++++*/
820  | char *SQ_info_to_string(SQ_connection_t *sql_connection) {
821  |   GString *buf;
822  |   char *str_tmp;
823  |   char *result;
824  | 
825  |   buf = g_string_sized_new(STR_XXL);
826  | 
827  |   /* Makes the server dump debug information to the log. */
828  |   g_string_sprintfa(buf, "mysql_dump_debug_info()=%d\n", 
829  |                     mysql_dump_debug_info(sql_connection));
830  | 
831  |   /* Returns the error number from the last MySQL function. */
832  |   g_string_sprintfa(buf, "mysql_errno()=%d\n", mysql_errno(sql_connection));
833  | 
834  |   /* Returns the error message from the last MySQL function. */
835  |   g_string_sprintfa(buf, "mysql_error()=%s\n", mysql_error(sql_connection));
836  | 
837  |   /* Returns client version information. */
838  |   g_string_sprintfa(buf, "mysql_get_client_info()=%s\n", 
839  |                     mysql_get_client_info() );
840  | 
841  |   /* Returns a string describing the connection. */
842  |   g_string_sprintfa(buf, "mysql_get_host_info()=%s\n",
843  |                     mysql_get_host_info(sql_connection));
844  | 
845  |   /* Returns the protocol version used by the connection. */
846  |   g_string_sprintfa(buf, "mysql_get_proto_info()=%d\n", 
847  |                     mysql_get_proto_info(sql_connection));
848  | 
849  |   /* Returns the server version number. */
850  |   g_string_sprintfa(buf, "mysql_get_server_info()=%s\n",
851  |                     mysql_get_server_info(sql_connection));
852  | 
853  |   /* Information about the most recently executed query. */
854  |   str_tmp = mysql_info(sql_connection);
855  |   if (str_tmp != NULL) {
856  |     g_string_sprintfa(buf, "mysql_info()=\"%s\"\n", str_tmp);
857  |   } else {
858  |     g_string_append(buf, "mysql_info()=NULL\n");
859  |   }
860  | 
861  | 
862  |   /* Returns a list of the current server threads. 
863  | 
864  |      NOT Used here, because it returns a RESULT struct that must be 
865  |      iterated through.
866  |      
867  |      sprintf(str_buffer_tmp, "mysql_list_processes()=%x\n", mysql_list_processes(sql_connection));
868  |      strcat(str_buffer, str_buffer_tmp);
869  |      
870  |   */
871  | 
872  |   /* Checks if the connection to the server is working. */
873  |   g_string_sprintfa(buf, "mysql_ping()=%d\n", mysql_ping(sql_connection));
874  | 
875  |   /* Returns the server status as a string. */
876  |   g_string_sprintfa(buf, "mysql_stat()=%s\n", mysql_stat(sql_connection));
877  | 
878  |   /* Returns the current thread id. */
879  |   g_string_sprintfa(buf, "mysql_thread_id()=%ld\n",
880  |                     mysql_thread_id(sql_connection));
881  | 
882  | 
883  |   /* copy our string and return the copy */
884  |   result = UT_strdup(buf->str);
885  |   g_string_free(buf, TRUE);
886  |   return result;
887  | 
888  | } /* SQ_info_to_string() */
889  | 
890  | /* SQ_error() */
891  | /*++++++++++++++++++++++++++++++++++++++
892  |   Get the error string for the last error.
893  | 
894  |   SQ_connection_t *sql_connection The connection to the database.
895  | 
896  |   More:
897  |   +html+ <PRE>
898  |   Authors:
899  |         ottrey
900  |   +html+ </PRE><DL COMPACT>
901  |   +html+ <DT>Online References:
902  |   +html+ <DD><UL>
903  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_error">mysql_error()</A>
904  |   +html+ </UL></DL>
905  | 
906  |   ++++++++++++++++++++++++++++++++++++++*/
907  | char *SQ_error(SQ_connection_t *sql_connection) {
908  | 
909  |   return mysql_error(sql_connection);
910  | 
911  | } /* SQ_error() */
912  | 
913  | /* SQ_errno() */
914  | /*++++++++++++++++++++++++++++++++++++++
915  |   Get the error number for the last error.
916  | 
917  |   SQ_connection_t *sql_connection The connection to the database.
918  | 
919  |   More:
920  |   +html+ <PRE>
921  |   Authors:
922  |         ottrey
923  |   +html+ </PRE><DL COMPACT>
924  |   +html+ <DT>Online References:
925  |   +html+ <DD><UL>
926  |   +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_free_result">mysql_free_result()</A>
927  |   +html+ </UL></DL>
928  | 
929  |   ++++++++++++++++++++++++++++++++++++++*/
930  | int SQ_errno(SQ_connection_t *sql_connection) {
931  | 
932  |   return mysql_errno(sql_connection);
933  | 
934  | } /* SQ_errno() */
935  | 
936  | /* SQ_get_info() */
937  | /*++++++++++++++++++++++++++++++++++++++
938  |   Get additional information about the most 
939  |   recently executed query.
940  |   
941  |   SQ_connection_t *sql_connection The connection to the database.
942  |   int info[3] array of integers where information is stored
943  |   
944  |   The meaning of the numbers returned depends on the query type:
945  |   
946  |   info[SQL_RECORDS] - # of Records for INSERT
947  |   info[SQL_MATCHES] - # of Matches for UPDATE
948  |   info[SQL_DUPLICATES] - # of Duplicates
949  |   info[SQL_WARNINGS] - # of Warnings
950  |   
951  |   More:
952  |  +html+ <PRE>
953  |  Authors:
954  |   andrei
955  |  +html+ </PRE><DL COMPACT>
956  |  +html+ <DT>Online References:
957  |  +html+ <DD><UL>
958  |  +html+     <LI><A HREF="http://www.tcx.se/Manual/manual.html#mysql_info">mysql_info()</A>
959  |  +html+ </UL></DL>
960  | 
961  | ++++++++++++++++++++++++++++++++++++++*/  
962  |   
963  | int SQ_get_info(SQ_connection_t *sql_connection, int info[3])
964  | {
965  | int ii;
966  | char *colon, *buf_ptr, buf[20]; 
967  | char *infoline;
968  | 
969  |   infoline=mysql_info(sql_connection); 
970  |   ii=0;
971  |   colon = infoline;
972  |   while (*colon != '\0') {
973  |    colon++;
974  |    buf_ptr=buf;
975  |    if(isdigit((int)*colon)){
976  |     while(isdigit((int)*colon)){
977  |      *buf_ptr=*colon; buf_ptr++; colon++;
978  |     }
979  |     *buf_ptr='\0';
980  |     info[ii]=atoi(buf); ii++;
981  |    } 
982  |   }
983  |  return(0);
984  | }
985  | 
986  | 
987  | /* 
988  |    open a connection with the same parameters
989  | 
990  |    by marek
991  | */
992  | SQ_connection_t *
993  | SQ_duplicate_connection(SQ_connection_t *orig)
994  | {
995  |   return SQ_get_connection(orig->host, orig->port, orig->db, 
996  | 			   orig->user, orig->passwd);
997  | }
998  | 
999  | /* 
1000 |    abort the current query on the given connection
1001 | 
1002 |    by marek
1003 | */
1004 | int
1005 | SQ_abort_query(SQ_connection_t *sql_connection)
1006 | {
1007 |   SQ_connection_t *contemp = SQ_duplicate_connection(sql_connection);
1008 |   int res = mysql_kill(contemp, sql_connection->thread_id);
1009 | 
1010 |   ER_dbg_va(FAC_SQ, ASP_SQ_ABORT,
1011 | 	    "connection %d aborted by tmp thread %d",
1012 | 	    sql_connection->thread_id,
1013 | 	    contemp->thread_id);
1014 | 
1015 |   SQ_close_connection(contemp);
1016 | 
1017 |   return res;
1018 | }
1019 | 
1020 | /* SQ_ping() */
1021 | /*++++++++++++++++++++++++++++++++++++++
1022 |   Checks whether or not the connection to the server is working. 
1023 |   If it has gone down, an automatic reconnection is attempted.
1024 | 
1025 |   Return values
1026 | 
1027 |   Zero if the server is alive. Non-zero if an error occurred.
1028 | 
1029 |   More:
1030 |   +html+ <PRE>
1031 |   Authors:
1032 |         andrei
1033 |   +html+ </PRE><DL COMPACT>
1034 |   +html+ <DT>Online References:
1035 |   +html+ <DD><UL>
1036 |   +html+ </UL></DL>
1037 | 
1038 |   ++++++++++++++++++++++++++++++++++++++*/
1039 | int SQ_ping(SQ_connection_t *sql_connection)
1040 | {
1041 | 	return(mysql_ping(sql_connection));
1042 | }
1043 | 
1044 | /************************************************************
1045 | * get_minmax_id()                                           *
1046 | *                                                           *
1047 | * Returns the min or max ID of the table                    *
1048 | *                                                           *
1049 | * Returns:                                                  *
1050 | *  min (max=0) or max (max=1) ID                            *
1051 | *  -1 in case of an error                                   *
1052 | *                                                           *
1053 | *                                                           *
1054 | *************************************************************/
1055 | 
1056 | long sq_get_minmax_id(SQ_connection_t *sql_connection, char *id_name, char *table, int max)
1057 | {
1058 | GString *query;
1059 | SQ_result_set_t *sql_result;
1060 | SQ_row_t *sql_row;
1061 | char *sql_str;
1062 | long id;
1063 | char *minmax;
1064 | int sql_err;
1065 | 
1066 | query = g_string_sized_new(STR_M);
1067 | 
1068 | if(max==1)minmax="max"; else minmax="min";
1069 | 
1070 |  g_string_sprintf(query, "SELECT %s(%s) FROM %s ", minmax, id_name, table);
1071 | 
1072 |  sql_err = SQ_execute_query(sql_connection, query->str, &sql_result);
1073 |  
1074 |  if(sql_err) {
1075 |     ER_perror(FAC_SQ, SQ_CNCT, "%s[%s]\n", SQ_error(sql_connection), 
1076 |               query->str);
1077 |     die;
1078 |  }
1079 |         
1080 | 	 
1081 |  if ((sql_row = SQ_row_next(sql_result)) != NULL) {
1082 | 	sql_str = SQ_get_column_string(sql_result, sql_row, 0);
1083 | 
1084 |      /* We must process all the rows of the result,*/
1085 |      /* otherwise we'll have them as part of the next qry */
1086 | 	while ( (sql_row = SQ_row_next(sql_result)) != NULL) {
1087 | 	  ER_perror(FAC_SQ, SQ_CNCT, "duplicate max [%s]\n", query->str);
1088 | 	  die;
1089 | 	}
1090 |  }
1091 |  else sql_str=NULL;
1092 |  
1093 |  if(sql_result){ SQ_free_result(sql_result); sql_result=NULL; }
1094 |  
1095 |  if(sql_str) {
1096 |   id = atol(sql_str);
1097 |   UT_free(sql_str);
1098 |  }
1099 |  /* table is empty, max_id=min_id=0 */
1100 |  else id=0;
1101 | 
1102 |  /* free temporary space used for query */
1103 |  g_string_free(query, TRUE);
1104 |  
1105 |  return(id);
1106 |  
1107 | }
1108 | 
1109 | /* SQ_escape_string() */
1110 | /*++++++++++++++++++++++++++++++++++++++
1111 |   Returns a copy of the string passed that has been escaped so it
1112 |   may be safely used in SQL strings.
1113 | 
1114 |   Return values
1115 | 
1116 |   Escaped string (allocated memory which must be freed)
1117 | 
1118 |   More:
1119 |   +html+ <PRE>
1120 |   Authors:
1121 |         shane
1122 |   +html+ </PRE><DL COMPACT>
1123 |   +html+ <DT>Online References:
1124 |   +html+ <DD><UL>
1125 |   +html+ </UL></DL>
1126 | 
1127 |   ++++++++++++++++++++++++++++++++++++++*/
1128 | 
1129 | char *SQ_escape_string(SQ_connection_t *sql_connection, char *str)
1130 | {
1131 |     char *new_str;
1132 |     int length;
1133 | 
1134 |     length = strlen(str);
1135 |     new_str = (char *)UT_malloc((length * 2) + 1);
1136 |     mysql_real_escape_string(sql_connection, new_str, str, length);
1137 |     return new_str;
1138 | }
1139 |